Nowadays we know SOLID principles well. We know the importance of DIP/DI. But what if you think that all products from Microsoft are the best and you want use them and
in case of dependency injection you want to use Unity (originally part of Enterprise Library). So, how to use it correctly within a WEB application (WEB API or MVC).
The question is easy, but there are some concerns :)
Different lifetime mananagers
For Transient instances, we use TransientLifetimeManager. In that case Unity creates and returns a new instance for every call for Resolve().
This lifetime scope can be used for any object if it's needed. For example, for object encapsulating system resources like connections, synchronization primitives (should be implemented disposed pattern). But in that case there is some important thing we need to take into consideration dependency injection containers follows the rule that for transient object there is no calling dispose method, so you should do that by yourself.
For Singleton instances, we use ContainerControlledLifetimeManager. Single object for all containers. Dispose method will be called on calling container's dispose methods
For Per Request instances, we use HierarchicalLifetimeManager. Inherits ContainerControlledLifetimeManager and works like ContainerControlledLifetimeManager,
but in case of HierarchicalLifetimeManager child containers resolve it's own instance of an object.
Note!!! It's mandatory that it must be used for such object like EF DbContext, NHibernate Context. Can be used for many objects from the BLL and DLL (services, managers, commands, queries, repositories, etc.) if they are not needed to be transient or singleton
For Per Tread instances, we use PerThreadLifetimeManager. It resolves instances per thread. Note!!! For threads in thread pool instances are cached, so for objects as DatabaseContext (EF, NHibernate) we can't use this lifetime manager and should use HierarchicalLifetimeManager.
How to use (example)
public class UnityContainerBuilder
{
public static IUnityContainer Build<TLifetimeManager>()
where TLifetimeManager : LifetimeManager, new()
{
var container = new UnityContainer();
container
.RegisterType<IEmailSender, EmailSender>(new TLifetimeManager())
.RegisterType<IMyEfDbContext, MyEfDbContext>(new TLifetimeManager())
}
}
public class StartUpContext
{
public StartUpContext(IAppBuilder owinAppBuilder,
HttpConfiguration globalConfiguration)
{
this.OwinAppBuilder = owinAppBuilder;
this.GlobalConfiguration = globalConfiguration;
}
public IAppBuilder OwinAppBuilder { get; private set; }
public HttpConfiguration GlobalConfiguration { get; private set; }
public IUnityContainer UnityContainer { get; set; }
}
public static class StartUpContextExtensions
{
public static StartUpContext SetupDI(this StartUpContext context)
{
var container
= UnityContainerBuilder.Build<HierarchicalLifetimeManager>();
context.GlobalConfiguration.DependencyResolver
= new Unity.WebApi.UnityDependencyResolver(container);
context.UnityContainer = container;
return context;
}
}
new StartUpContext(owinAppBuilder, GlobalConfiguration.Configuration)
.SetupDI();
Here we see 2 important things
1) For basic behavior we use HierarchicalLifetimeManager
2) Inside dependency resolver there is invocation of container.CreateChildContainer()
UnityDependencyResolver code looks like:
public class UnityDependencyResolver : UnityDependencyScope,
IDependencyResolver,
IDependencyScope,
IDisposable
{
public UnityDependencyResolver(IUnityContainer container)
: base(container)
{
}
public IDependencyScope BeginScope()
{
return (IDependencyScope)
new UnityDependencyScope(this.Container.CreateChildContainer());
}
}
How it works
Dependency resolver resolve all dependencies for controller (all objects we pass in controller as parameters if we use constructor injection).
Corresponding objects are being resolved using corresponding lifetime managers described in settings. And it works as expected, our dependencies have per request lifetime scope.
Useful TIP
Let's assume that we need to have some validations according to data in db before we process our controller action.
We can make a child class from DelegatingHandler and add some validation logic inside. But how should we create an EF DB context to make queries, there are 2 possible ways:
1) Resolve MyDbContext instance
2) Create MyDbContext by calling a constructor
We need to know that first approach works not in all cases. In that case, before resolve context, we need to create a child container each time we do resolve otherwise it will resolve instance from parent container which will be a singleton. That will cause issues on retrieving data from DB (Not closed DbReader for example).
Links
I want to mention here some links connected with the topic.