Sunday, February 15, 2009 5:40 PM Kazi Manzur Rashid

ASP.NET MVC, Unity and Common Service Locator

In this post, I will show you how can you extend Microsoft Patterns & Practices Unity Application Block and use it in ASP.NET MVC in conjunction with Patterns & Practices Common Service Locator.

Extending Unity

Unity is a dependency injection container from MS Patterns & Practices team. But unlike the other containers it does not have any PerWebRequest support, which means you cannot get the same object in the same web request, but it has excellent extensibility support which we can utilize to fill the gap.  The latest version of Unity has the following life time support for object instance:

  • ContainerControlledLifetimeManager: Usually used for making an instance Singleton.
  • ExternallyControlledLifetimeManager: Returns the same instance but keeps a weak reference so that object can garbage collected.
  • PerThreadLifetimeManager: Returns the same object for the same thread.
  • If no lifetime manager is specified Unity will return new instance each time it is requested.

To create a new life time manager we have to create a new class which inherits from abstract LifeTimeManager and override three methods, since we want the Unity container to return the same object for the same web request we will use the HttpContext.Items. We will also use the .NET Framework 3.5 SP1 System.Web.Abstraction to make it unit testable, code:

namespace UnityCommonServiceLocatorMVC
{
    using System;
    using System.Collections.Generic;
    using System.Web;

    using Microsoft.Practices.Unity;

    public class UnityPerWebRequestLifetimeManager : LifetimeManager
    {
        private HttpContextBase _httpContext;

        //Need this constructor for Unit Test
        public UnityPerWebRequestLifetimeManager(HttpContextBase httpContext)
        {
            _httpContext = httpContext;
        }

        public UnityPerWebRequestLifetimeManager(): this(new HttpContextWrapper(HttpContext.Current))
        {
        }

        private IDictionary<UnityPerWebRequestLifetimeManager, object> BackingStore
        {
            get
            {
                _httpContext = (HttpContext.Current != null) ? new HttpContextWrapper(HttpContext.Current) : _httpContext;

                return UnityPerWebRequestLifetimeModule.GetInstances(_httpContext);
            }
        }

        private object Value
        {
            get
            {
                IDictionary<UnityPerWebRequestLifetimeManager, object> backingStore = BackingStore;

                return backingStore.ContainsKey(this) ? backingStore[this] : null;
            }
            set
            {
                IDictionary<UnityPerWebRequestLifetimeManager, object> backingStore = BackingStore;

                if (backingStore.ContainsKey(this))
                {
                    object oldValue = backingStore[this];

                    if (!ReferenceEquals(value, oldValue))
                    {
                        IDisposable disposable = oldValue as IDisposable;

                        if (disposable != null)
                        {
                            disposable.Dispose();
                        }

                        if (value == null)
                        {
                            backingStore.Remove(this);
                        }
                        else
                        {
                            backingStore[this] = value;
                        }
                    }
                }
                else
                {
                    if (value != null)
                    {
                        backingStore.Add(this, value);
                    }
                }
            }
        }

        public override object GetValue()
        {
            return Value;
        }

        public override void SetValue(object newValue)
        {
            Value = newValue;
        }

        public override void RemoveValue()
        {
            Value = null;
        }
    }
}

Nothing special, we are overriding GetValue, SetValue, RemoveValue and created a private property which is called from these methods. There are two points I want to highlight, first the BackingStore property, although we are injecting the HttpContext in the constructor but we are creating a new HttpContext in case we are running in the production mode, otherwise it will pin down the constructor injected HttpContext and behave incorrectly. Next, for returning the BackingStore I am using an helper class, this idea is actually brought from the Castle Windsor. Creating a LifetimeManger does serves our purpose completely, we also have to ensure that disposable objects are disposed when the web request completes to avoid memory leak. And to do this we will create an HttpModule and hook its EndRequest event to do the cleanup, code:

namespace UnityCommonServiceLocatorMVC
{
    using System;
    using System.Collections.Generic;
    using System.Web;

    public class UnityPerWebRequestLifetimeModule : IHttpModule
    {
        private static readonly object Key = new object();
        private HttpContextBase _httpContext;

        //Need this constructor for Unit Test
        public UnityPerWebRequestLifetimeModule(HttpContextBase httpContext)
        {
            _httpContext = httpContext;
        }

        public UnityPerWebRequestLifetimeModule()
        {
        }

        internal IDictionary<UnityPerWebRequestLifetimeManager, object> Instances
        {
            get
            {
                _httpContext = (HttpContext.Current != null) ? new HttpContextWrapper(HttpContext.Current) : _httpContext;

                return (_httpContext == null) ? null : GetInstances(_httpContext);
            }
        }

