MVVM + Services + Entity Framework and Dependency Injection vs Locator

I have a lot of systems using WPF with MVVM. For unit testing, we inject dependencies into View Models, however I found that when injecting a dependent class at build time, we have no control over the lifetime of a dependent object such as the Entity Framework DbContext.

A simple scenario is as follows:

public class FooVM
{
    private readonly IBarService _barService;

    // Set in the UI via Databinding
    public string Name { get; set; }
    public string OtherName { get; set; }

    public FooVM(IBarService barService)
    {
        _barService = barService;
    }

    public void SaveFoo()
    {
        _barService.SaveFoo(Name);
    }

    public void SaveBar()
    {
        _barService.SaveBar(OtherName);
    }
}

public class BarService : IBarService
{
    private readonly IEntityContext _entityContext;

    public BarService(IEntityContext entityContext)
    {
        _entityContext = entityContext;
    }

    public void SaveFoo(string name)
    {
        // some EF stuff here
        _entityContext.SaveChanges();
    }

    public void SaveBar(string otherName)
    {
        // some EF stuff here
        _entityContext.SaveChanges();
    }
}

      

The VM needs to use the service, so it needs to be injected, the service is required, IEntityContext

and therefore it will be injected. The problem occurs when in the virtual machine we call SaveFoo

and SaveBar

, since the object _entityContext

gets dirty after one call. Ideally, we want to get rid of the object _entityContext

after each call.

The only way I have found this is to use dependency injection to inject the container, which then calls the code like this:

public class FooVM
{
    private readonly IInjector _injector;

    // Set in the UI via Databinding
    public string Name { get; set; }
    public string OtherName { get; set; }

    public FooVM(IInjector injector)
    {
        _injector = injector;
    }

    public void SaveFoo()
    {
        var barService = _injector.GetUniqueInstance<IBarService>();
        barService.SaveFoo(Name);
    }

    public void SaveBar()
    {
        var barService = _injector.GetUniqueInstance<IBarService>();
        barService.SaveBar(OtherName);
    }
}

      

This way the container ( IInjector

) acts as a service locator, which works great except for the inconvenience for unit testing. Is there a better way to handle this? I realize that this almost eliminates all the benefits of Dependency Injection, but I can't think of any other way.

EDIT: Additional example

