This is a simple tutorial in how to use Dependency Injection using SimpleInjector (You can get this package from NuGet)
In this case, I use SimpleInjector to manage my Data Context – I want my Data Context to be per request (Unit of Work per request). The concept of this dependency Injection is to have a global container where you can resolve your object from
1. Create an extension method to the Simple Injector
Code Snippet
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Web;
- using SimpleInjector;
- using System.Diagnostics;
- using System.Linq.Expressions;
- ///<summary>
- /// Extension methods for registering types on a per web request basis.
- ///</summary>
- public static partial class SimpleInjectorPerWebRequestExtensions
- {
- [DebuggerStepThrough]
- public static void RegisterPerWebRequest<TService, TImplementation>(
- this Container container)
- where TService : class
- where TImplementation : class, TService
- {
- Func<TService> instanceCreator =
- () => container.GetInstance<TImplementation>();
- container.RegisterPerWebRequest<TService>(instanceCreator);
- }
- [DebuggerStepThrough]
- public static void RegisterPerWebRequest<TService>(
- this Container container,
- Func<TService> instanceCreator) where TService : class
- {
- var creator =
- new PerWebRequestInstanceCreator<TService>(instanceCreator);
- container.Register<TService>(creator.GetInstance);
- }
- [DebuggerStepThrough]
- public static void RegisterPerWebRequest<TConcrete>(this Container container)
- where TConcrete : class
- {
- container.Register<TConcrete>();
- container.ExpressionBuilt += (sender, e) =>
- {
- if (e.RegisteredServiceType == typeof(TConcrete))
- {
- var transientInstanceCreator = Expression.Lambda<Func<TConcrete>>(
- e.Expression, new ParameterExpression[0]).Compile();
- var creator = new PerWebRequestInstanceCreator<TConcrete>(
- transientInstanceCreator);
- e.Expression = Expression.Call(Expression.Constant(creator),
- creator.GetType().GetMethod(“GetInstance”));
- }
- };
- }
- [DebuggerStepThrough]
- public static void DisposeInstance<TService>() where TService : class
- {
- object key = typeof(PerWebRequestInstanceCreator<TService>);
- var instance = HttpContext.Current.Items[key] as IDisposable;
- if (instance != null)
- {
- instance.Dispose();
- }
- }
- private sealed class PerWebRequestInstanceCreator<T> where T : class
- {
- private readonly Func<T> instanceCreator;
- internal PerWebRequestInstanceCreator(Func<T> instanceCreator)
- {
- this.instanceCreator = instanceCreator;
- }
- [DebuggerStepThrough]
- public T GetInstance()
- {
- var context = HttpContext.Current;
- if (context == null)
- {
- // No HttpContext: Let’s create a transient object.
- return this.instanceCreator();
- }
- object key = this.GetType();
- T instance = (T)context.Items[key];
- if (instance == null)
- {
- context.Items[key] = instance = this.instanceCreator();
- }
- return instance;
- }
- }
- }
2. Modify Global.asax – The class name will be MvcApplication in MVC Project
Code Snippet
- #region “Dependency Injection”
- private static Container Container;
- public static T GetInstance<T>() where T : class
- {
- return Container.GetInstance<T>();
- }
- protected void RegisterDependency()
- {
- //Create a main containers
- var container = new Container();
- // 2. Configure the container (register)
- container.RegisterPerWebRequest<IUnitOfWork>(() => new UnitOfWork(new PosDataContext()));
- container.Register<ITableRepository, TableRepository>();
- container.Verify();
- Container = container;
- }
- #endregion
- protected void Application_Start()
- {
- AreaRegistration.RegisterAllAreas();
- RegisterGlobalFilters(GlobalFilters.Filters);
- RegisterRoutes(RouteTable.Routes);
- BundleTable.Bundles.RegisterTemplateBundles();
- RegisterDependency();
- }
- protected void Application_EndRequest(object src, EventArgs e)
- {
- ServiceStack.MiniProfiler.Profiler.Stop();
- SimpleInjectorPerWebRequestExtensions.DisposeInstance<IUnitOfWork>();
- }
3. Consume it from the controller – Call the container in the Global.asax to resolve the object (GetInstance function)
Code Snippet
- public ActionResult Index()
- {
- ViewBag.Title = “Tables”;
- return View(MvcApplication.GetInstance<IUnitOfWork>().TableRepository.Get(e => e.Active));
- }
Steven
These extension methods are now redundant with Simple Injector v2. You can simply call container.Register(new WebRequestLifestyle()) and you’re done.
fransiscuss
Hi Steven,
Thanks for your tips. I’ll update it accordingly 🙂
Devon Burriss
Hi Fransiscus
This appears to be a Service Locator pattern. Some would argue this is misusing an IoC container. Personally I don’t like to argue so I will leave it to readers and author to hash out 🙂 I do recommend:
http://www.manning.com/seemann/
or the short version: http://blog.ploeh.dk/2010/02/03/ServiceLocatorisanAnti-Pattern/
fransiscuss
Hi Devon,
thank you for your feedback. CMIIW, what I did was querying the repository (Service Locator) to get the interface while ideally it should be auto wired ?Do you mind to elaborate more what’s the difference between those Service Locator and IoC?I thought somehow both are inter-related
Devon Burriss
Yes I suppose Service Locator(SL) is IoC. I am a bit vague on whether SL counts as DI, as usually this is done with the autowiring for constructor injection or property injection.
I prefer constructor injection as it revels a classes dependencies. With a SL a class will look like it has no dependencies, and only at runtime will you see that they have not been configured. So in this case you have inverted control of creation of your UoW but your controller is dependent on the SL. I have not thought it through entirely but I would imagine having the SL as a static could lead to some complications if you tried to unit test your controllers.
I know better developers than myself who use SL but I personally don’t like it adn I just thought I would point out that there are those that consider it an anti-pattern rather than a design pattern as there are ways to achieve the same decoupling without the drawbacks. Just my 2 cents…