Tritac (Master) ASP.NET MVC with StructureMap

ASP.NET MVC with StructureMap

21 May 2015  |  Developer

For a recent web project, we had the opportunity to use the state of the art .NET tools. Naturally we took the opportunity to take advantage of the new ASP.NET MVC goodness. We didn't stop there and wanted to use a modern Inversion of Control/Dependency Injection framework too.

Why Inversion of Control/Dependency Injection?

ASP.NET MVC encourages better architectual design over regular ASP.NET. Inversion of Control (IoC) implemented by way of the Depedenncy Injection (DI) pattern takes it a step further. It allows your classes to be less tighly coupled, which gives you less duplication, cleaner code, better maintainability and (unit) testability. It does not solve world hunger though. What we want to avoid is something like:

    public MyController() {
        myService = new MyService(new MyDataAccessObject(ConfigurationManager.AppSettings["dbcon")));
    }

In this way the controller nor the service nor the dao cannot be unit tested and they are tighly coupled, so replacing them with other implementations is not easy.

Which framework?

There are several .NET IoC/DI frameworks. I had previous experience with Spring.NET which is okay. I especially liked its AOP (Aspect Oriented Programming) capabilities. But it also quite verbose with its XML configuration. The documentation was unreadable and user hostile. Therefore we went on the hunt for something better. Key requirement was that it should be quick and easy to implement in the project. We would like to get rid of XML and use regular code, so we have IntelliSense and compile time errors.

  • Microsoft Unity. Microsoft is finally going the right way (in some ways at least) and has also developed a DI framework. However at the time of search (last year) version 2 was still in beta and moreover its primary focus was the desktop (wtf?). It seemed non trivial to get it to work in a web application. Above that the configuration is XML.
  • Castle Windsor. There was no Getting Started documentation and the documentation overall was a mess. It required extra items in de web.config (the others don't).
  • StructureMap. Easy to use immediately. It also seems the most widely used with ASP.NET MVC, so when you have problems, there is likely to be a solution on StackOverflow. However, the documentation lags behind and is not quite complete. And it has only a handful of active developers. (Most important: the website is ugly, as if designed by Jakob Nielsen)

The choice fell on StructureMap as it was easy to get running. The negatives were never a serious problem.

How does it work?

Rather than creating the objects in your controller, you put the interfaces in its constructor. When a request to the controller is made (and it does not exist yet), the controller object is created and the depedencies are resolved by StructureMap. I.e. it looks at the arguments of the constructor and tries to match the interfaces to actual classes. For example, the controller would be:

    protected IMyService myService;
 
    public MyController(IMyService arg_myService) {
        myService = arg_myService;
    }

The service interface and class would be:

    public interface IMyService {
 
   }

   public class MyService : IMyService {
 
      protected IMyDataAccessObject myDao;
 
      public MyService(IMyDataAccessObject arg_myDao) {
        myDao = arg_myDao;
      }
   }

Unlike Spring.NET, where you have to configure every mapping of FoobarService to IFoobarService, StructureMap can do so automatically based on the convention that the class maps to the interface with an "I" before its name. Naturally you can implement your own convention if you like, or you can override it for specific cases. We'll see how later on. You can also use Setter Injection like you can in Spring.NET (see example below), but the favoured way of injection is this way, Constructor Injection. While this excellent as it is clearer to the developer what is injected, it has also a pitfall. This constructor injection does not allow cycles: you cannot have MyService referring to IMySecondService and MySecondService to IMyService. You'll have to defer shared code to a third class or find another solution. Finally, I could not get Setter Injection to work with ASP.NET MVC Controllers.

How to use in an ASP.NET MVC project?

Separate IoC project

I like to keeps things separated. So if you're like me, your project will be structured somewhat like this (three-tier architecture):

  • MyProject.App.UI - web presentation tier: an ASP.NET MVC project
  • MyProject.App.ScheduledTask - not uncommonly, there will be a need to run a console application as daily scheduled task
  • MyProject.Business - application/business logic tier
  • MyProject.Data - data tier
  • MyProject.Data.NHibernate - data tier, NHibernate specific

As the web app and the scheduled task will share some of the IoC configuration it makes sense to keep it in a separate project as well. So:

  • MyProject.IoC - IoC tier with configuration for business and data tiers

StructureMap is contained in one dll that is referenced by the App and IoC projects.

Bootstrap in the ASP.NET MVC application

The StructureMap configuration is done at application start. We create a ContainerBootstrap class as start point to bootstrap the IoC container. Such a bootstrap should be in the App.UI and App.ScheduledTask. The bootstrap in the web app is called from Global.asax:

     protected void Application_Start() {
         log4net.Config.XmlConfigurator.Configure();
         AreaRegistration.RegisterAllAreas();
         RegisterRoutes(RouteTable.Routes);

         // Bootstrap StructureMap IoC container
         IoC.ContainerBootstrap.Bootstrap();

         // Wire up controller factory to StructureMap
         ControllerBuilder.Current.SetControllerFactory(new IoC.ControllerFactory());
     }

The bootstrap class looks like this (in our case, located in the IoC folder below the root of the web app):

namespace MyProject.App.UI.IoC {
    public static class ContainerBootstrap {
 
        public static void Bootstrap() {
            ObjectFactory.Initialize(x => {
                x.For<ILog>().Singleton().Use(LogManager.GetLogger("UILogger"));
 
                x.Scan(y => {
                    y.Assembly("MyProject.IoC");
                    y.TheCallingAssembly();
                    // auto discover registries in registries folder
                    y.LookForRegistries();
 
                    y.WithDefaultConventions();
                });
 
            });
 
        }
 
    }
}

We use a log4net logger. We tell StructureMap to resolve every ILog to the logger UILogger, configured in the web.config, with:

x.For<ILog>().Singleton().Use(LogManager.GetLogger("UILogger"));

It loads the assembly MyProject.IoC thanks to: y.Assembly("MyProject.IoC"). With y.LookForRegistries() we tell it look for separate registries in the loaded assemblies: the current from the web app and registries from the IoC project we just told it to load. Such a registiry is the UIRegistry in the UI folder below the IoC folder and looks something like this (just an example):

namespace MyProject.UI.IoC.Registries {
    public class UIRegistry : Registry {
        public UIRegistry() { 
            For<IFormsAuthenticationService>().Singleton().Use<FormsAuthenticationService>();
            For<IMembershipService>().Singleton().Use<AccountMembershipService>();
            For<MembershipProvider>().Singleton().UseMembershipProvider>();                        
        }        
    }
}

The rest in the UI is done automatically by convention with: y.WithDefaultConventions().

ASP.NET MVC specific

The rest of classes in the web app however won't be much. The main things are the controllers. For the controllers you need a specific ControllerFactory, as you saw declared in the Global.asax. The ControllerFactory is really simple:

namespace Trijact.UI.IoC {
    public class ControllerFactory : DefaultControllerFactory {
        protected override IController GetControllerInstance(System.Web.Routing.RequestContext requestContext, System.Type controllerType) {
            if (controllerType == null) {
                return base.GetControllerInstance(requestContext, controllerType);
            } 
            return (Controller) ObjectFactory.GetInstance(controllerType);
        }
 
    }
}

This works in both ASP.NET MVC 2 en 3. I've read that it can be even simpler in ASP.NET MVC 3. But this is simple enough.

Business tier

In the IoC project, we have a registries folder with a BusinessRegistry class. Sample registry for the business tier:

namespace MyProject.IoC.Registries {
 
    public class BusinessRegistry : Registry {
        public BusinessRegistry() {
            string filesDir = ConfigurationManager.AppSettings["filesDir"];            
            For<IFileService>().Singleton().Use<FileService>().Ctor<string>("filesDirectory").Is(filesDir);
            Scan(x => {
                x.Assembly("MyProject.Business");
                x.Convention<SingletonConvention>();
            });
        }        
    }
}

As strings don't have interfaces, you have to tell StructureMap what it should use. The FileService requires a string fileDirectory as argument, (apparently) a files directory set in the web.config.

Singleton convention

We wanted to improve performance by not having the objects created at each request to the Controller. So we can declare it a singleton. The default convention is that objects are not singleton. You could declare each mapping singleton as shown above for the

FileService (For&tlt;IFileService>().Singleton().Use<FileService>())

, but you can also create your own convention to do so automatically:

namespace MyProject.IoC {
    public class SingletonConvention : DefaultConventionScanner {
 
        public override void Process(Type type, Registry registry) {
            if (type.IsAbstract) return;
            Type pluginType = FindPluginType(type);
            if (pluginType != null && Constructor.HasConstructors(type)) {
                registry.AddType(pluginType, type);
 
                ConfigureFamily(registry.For(pluginType).Singleton());
            }
        }
 
    }
}

This is just a copy of the default implementation of Process, with exception for the Singleton declaration at the end.

Data tier

Using NHibernate or another ORM framework is a separate topic, but this is how the DataRegistry might look like:

namespace MyProject.IoC.Registries {
    public class DataRegistry : Registry {
        public DataRegistry() {
            var dbSettings = new DatabaseSettings();
            dbSettings.ConnectionString = System.Configuration.ConfigurationManager.ConnectionStrings["trijact_default"].ToString();
            For<DatabaseSettings>().Singleton().Use(dbSettings);
 
            var unitOfWorkFactory = new UnitOfWorkFactory();
            For<IUnitOfWorkFactory>().Singleton().Use(unitOfWorkFactory);            
 
            string filesDir = SysConfig.ConfigurationManager.AppSettings["filesDir"];            
 
            var dbConfig = new NHibernateDatabaseConfig(dbSettings, null, filesDir);
            For<Configuration>().Singleton().Use(dbConfig.Configuration);
            For<ISessionFactory>().Singleton().Use(dbConfig.SessionFactory);
            
            Scan(x => {
                x.AssemblyContainingType<ProjectDao>();
                x.Convention<SingletonConvention>();                
            });
 
            // Setter injection of DatabaseConfig
            FillAllPropertiesOfType<IDatabaseConfig>().Singleton().Use(dbConfig);
        }
    }
}

In our case, every dao inherits from a BaseDao. It is easier to use Setter Injection then, so you won't have to have the database configuration object in every Dao constructor. The setter injection is done by:

FillAllPropertiesOfType<IDatabaseConfig>().Singleton().Use(dbConfig);

Conclusion

Once you have it setup, you'll hardly notice the presence of StructureMap. It is silently doing its job, like technologies should.