        void IHttpModule.Init(HttpApplication context)
        {
            context.EndRequest += (sender, e) => RemoveAllInstances();
        }

        void IHttpModule.Dispose()
        {
        }

        internal static IDictionary<UnityPerWebRequestLifetimeManager, object> GetInstances(HttpContextBase httpContext)
        {
            IDictionary<UnityPerWebRequestLifetimeManager, object> instances;

            if (httpContext.Items.Contains(Key))
            {
                instances = (IDictionary<UnityPerWebRequestLifetimeManager, object>) httpContext.Items[Key];
            }
            else
            {
                lock (httpContext.Items)
                {
                    if (httpContext.Items.Contains(Key))
                    {
                        instances = (IDictionary<UnityPerWebRequestLifetimeManager, object>) httpContext.Items[Key];
                    }
                    else
                    {
                        instances = new Dictionary<UnityPerWebRequestLifetimeManager, object>();
                        httpContext.Items.Add(Key, instances);
                    }
                }
            }

            return instances;
        }

        internal void RemoveAllInstances()
        {
            IDictionary<UnityPerWebRequestLifetimeManager, object> instances = Instances;

            if (instances != null)
            {
                foreach (KeyValuePair<UnityPerWebRequestLifetimeManager, object> entry in instances)
                {
                    IDisposable disposable = entry.Value as IDisposable;

                    if (disposable != null)
                    {
                        disposable.Dispose();
                    }
                }

                instances.Clear();
            }
        }
    }
}

We are maintaining a Dictionary against a Key in the HttpContext.Items which holds the Lifetime manager and its corresponding value as key value pair and in the end request we are iterating this dictionary and calling the dispose method.

Common Service Locator

The Common Service Locator is an abstraction over the actual container so that our application does not make hard dependency on any specific container. Currently the Common Service Locator has support for Windsor, StructureMap, Spring.NET, Autofac, Unity and MEF.

Integrate with ASP.NET MVC

We will use the default ASP.NET MVC project to demonstrate the integration. Assuming our HomeController depends upon  CategoryRepository and ProductRepository, code:

private readonly ICategoryRepository _categoryRepository;
private readonly IProductRepository _productRepository;

public HomeController(ICategoryRepository categoryRepository, IProductRepository productRepository)
{
    _categoryRepository = categoryRepository;
    _productRepository = productRepository;
}

Next, we will have to inject these dependency and for this we will create a ControllerFactory:

namespace UnityCommonServiceLocatorMVC
{
    using System;
    using System.Web.Mvc;

    using Microsoft.Practices.ServiceLocation;

    public class CommonServiceLocatorControllerFactory : DefaultControllerFactory
    {
        protected override IController GetControllerInstance(Type controllerType)
        {
            return (controllerType == null) ? base.GetControllerInstance(controllerType) : ServiceLocator.Current.GetInstance(controllerType) as IController;
        }
    }
}

We inheriting from the ASP.NET MVC default controller factory and overriding just a single method where we are using the ServiceLocator to create the requested controller. But before that we have to specify the ServiceLocator which IoC/DI container it should use, in this case it is Unity. In the global.asax:

IUnityContainer container = new UnityContainer();

UnityConfigurationSection configuration = (UnityConfigurationSection) ConfigurationManager.GetSection("unity");
configuration.Containers.Default.Configure(container);

ServiceLocator.SetLocatorProvider(() => new UnityServiceLocator(container));

We are creating an Unity Container and configuring it from the configuration, although you can use the nice fluent syntax of Unity instead of using configuration file. Next, we are creating the UnityAdapter by passing the container and instructing the ServiceLocator to use it. The configuration:

<configuration>
	<configSections>
		<section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration" requirePermission="false"/>
	</configSections>
	<appSettings/>
	<unity>
		<typeAliases>
			<typeAlias alias="PerWebRequest" type="UnityCommonServiceLocatorMVC.UnityPerWebRequestLifetimeManager, UnityCommonServiceLocatorMVC"/>
			<typeAlias alias="IDatabase" type="UnityCommonServiceLocatorMVC.IDatabase, UnityCommonServiceLocatorMVC"/>
			<typeAlias alias="Database" type="UnityCommonServiceLocatorMVC.InMemoryDatabase, UnityCommonServiceLocatorMVC"/>
			<typeAlias alias="ICategoryRepository" type="UnityCommonServiceLocatorMVC.ICategoryRepository, UnityCommonServiceLocatorMVC"/>
			<typeAlias alias="CategoryRepository" type="UnityCommonServiceLocatorMVC.CategoryRepository, UnityCommonServiceLocatorMVC"/>
			<typeAlias alias="IProductRepository" type="UnityCommonServiceLocatorMVC.IProductRepository, UnityCommonServiceLocatorMVC"/>
			<typeAlias alias="ProductRepository" type="UnityCommonServiceLocatorMVC.ProductRepository, UnityCommonServiceLocatorMVC"/>
			<typeAlias alias="HomeController" type="UnityCommonServiceLocatorMVC.HomeController, UnityCommonServiceLocatorMVC"/>
		</typeAliases>
		<containers>
			<container>
				<types>
					<type type="IDatabase" mapTo="Database">
						<lifetime type="PerWebRequest"/>
					</type>
					<type type="ICategoryRepository" mapTo="CategoryRepository">
						<lifetime type="PerWebRequest"/>
						<typeConfig extensionType="Microsoft.Practices.Unity.Configuration.TypeInjectionElement, Microsoft.Practices.Unity.Configuration">
							<constructor>
								<param name="database" parameterType="IDatabase">
									<dependency/>
								</param>
							</constructor>
						</typeConfig>
					</type>
					<type type="IProductRepository" mapTo="ProductRepository">
						<lifetime type="PerWebRequest"/>
						<typeConfig extensionType="Microsoft.Practices.Unity.Configuration.TypeInjectionElement, Microsoft.Practices.Unity.Configuration">
							<constructor>
								<param name="database" parameterType="IDatabase">
									<dependency/>
								</param>
							</constructor>
						</typeConfig>
					</type>
					<type type="HomeController">
						<typeConfig extensionType="Microsoft.Practices.Unity.Configuration.TypeInjectionElement, Microsoft.Practices.Unity.Configuration">
							<constructor>
								<param name="categoryRepository" parameterType="ICategoryRepository">
									<dependency/>
								</param>
								<param name="productRepository" parameterType="IProductRepository">
									<dependency/>
								</param>
							</constructor>
						</typeConfig>
					</type>
				</types>
			</container>
		</containers>
	</unity>
	<system.web>
		<httpModules>
			<add name="UnityPerWebRequestLifetimeModule" type="UnityCommonServiceLocatorMVC.UnityPerWebRequestLifetimeModule, UnityCommonServiceLocatorMVC"/>
		</httpModules>
	</system.web>
	<system.webServer>
		<validation validateIntegratedModeConfiguration="false"/>
		<modules runAllManagedModulesForAllRequests="true">
			<remove name="UnityPerWebRequestLifetimeModule"/>
			<add name="UnityPerWebRequestLifetimeModule" type="UnityCommonServiceLocatorMVC.UnityPerWebRequestLifetimeModule, UnityCommonServiceLocatorMVC"/>
		</modules>
	</system.webServer>
</configuration>

First, we have added the types in typealias section so that we do not have to specify the complete type name later on. Next, IDatabase, ICategoryRepository and IProductRepository is mapped to its corresponding implementation and it will use the UnityPerWebRequestLifetimeManager that we have created earlier.  And at last we have added the UnityPerWebRequestLifetimeModule in the httpModules section so that the database which is disposable is disposed when the request ends.

You can find the complete solution in the following zip file.

UnityCommonServiceLocatorMVC.zip

Shout it
Filed under: , , , , , ,

Comments

# re: ASP.NET MVC, Unity and Common Service Locator

Monday, February 16, 2009 4:12 PM by Stuart Thompson

Excellent article.  Thanks for sharing.

-- Stuart Thompson

# re: ASP.NET MVC, Unity and Common Service Locator

Saturday, March 21, 2009 8:02 AM by Tiit

Very good, just what i needed. Thanks

# re: ASP.NET MVC, Unity and Common Service Locator

Wednesday, April 15, 2009 9:22 PM by Diego

simply great!

# re: ASP.NET MVC, Unity and Common Service Locator

Thursday, April 16, 2009 7:32 AM by Mike Scott

Shouldn't this be a typecast, rather than a safe "as" cast?

return (controllerType == null) ? base.GetControllerInstance(controllerType) : ServiceLocator.Current.GetInstance(controllerType) as IController;

If the type doesn't implement IController, you're going to get a confusing null reference exception somewhere else, rather than a fail fast with an explicit type cast exception where the problem occurred.

# re: ASP.NET MVC, Unity and Common Service Locator

Saturday, April 18, 2009 6:52 PM by Yazid

Hello,

This is excellent, can you give an exmaple on how to apply this example using StrucureMap?

TIA

Yaz