Wednesday, 13 April 2011

Unity and ASP.NET MVC 2 Controller injection

I’ve been reading a brilliant ASP.NET MVC Framework V2 Book by Steven Sanderson and found that he uses NInject DI framework with MVC. So I thought I’d give it a go with Unity – and it actually isn’t that bad.

So the things we need are:

  1. An interface declaration defining the contract that the concrete class should meet. Also implementation of the controller.
  2. A DI framework (Unity) to carry out the injection
  3. A Controller Factory to provide the DI framework with the controller to populate.
  4. Attach the custom factory to the MVC framework.

So, I won’t provide huge amounts of code for the interface/class implementations, but here is a quick example:

Step 1 – Create an interface, class and controller implementation

interface IProductsRepository
{
IQueryable<Product> Products { get; }
}
//
class SqlDbRepository: DbContext, IProductsRepository
{
IQueryable<Product> Products { get; }
// implementation here
}
//

class ProductsController : Controller
{
private IProductsRepository productsRepository;

public ProductsController (IProductsRepository repository)
{
this.productsRepository = repository;
}
}

Step 2 - Configure the Unity container

Now, create the mapping in the configuration file. I find this the hardest way, hence why I do it :-P

<configSections>
<section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration" />
</configSections>

<unity xmlns="http://schemas.microsoft.com/practices/2010/unity">
<alias alias="IProductsRepository" type="SportsStore.Domain.Abstract.IProductsRepository,SportsStore.Domain"/>
<alias alias="SqlDbRepository" type="SportsStore.Domain.Concrete.SqlDbRepository,SportsStore.Domain"/>

<container>
<register type="IProductsRepository" mapTo="SqlDbRepository" name="">
<lifetime type="singleton"/>
<constructor>
<param name="connectionStringName" type="string" value="Default" />
</constructor>
</register>
</container>
</unity>

<connectionStrings>
<add name="Default" connectionString='Data Source=.\SQLEXPRESS;AttachDbFilename="&amp;lt;path to>\SportsStore.mdf";Integrated Security=True;User Instance=True' providerName='System.Data.SqlClient'/>
</connectionStrings>

I admit, the configuration looks scary. But I am just <register> ing an interface to a class.

  • <alias> means I don’t have to retype the fully qualified type every time I want to use it.
  • I am ‘injecting’ a <constructor> <param> eter into the SqlDbRepository class with the <value=“Default”/>. My SqlDbRepository is based on the DbContext class which accepts the connection string name, which the Entity Framework uses to look up the actual connection string.

Lets get some code together to initialise the UnityContainer…

// Create a container
IUnityContainer container = new UnityContainer();

// Get the section and apply it to the container.
UnityConfigurationSection section = ConfigurationManager.GetSection("unity") as UnityConfigurationSection;
section.Configure(container);

Next, we need some code that will apply this mapping (IProductsRepository to SqlDbRepository) to a given object. So I give it an object that has an IProductsRepository constructor parameter. It will then return me an initialised instance of that object, with the SqlDbRepository passed directly into it.

Step 3 – Create a custom ControllerFactory to populate the controllers

MVC have a class called the “DefaultControllerFactory”, which has a method called “GetControllerInstance”. This method is used to create a new instance of a given controller type. So for example, when initialising a ProductsController controller, this method is fired with

protected override IController GetControllerInstance(RequestContext context, Type controllerType)
{
// controllerType == ProductsController
}

Therefore, if we want to create a ProductsController created with the IProductsRepository constructor parameter populated, we just call the .Resolve() method.

This method takes a Type object and says .. “hmmm .. interesting, having a look over you, I see you have an IProductsRepository constructor parameter. Veeryyy interesting …. Now the thing is, I’ve been configured to pass in a …….. SqlDbRepository object … to a constructor which requires IProductsRepository“ .. So it creates a SqlDbRepository class (with constructor parameters specified earlier), injects it into the constructor, creating a new instance of the ProductsController class.

Now once that Resolve() has returned, it needs to be cast to an IController (i.e. the interface of Controller i.e. the parent class of ProductsController).

public class UnityControllerFactory : DefaultControllerFactory
{
private IUnityContainer unityContainer;

public UnityControllerFactory(IUnityContainer container)
{
this.unityContainer = container;
}

protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
return unityContainer.Resolve(controllerType) as IController;
}
}

Step 4 – Attach the custom ControllerFactory to the framework

And finally, we have to tell MVC that when creating controllers, use this ControllerFactory to populate them. So in Global.asax, I do:

protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterRoutes(RouteTable.Routes);

// From earlier
IUnityContainer container = new UnityContainer();
UnityConfigurationSection section = ConfigurationManager.GetSection("unity") as UnityConfigurationSection;
section.Configure(container);

// Configure framework to populate using my controller factory
UnityControllerFactory unity = new UnityControllerFactory(container);
ControllerBuilder.Current.SetControllerFactory(unity);
}

Job done!