Let's say you have a window with two buttons. There is one service behind it that was introduced through dependency injection. You press the A button and it loads the object, modifies it and saves, however that doesn't work (for some reason, let's say some validation is not done in the DbContext), you show a nice message.

Now you press button 2. It loads another object and modifies it and tries to save, now when the first button was clicked and the service is the same service, with the same context, this operation will fail for the same as when pressing the A button.

+3


source to share


6 answers


My company is doing the same as you and we are solving it using Repository and UnitOfWorkFactory patterns.

A simple version of this would look something like this:

public class BarService : IBarService
{
    private readonly IEntityContextFactory _entityContextFactory;

    public BarService(IEntityContextFactory entityContextFactory)
    {
        _entityContextFactory = entityContextFactory;
    }

    public void SaveFoo(string name)
    {
        using (IEntityContext entityContext = _entityContextFactory.CreateEntityContext())
        {
            // some EF stuff here
            entityContext.SaveChanges();
        }
    }

    public void SaveBar(string otherName)
    {
        using (IEntityContext entityContext = _entityContextFactory.CreateEntityContext())
        {
            // some EF stuff here
            _entityContext.SaveChanges();
        }
    }
}

      



And the factory:

public class EntityContextFactory : IEntityContextFactory
{
    private readonly Uri _someEndpoint = new Uri("http://somwhere.com");

    public IEntityContext CreateEntityContext()
    {
        // Code that creates the context.
        // If it complex, pull it from your bootstrap or wherever else you've got it right now.
        return new EntityContext(_someEndpoint);
    }
}

      

Your IEntityContext must implement IDisposable for the "using" keyword in order to work here, but that should be the gist of what you need.

+5


source


As also pointed out by @ValentinP, I also believe that you are going down the wrong path, but for a different reason.

If you don't want to pollute the instance state tracking DbContext

for your persistence methods with objects that have already been fetched during database queries, then you will need to reconfigure your application and split your business logic into 2 logical levels, One level for search and one for persistence, each layer will use its own instance DbContext

, so you never have to worry about objects that were fetched and processed accidentally left with another operation (I suppose that's why you asked the question).

This is a common pattern called command request separation of concerns, or CQRS for short. See this article Martin Fowler's CQRS by Pattern or this Microsoft article for code samples.

Using this pattern, you can dispose of the instances DbContext

(directly or indirectly through Dispose of the object owned by root).



Edit based on last edit

This script removes many questions about what you are trying to accomplish.

  • I support the CQRS implementation as I believe it is applicable.
  • It is a common approach to avoid using long instances DbContext

    in applications. Create it when you need it and then delete it when you're done with it. There DbContext

    is minimal overhead in creating / deleting an object . Then you must reattach any modified models / collections to the new DbContext

    one where you want to store the changes, there is no reason to re-check them out from the main repository. If a failure occurs, the entry point to that piece of code (either at the service level or at the presentation level) must handle the error (display message, revert changes, etc.). Concurrency exceptions (using TimeStamp / Rowversion) are also handled correctly using this approach. Also, since you used the newDbContext

    , you don't need to worry about other commands that might also run in the same view if they try to do something independent.

You should be able to specify the lifetime area of ​​each object you enter. For yours, IEntityContext

you can specify a Transient (which is the default) and inject it into the appropriate service layer constructor. Each instanceIEntityContext

must have exactly one owner / root. If you use the CQRS template, it becomes a little easier to manage. If you use something like the DDD pattern, it gets a little more confusing, but still doable. Alternatively, you can also specify a thread-level timing scope, although I would not recommend this as it could cause many unexpected side effects if you ever forget about it and try to add parallel programming or use the async / pattern await without returning the original thread context.

+1


source


My heart-to-heart recommendation will be reflected in your design in the IoC container known throughout life as Autofac.

Take a look at this to learn how to manage lifetime even with IoC: http://autofac.readthedocs.org/en/latest/lifetime/instance-scope.html

If you need more information on how to implement this for your purpose, please report me here.

+1


source


What DI framework are you using? With Autofac, you have something called LifeTimeScope. Perhaps other structures have similar functionality.

http://docs.autofac.org/en/latest/lifetime/index.html

Basically, you will need to define which department is working on your application (each ViewModel instance? Each ViewModel element?) As well as a new LifeTimeScope for each UoW and resolve your dependencies with a life scale. Depending on your implementation, it might look more like a service locator, but it simplifies dependency lifetime management. (If you register DBContext as a PerLifeTimeScope, you can be sure that all dependencies resolved in the same life scope will have the same dbcontext and that it will not be used for dependencies resolved with a different lifetimecope).

Also, since lifetimecopes implements an interface, it can be easily mocked for allowed mock services for unit test purposes.

+1


source


You have to use factory to create db context for every time. If you want to use Autofac , it automatically created a factory for that. You can use Dynamic Instantiation to create dbcontext every time. You can use Controlled Lifetime to control the dbcontext lifetime for yourself. If you combine both, you will have a dbcontext each time and you will manage the lifetime in the (Dispose it yourself) method.

While you are testing, you simply registered a mocked copy IEntityContext

.

public class BarService : IBarService
    {
        private readonly Func<Owned<IEntityContext>> _entityContext;

        public BarService(Func<Owned<IEntityContext>> entityContext)
        {
            _entityContext = entityContext;
        }

        public void SaveFoo(string name)
        {
            using (var context = _entityContext())
            {
                context.SaveChanges();
            }
        }

        public void SaveBar(string otherName)
        {
            using (var context = _entityContext())
            {
                context.SaveChanges();
            }
        }
    }

      

If you want to manage the entire lifetime of dbcontexts, we can delete Owned

and we can register your context ExternallyOwned

. This means autofac will not handle the lifetime of this object.

builder.RegisterType<EntityContext>().As<IEntityContext>().ExternallyOwned();

      

Then your field and constructor should be like this:

private readonly Func<IEntityContext> _entityContext;

            public BarService(Func<IEntityContext> entityContext)
            {
                _entityContext = entityContext;
            }

      

+1


source


  • I find it bad practice to create and delete the DbContext every time. This seems to be very beneficial.
  • So you don't want to fetch the SaveChanges method? It will just call SaveChanges on the DbContext.
  • If you cannot do this, I believe that creating a ContextFactory is the best way to go instead of a Service Locator. I know that for example Windsor can generate factory implementations for a given interface automatically ( http://docs.castleproject.org/Default.aspx?Page=Typed-Factory-Facility-interface-based-factories&NS=Windsor ). It's better semantically and for testing purposes. The focus here is on the transparent factory interface, the implementation of which is based on IoC configuration and life policies.
  • Finally, if you are not interested in immediate click changes, you can create an IDisposable DbContext wrapper that will save SaveChanges upon disposal. Assuming you are using request / response paradigm and request lifecycle management.
0


source







All Articles