I have many systems that use WPF with MVVM. For unit testing we inject dependencies into the View Models, however I have found that when injecting the dependent class at construction time we cannot control the lifetime of the dependent object such as an Entity Framework DbContext.
A simple scenario is a 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 has it injected, the service needs an IEntityContext
and thus has that injected. The problem comes when in the VM we call SaveFoo
and SaveBar
, as the _entityContext
object is dirty after a single call. Ideally we want to dispose of the _entityContext
object after each call.
The only way I've found round this is to use Dependency Injection to inject the container which then calls the code as follows:
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);
}
}
In this way the container (IInjector
) is acting like a service locator which works great, except is clunky for unit testing. Is there a better way to manage this? I understand that doing this pretty much voids all the benefits of Dependency Injection, but I can't figure another way.
EDIT: Further Example
Say you have a window with two buttons. One service sits behind it which has been injected via dependency injection. You click button A and it loads an object, modifies it, and saves, however this fails (for some reason, lets say some validation fails in the DbContext), you show a nice message.
Now you click button 2. It loads a different object and modifies it and tries to save, now because the first button was pressed, and the service is the same service, with the same context, this operation will fail for the same reason as when clicking button A.