Refactoring to SOLID - Part 8: Dependency Injection in ASP.NET MVC (Unity Version)

by Larry Spencer Sunday, May 27, 2012 1:14 PM

In this series, We have been refactoring an ASP.NET WebForms application to an ASP.NET MVC app that adheres to the principles of SOLID development.

In Part 7, we got to the point where our dependency injection was centralized in the application root (global.asax.cs). However, our choice of the concrete class there was still hard-coded. In this post, we will move the choice to the configuration file, employing the Unity dependency-injection framework. If you would like to follow along using the full solution, instructions for downloading it are here.

A dependency-injection framework makes it easy to plug in concrete classes behind interfaces. You could do all the same things with reflection, but not as easily. Unity is only one of many such frameworks, but I've had good luck with it. You can read about it on MSDN, or hopefully you can just get the idea from this post.

Recall the Application_Start() method in global.asax.cs from the last post. The last two lines set up a custom controller factory that injects a DocumentSearcher into the DocumentControllers it creates. DocumentSearcher is the concrete class that we wish to inject behind the ISearcher<Document> interface.

 

protected void Application_Start()
{
    AreaRegistration.RegisterAllAreas();

    RegisterGlobalFilters(GlobalFilters.Filters);
    RegisterRoutes(RouteTable.Routes);
    WebAppControllerFactory.DocumentSearcher = new DocumentSearcher();
    ControllerBuilder.Current.SetControllerFactory(typeof(WebAppControllerFactory));
}

 

That was "Poor Man's Dependency Injection." Yes, we were injecting a concrete class, but our choice was hard-coded. As a first step away from that, we will replace the line that instantiates the new DocumentSearcher with equivalent code that uses Unity.

 

protected void Application_Start()
{
    AreaRegistration.RegisterAllAreas();

    RegisterGlobalFilters(GlobalFilters.Filters);
    RegisterRoutes(RouteTable.Routes);
    using (var unity = new UnityContainer())
    {
        unity.RegisterType(typeof(ISearcher<Document>), typeof(DocumentSearcher));
        WebAppControllerFactory.DocumentSearcher = unity.Resolve<ISearcher<Document>>()
    }
    ControllerBuilder.Current.SetControllerFactory(typeof(WebAppControllerFactory));
}

 

This does not look like progress. We have replaced one line of code with three, not to mention the curly braces. But it does allow me to explain what Unity does.

Unity works by setting up a "container" in which you register mappings between abstract types (ISearcher<Document>) and the concrete versions you want to use (DocumentSearcher). When you want a concrete object for type T, you call Resolve<T> to get one.

Now let's move the choice of ISearcher<Document> to the web.config file. Here are the relevant portions.

 

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


  <unity xmlns="http://schemas.microsoft.com/practices/2010/unity">

    <assembly  name="Solid.Core" />
    <namespace name="Solid.Core" />

    <assembly  name="Solid.Interfaces" />
    <namespace name="Solid.Interfaces" />

    <assembly  name="Solid.MyDatabase"/>
    <namespace name="Solid.MyDatabase"/>

    <assembly  name="Solid.WebApp"/>
    <namespace name="Solid.WebApp"/>

    <container>
      <register type="ISearcher[Document]" mapTo="DocumentSearcher" />
    </container>

  </unity>

 

In Application_Start() we replace the hard-coded type registration with unity.LoadConfiguration().

 

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

    using (var unity = new UnityContainer())
    {
        unity.LoadConfiguration();
        WebAppControllerFactory.DocumentSearcher = unity.Resolve<ISearcher<Document>>();
    }

    ControllerBuilder.Current.SetControllerFactory(typeof(WebAppControllerFactory));
}

 

 

This is great, but if you change the web.config to inject a LoggingSearcher instead of a DocumentSearcher, you'll run into a problem. DocumentSearcher has a default constructor, so Unity will have no problem creating a DocumentSearcher for you. However, you may recall that LoggingSearcher's constructor needs two things: the wrapped searcher whose queries it logs and an IQueryLogger that performs the logging. Unity is intelligent enough to see the IQueryLogger in the constructor and consult its container for a type mapping. This cascading dependency injection is one thing that dependency-injection containers do for you. So, if we just add IQueryLogger to the web.config, that part will be taken care of.

 

<container>
    <register type="ISearcher[Document]" mapTo="LoggingSearcher[Document]" />
    <register type="IQueryLogger" mapTo="QueryLogger" />
</container>

 

 

Can we do the same thing for LoggingSearcher's constructor's other parameter -- the ISearcher<T>? Unfortunately not! We already have a type mapping for ISearcher<Document> in our Unity container. If we add another one, it will wipe out the first. Unity provides a remedy for this situation in the form of a name attribute on a type mapping. So far, we have used default (un-named) type mappings, but we can add a named version of the ISearcher<Document> mapping for the wrapped searcher. We correlate with that by adding a [Dependency(name)] attribute on the ISearcher<Document> parameter of LoggingSearcher's constructor.

The web.config's Unity container becomes...

 

<container>
    <register type="ISearcher[Document]" mapTo="LoggingSearcher[Document]" />
    <register type="ISearcher[Document]" name="LoggingSearcher_WrappedSearcher" mapTo="DocumentSearcher" />
    <register type="IQueryLogger" mapTo="QueryLogger" />
</container>

 

...and LoggingSearcher is as follows.

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Solid.Interfaces;
using System.Diagnostics.Contracts;
using System.Security.Principal;
using Microsoft.Practices.Unity;

namespace Solid.Core
{
    /// <summary>
    /// Decorates a wrapped ISearcher with logging.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class LoggingSearcher<T> : ISearcher<T>
    {
        #region Fields
        ISearcher<T> _wrappedSearcher;
        IQueryLogger _logger;
        #endregion

        #region Properties
        public const string SearcherDependency  = "LoggingSearcher_WrappedSearcher";
        #endregion

        #region Constructor
        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="wrappedSearcher">The underlying searcher.</param>
        /// <param name="logger">The logger, or null if logging is not desired.</param>
        public LoggingSearcher(
            [Dependency(SearcherDependency)] ISearcher<T> wrappedSearcher, 
            IQueryLogger logger)
        {
            Contract.Requires(wrappedSearcher != null);

            _wrappedSearcher = wrappedSearcher;
            _logger = logger;
        }
        #endregion

        #region ISearcher<T> Implementation
        /// <summary>
        /// Search the repository and return the matches.
        /// </summary>
        /// <param name="criteria">The criteria for the search.
        /// Null means to return all records.</param>
        /// <returns>An IEnumerable of matching objects.</returns>
        IEnumerable<T> ISearcher<T>.Search(ISearchCriteria<T> criteria)
        {
            if (_logger != null)
                _logger.Log(WindowsIdentity.GetCurrent().User, criteria.FriendlyString);
            return _wrappedSearcher.Search(criteria);
        }
        #endregion
    }
}

 

This does have the disadvantage of making LoggingSearcher depend on Unity, but you do what you gotta do.

Incidentally, as I explained in a previous post, it is possible to hard-code a Unity dependency as a default, and then allow an override with a config file. It's as simple as this:

 

using (var unity = new UnityContainer())
{
    // By default: no logging.
    unity.RegisterType(typeof(ISearcher<Document>), typeof(DocumentSearcher));

    // A chance to override with the config file.
    unity.LoadConfiguration();

    WebAppControllerFactory.DocumentSearcher = unity.Resolve<ISearcher<Document>>();
}

 

Our refactoring exercise has brought us to the point where

  • Each class does just one thing: searching, logging or whatever. This is the Single Responsibility Principle.
  • Following the Open/Closed Principle, we don't change a class once it is working. Instead, we extend it. For example, we added logging functionality to our searches not by modifying the DocumentSearcher class but by wrapping and extending it in LoggingSearcher.
  • We are careful to implement additional concrete classes behind interfaces without introducing surprises: the Liskov Substitution Principle.
  • Our interfaces are narrowly scoped (the Interface Segregation Principle).
  • We depend on abstractions, not concrete classes (the Dependency Inversion Principle). Rather than "newing up" an object in a class, we inject it into the class' constructor. Our choice of concrete classes happens in the application root, ideally with a config file.

Although our code is now SOLID, there is one more thing I'd like to show you. Earlier, we used the decorator pattern to extend the DocumentSearcher class with logging. We can do the same thing with Unity's interception feature.

Tags: , , ,

All | Dependency Injection | Talks

About the Author

Larry Spencer

Larry Spencer develops software with the Microsoft .NET Framework for ScerIS, a document-management company in Sudbury, MA.