Monday, 23 December 2013

Configuring IoC Services per area in MVC

In the last blog post, I described a solution that was broken down into different areas per location. The MVC project has this structure:
  • Areas
    • UK
    • US
    • HK
  • Controllers
  • Views
Each area had shared UI implementations for the most part, but required specialisation on the business logic side.
Problem
Specialising the services isn’t too much of a problem. You have an interface for the basic functions, and then specialise them within. What made our implementation work difficult is that there were specific repositories for each area! This was because each area interacted with a database of differing schema AND differing platform.
Design
The design of the services is fairly straightforward. You create the interface and then specialise each implementation like this:
public interface IOrderService
{
   OrderSummaryViewModel GetOrderSummary(string orderNumber);

   OrderViewModel GetOrder(string orderNumber);
}

// US specific implementation
public class USOrderService : IOrderService
{
   protected readonly IOrderRepository orderRep;

   public USOrderService(IOrderRepository orderRep)
   {
      this.orderRep = orderRep;
   }

   public OrderSummaryViewModel GetOrderSummary(string orderNumber)
   {
      var order = orderRep.FirstOrDefault(o=>o.OrderReference == orderNumber);

      if(order==null)
         return OrderSummaryViewModel.NoOrderFound;

      var model = new OrderSummaryViewModel
      {
         OrderNumber = order.OrderReference,
         CustomerName = order.FirstName + " " + order.LastName,
         OrderDate = order.CreatedDate,
         OrderTotal = order.Lines.Sum(x=>x.Quantity * x.UnitPrice),
         TaxTotal = order.Lines.Sum(x=>x.Quantity * x.UnitTaxPrice)
      };

      return model;
   }
}

All very easy code. So lets say we have one service per area, resulting in:
  • HKOrderService - using the SqlRepository repository
  • USOrderService - using the OracleRepository repository
  • UKOrderService - using the SybaseRepository repository

Therefore, to summarise we have these chains of dependencies:

Area Controller (assuming Area namespace) Service Repository
UK OrderSummaryController UKOrderService SybaseRepository
US OrderSummaryController USOrderService OracleRepository
HK OrderSummaryController HKOrderService SqlRepository

Solution

We were using AutoFac as our DI tool. Since MVC has DI support out of the box, this made it easier to start with. But breaking down dependencies by area was a little more tricky.

AutoFac works by inheriting from a Module class, and then connecting the dependencies together within the module. If you’ve used StructureMap before, this is exactly the same as the Registry class.

Here is an example of how it is implemented

public class ServiceInitializationModule : Module
{
   private static readonly HKArea = "HK";
   private static readonly USArea = "US";
   private static readonly UKArea = "UK";

   private static readonly SqlConfigKey = "SQL";
   private static readonly OracleConfigKey = "ORA";
   private static readonly SybaseConfigKey = "SYB";

   public override Load(ContainerBuilder builder)
   {
      InitializeRepositories(builder);
      InitializeFormatters(builder);
      InitializeServices(builder);
   }

   // This registers a concrete implementation with an interface and assigns it a name for later retrieval.
   private void InitializeRepositories(ContainerBuilder builder)
   {
     builder.Register( r => new SqlRepository (ConfigurationManager.ConnectionStrings[SqlConfigKey].ConnectionString))
            .Named<IOrderRepository>(SqlDb)
            .InstancePerHttpRequest();

     builder.Register( r => new OracleRepository (ConfigurationManager.ConnectionStrings[OracleConfigKey].ConnectionString))
            .Named<IOrderRepository>(OracleDb)
            .InstancePerHttpRequest();

     builder.Register( r => new SybaseRepository (ConfigurationManager.ConnectionStrings[SybaseConfigKey].ConnectionString))
            .Named<IOrderRepository>(SybaseDb)
            .InstancePerHttpRequest();
   }

   private void InitializeFormatters(ContainerBuilder builder)
   {
      builder.Register( r => new UKCurrencyFormatter() )
             .Named<ICurrencyFormatter>()
             .Singleton();

      builder.Register( r => new USCurrencyFormatter() )
             .Named<ICurrencyFormatter>(USAREA)
             .Singleton();

      builder.Register( r => new HKCurrencyFormatter() )
             .Named<ICurrencyFormatter>(HKAREA)
             .Singleton();
   }

   private void InitializeServices(ContainerBuilder builder)
   {
      builder.Register( r => new UKOrderService(
                  r.ResolveNamed<IOrderRepository>(SybaseDb))
             .Named<IOrderService>(UKAREA)
             .InstancePerHttpRequest();

      builder.Register( r => new USOrderService(
                  r.ResolveNamed<IOrderRepository>(OracleDb))
             .Named<IOrderService>(USAREA)
             .InstancePerHttpRequest();

      builder.Register( r => new HKOrderService(
                r.ResolveNamed<IOrderRepository>(SqlDb))
             .Named<IOrderService>(HKAREA)
             .InstancePerHttpRequest();
    }
}

This is only half the story. The DI is set up, but the controllers were taking interfaces in as parameters. Here they are again:

// Base Controller
public abstract class OrderSummaryBaseController : Controller
{
   protected readonly IOrderService orderService;
   protected readonly ICurrencyFormatter formatter;

   protected OrderSummaryBaseController (IOrderService orderService, ICurrencyFormatter formatter) 
   {
      this.orderService = orderService;
      this.formatter = formatter;
   }
}

// UK/Controller
public class OrderSummaryController : OrderSummaryBaseController
{
   public OrderSummaryController () 
     : base(new UKOrderService(), new UKCurrencyFormatter()) {}

   public OrderSummaryController (IOrderService service, ICurrencyFormatter formatter) 
     : base(service, formatter) {}
}

So what is the problem? The problem is that there are multiple controllers called OrderSummaryController. They all have the same constructor, which looks identical and does conflict with the base class.
Quick-recap
I'm throwing a lot of code at you at the moment. It is an important lesson to learn about how this solution is working in the bigger scheme of things. We want to use a controller and its base to share functionality. We also want to make sure the signatures match, as it will making unit testing much easier. My solution is attempting to keep the constructors the same signature, so that a) unit testing can be easier and b) so that when a new area is created, just the constructors need copying into the new file.

Ahh - you're back! Ok, so what are we trying to do? We want Autofac to differentiate between a class and its base. You'll find Autofac throws all sorts of resolution errors in this scenario. Luckily, if you don't mind taking the plunge and allowing a litle AutoFac code into your concrete controllers, all is not lost!

// UK/Controller
public class OrderSummaryController : OrderSummaryBaseController
{
   public OrderSummaryController () 
     : base(new UKOrderService(), new UKCurrencyFormatter()) {}

   public OrderSummaryController (IOrderService service, ICurrencyFormatter formatter) 
     : base(service, formatter) {}

   public OrderSummaryController (IComponentContext autoFacContext) 
     : base( 
          autoFactContext.ResolveNamed<IOrderService>(Areas.UK),  // <- Resolves the UKOrderService
          autoFacContext.ResolveNamed<ICurrencyFormatter>(Areas.UK));// <- Resolves the UKCurrencyFormatter
   {
   }
}

This constructor overload gives AutoFac an easy way in. The beauty of it is that the application is still unit testable via the IOrderService and ICurrencyFormatter, but the IComponentContext is giving you an easier (i.e. time-saving) way in. Now, time for the hooking-up within MVC. We need to register that Module, so that MVC resolves items correctly. The easiest (and most common way) is to inherit from the DefaultControllerFactory - the MVC built-in resolver - and register the ServiceInitializationModule. We then re-point MVC at this resolver instead of its default one.

public class AutofacControllerFactory : DefaultControllerFactory
{
   private readonly ContainerBuilder builder;

   public AutofacControllerFactory()
   { 
      this.builder = new ContainerBuilder();
      this.AddBindings();
   }

   protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
   {
      if (controllerType == null)
      {
         return null;
      }

      var controller = DependencyResolver.Current.GetService(controllerType) as IController;

      return controller;
   }

   public virtual void AddBindings()
   {
      this.builder.RegisterModule(new ServiceInitializationModule());
      DependencyResolver.SetResolver(new AutofacDependencyResolver(this.container)); //<-- Tell MVC to use the AutoFac resolver, instead of the default.
   } 
}

And now tell MVC to use this AutofacControllerFactory when creating controllers...

// Global.asax.cs
protected void Application_Start()
{
   AreaRegistration.RegisterAllAreas();

   // Use the MVC 4 file structure, as its cleaner.
   FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
   RouteConfig.RegisterRoutes(RouteTable.Routes);
   BinderConfig.RegisterModelBinders(ModelBinders.Binders);

   // Resolve using my custom controller factory
   ControllerBuilder.Current.SetControllerFactory(new AutofacControllerFactory());
}

And we are done!
Summary

In this blog, I've described a scenario where an MVC application is separated into areas. Those areas all use different services, due to the business logic being different for them. In addition, the repositories themselves are also different.

Therefore, you should have come away with the following points

  1. A base controller can have multiple implementations in different areas
  2. Areas can have different services being injected into the controllers
  3. The services can even have repositories of differing concrete types injected as well.
This analysis took several weeks to get working. I wasn't able to find it possible with any other framework, but if you do find one that does, let me know!



Shared user interfaces, but still allowing for customisation per area

In the last blog post, I described a solution that was broken down into different areas per location. The MVC project has this structure:
  • Areas
    • UK
    • US
    • HK
  • Controllers
  • Views
Each area had shared UI implementations for the most part, but required specialisation on the business logic side.
Problem
Specialising the services isn’t too much of a problem. You have an interface for the basic functions, and then specialise them within. What made our implementation work difficult is that there were specific repositories for each area! This was because each area interacted with a database of differing schema AND differing platform.
Design
The design of the services is fairly straightforward. You create the interface and then specialise each implementation like this:
public interface IOrderService
{
   OrderSummaryViewModel GetOrderSummary(string orderNumber);

   OrderViewModel GetOrder(string orderNumber);
}

// US specific implementation
public class USOrderService : IOrderService
{
   protected readonly IOrderRepository orderRep;

   public USOrderService(IOrderRepository orderRep)
   {
      this.orderRep = orderRep;
   }

   public OrderSummaryViewModel GetOrderSummary(string orderNumber)
   {
      var order = orderRep.FirstOrDefault(o=>o.OrderReference == orderNumber);

      if(order==null)
         return OrderSummaryViewModel.NoOrderFound;

      var model = new OrderSummaryViewModel
      {
         OrderNumber = order.OrderReference,
         CustomerName = order.FirstName + " " + order.LastName,
         OrderDate = order.CreatedDate,
         OrderTotal = order.Lines.Sum(x=>x.Quantity * x.UnitPrice),
         TaxTotal = order.Lines.Sum(x=>x.Quantity * x.UnitTaxPrice)
      };

      return model;
   }
}

All very easy code. So lets say we have one service per area, resulting in:
  • HKOrderService - using the SqlRepository repository
  • USOrderService - using the OracleRepository repository
  • UKOrderService - using the SybaseRepository repository

Therefore, to summarise we have these chains of dependencies:

Area Controller (assuming Area namespace) Service Repository
UK OrderSummaryController UKOrderService SybaseRepository
US OrderSummaryController USOrderService OracleRepository
HK OrderSummaryController HKOrderService SqlRepository

Solution

We were using AutoFac as our DI tool. Since MVC has DI support out of the box, this made it easier to start with. But breaking down dependencies by area was a little more tricky.

AutoFac works by inheriting from a Module class, and then connecting the dependencies together within the module. If you’ve used StructureMap before, this is exactly the same as the Registry class.

Here is an example of how it is implemented

public class ServiceInitializationModule : Module
{
   private static readonly HKArea = "HK";
   private static readonly USArea = "US";
   private static readonly UKArea = "UK";

   private static readonly SqlConfigKey = "SQL";
   private static readonly OracleConfigKey = "ORA";
   private static readonly SybaseConfigKey = "SYB";

   public override Load(ContainerBuilder builder)
   {
      InitializeRepositories(builder);
      InitializeFormatters(builder);
      InitializeServices(builder);
   }

   // This registers a concrete implementation with an interface and assigns it a name for later retrieval.
   private void InitializeRepositories(ContainerBuilder builder)
   {
     builder.Register( r => new SqlRepository (ConfigurationManager.ConnectionStrings[SqlConfigKey].ConnectionString))
            .Named<IOrderRepository>(SqlDb)
            .InstancePerHttpRequest();

     builder.Register( r => new OracleRepository (ConfigurationManager.ConnectionStrings[OracleConfigKey].ConnectionString))
            .Named<IOrderRepository>(OracleDb)
            .InstancePerHttpRequest();

     builder.Register( r => new SybaseRepository (ConfigurationManager.ConnectionStrings[SybaseConfigKey].ConnectionString))
            .Named<IOrderRepository>(SybaseDb)
            .InstancePerHttpRequest();
   }

   private void InitializeFormatters(ContainerBuilder builder)
   {
      builder.Register( r => new UKCurrencyFormatter() )
             .Named<ICurrencyFormatter>()
             .Singleton();

      builder.Register( r => new USCurrencyFormatter() )
             .Named<ICurrencyFormatter>(USAREA)
             .Singleton();

      builder.Register( r => new HKCurrencyFormatter() )
             .Named<ICurrencyFormatter>(HKAREA)
             .Singleton();
   }

   private void InitializeServices(ContainerBuilder builder)
   {
      builder.Register( r => new UKOrderService(
                  r.ResolveNamed<IOrderRepository>(SybaseDb))
             .Named<IOrderService>(UKAREA)
             .InstancePerHttpRequest();

      builder.Register( r => new USOrderService(
                  r.ResolveNamed<IOrderRepository>(OracleDb))
             .Named<IOrderService>(USAREA)
             .InstancePerHttpRequest();

      builder.Register( r => new HKOrderService(
                r.ResolveNamed<IOrderRepository>(SqlDb))
             .Named<IOrderService>(HKAREA)
             .InstancePerHttpRequest();
    }
}

This is only half the story. The DI is set up, but the controllers were taking interfaces in as parameters. Here they are again:

// Base Controller
public abstract class OrderSummaryBaseController : Controller
{
   protected readonly IOrderService orderService;
   protected readonly ICurrencyFormatter formatter;

   protected OrderSummaryBaseController (IOrderService orderService, ICurrencyFormatter formatter) 
   {
      this.orderService = orderService;
      this.formatter = formatter;
   }
}

// UK/Controller
public class OrderSummaryController : OrderSummaryBaseController
{
   public OrderSummaryController () 
     : base(new UKOrderService(), new UKCurrencyFormatter()) {}

   public OrderSummaryController (IOrderService service, ICurrencyFormatter formatter) 
     : base(service, formatter) {}
}

So what is the problem? The problem is that there are multiple controllers called OrderSummaryController. They all have the same constructor, which looks identical and does conflict with the base class.
Quick-recap
I'm throwing a lot of code at you at the moment. It is an important lesson to learn about how this solution is working in the bigger scheme of things. We want to use a controller and its base to share functionality. We also want to make sure the signatures match, as it will making unit testing much easier. My solution is attempting to keep the constructors the same signature, so that a) unit testing can be easier and b) so that when a new area is created, just the constructors need copying into the new file.

Ahh - you're back! Ok, so what are we trying to do? We want Autofac to differentiate between a class and its base. You'll find Autofac throws all sorts of resolution errors in this scenario. Luckily, if you don't mind taking the plunge and allowing a litle AutoFac code into your concrete controllers, all is not lost!

// UK/Controller
public class OrderSummaryController : OrderSummaryBaseController
{
   public OrderSummaryController () 
     : base(new UKOrderService(), new UKCurrencyFormatter()) {}

   public OrderSummaryController (IOrderService service, ICurrencyFormatter formatter) 
     : base(service, formatter) {}

   public OrderSummaryController (IComponentContext autoFacContext) 
     : base( 
          autoFactContext.ResolveNamed<IOrderService>(Areas.UK),  // <- Resolves the UKOrderService
          autoFacContext.ResolveNamed<ICurrencyFormatter>(Areas.UK));// <- Resolves the UKCurrencyFormatter
   {
   }
}

This constructor overload gives AutoFac an easy way in. The beauty of it is that the application is still unit testable via the IOrderService and ICurrencyFormatter, but the IComponentContext is giving you an easier (i.e. time-saving) way in. Now, time for the hooking-up within MVC. We need to register that Module, so that MVC resolves items correctly. The easiest (and most common way) is to inherit from the DefaultControllerFactory - the MVC built-in resolver - and register the ServiceInitializationModule. We then re-point MVC at this resolver instead of its default one.

public class AutofacControllerFactory : DefaultControllerFactory
{
   private readonly ContainerBuilder builder;

   public AutofacControllerFactory()
   { 
      this.builder = new ContainerBuilder();
      this.AddBindings();
   }

   protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
   {
      if (controllerType == null)
      {
         return null;
      }

      var controller = DependencyResolver.Current.GetService(controllerType) as IController;

      return controller;
   }

   public virtual void AddBindings()
   {
      this.builder.RegisterModule(new ServiceInitializationModule());
      DependencyResolver.SetResolver(new AutofacDependencyResolver(this.container)); //<-- Tell MVC to use the AutoFac resolver, instead of the default.
   } 
}

And now tell MVC to use this AutofacControllerFactory when creating controllers...

// Global.asax.cs
protected void Application_Start()
{
   AreaRegistration.RegisterAllAreas();

   // Use the MVC 4 file structure, as its cleaner.
   FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
   RouteConfig.RegisterRoutes(RouteTable.Routes);
   BinderConfig.RegisterModelBinders(ModelBinders.Binders);

   // Resolve using my custom controller factory
   ControllerBuilder.Current.SetControllerFactory(new AutofacControllerFactory());
}

And we are done!
Summary

This is part 1 of a series of blogs, where I aim to give an answer to a question I posted on StackOverflow a while ago. Although the answer was useful, the solution was already implemented and thought it would be best to describe what the problem in more detail.

Problem

Our team were tasked with creating a consistent looking website, which would interact across several different locations. The locations would all be owned by a parent company, who were creating the site. At the project outset, an estimated 70-90% of the UI would look the same. There were caveats to be aware of when developing the site.
  1. Each location used a different order number format
  2. Each location has a custom currency format for the screen. For example;
    • United Kingdom (2 decimal places) = 1.00 GBP
    • Hong Kong (4 decimal places) = 1.0000 HKD
    • United States (3 decimal places) = 1.000 USD
  3. Each location had a different database schema – as these companies were buyouts.
  4. Each location may contain a different database platform.
  5. Each location may (or may not) require customised business rules. A base set of rules would be applicable and customised as such.

Design

The application was divided into 3 parallel sections / areas. The MVC structure of the application looked like this:
  • Areas
    • UK
      • Controllers
      • Views
    • US
    • HK
  • Controllers
  • Views
We also had some idea that most of the functionality would be the same, expect for specific UI tweaks. Luckily, the idea of shared functionality became apparent very early on in the project, so we had a starting point to go from.

Solution

We decided to have a base controller to contain a usual-case scenario for a particular process in the shared ~/Controllers directory. Each area would then inherit from this base controller, so that functionality was always available for all areas by default. The only additional work required was when the area needed to specialise the behaviour.
Base Controller Implementation
Here is an example of this approach:
// ~/Controllers
public abstract class OrderSummaryBaseController : Controller
{
   protected readonly IOrderService orderService;
   protected readonly ICurrencyFormatter currencyFormatter;

   protected OrderSummaryBaseController(IOrderService orderService, ICurrencyFormatter currencyFormatter)
   {
      this.orderService = orderService;
      this.currencyFormatter = currencyFormatter
   }

   public virtual ViewResult Index(string orderNumber)
   {
      var viewModel = this.orderService.GetOrderSummary(orderNumber);
      viewModel.Formatter = currencyFormatter;

      return this.View(viewModel);
   }
}

// ~/Areas/US/Controllers
public class OrderSummaryController : OrderSummaryBaseController
{
   // Poor mans IoC - to demonstrate the intent
   public OrderSummaryController()
        : base( new USOrderService(), new USCurrencyFormatter() )   {}
}
This was a good starting point for us, because we always had out-of-the-box functionality available to us. We only implemented additional functionality when required.
Overriding a view
We also had the additional benefit of having some inside knowledge of the ViewModel coming back, because we knew what service we were calling. This came in handy when the View had to be re-implemented. For example:

// ~/Areas/US/Controllers

public override ViewResult Index(string orderNumber)
{
   var viewModel = this.orderService.GetOrderSummary(orderNumber);

   var usViewModel = viewModel as USViewModel;

   if(usViewModel!=null)
   {
      usViewModel.HideTaxCalculations = true;
   }

   return this.View(viewModel);
}
I am abusing the inheritance structure here. It is almost as good as putting new in the method declaration. To respect the base classes implementation, another approach is:
// ~/Areas/US/Controllers
public override ViewResult Index(string orderNumber)
{
   var viewResult = base.Index(orderNumber);
   var viewModel = viewResult.ViewData.Model;

   var usViewModel = viewModel as USViewModel;

   if(usViewModel!=null)
   {
      usViewModel.HideTaxCalculations = true;
   }

   return this.View(viewModel);
}

Quick re-cap - what have we actually achieved here?

What we have achieved is a shared single controller, that allows customisation when required. By default, all controllers will use the same view. If you wanted to add a significantly different view, you can add it to the area, so that there is a clean separation of concerns. So lets say the UK has a customised branding of the order summary screen - radically different from the default, you can add it to the specific area:
  • Areas
    • UK 
      • Controllers 
        • OrderSummaryController
      • Views
        • OrderSummary
          • Index.cshtml
    • US / HK
      • Controllers
        • OrderSummaryController
  • Controllers
    • OrderSummaryBaseController
  • Views
    • OrderSummary
      • Index.cshtml

Summary

In this blog post, I demonstrated the use of a shared controllers, whilst allowing customisation between areas when required. This allows a new area to be built *almost* out of the box immediately.

There is a problem with this example; every controller is using a poor mans Dependency Injection. That means when a new area is brought in, every controller needs to be re-implemented.

So in the next blog, I will show you how we tackled this problem, so that new areas need only a little configuration to get up and running. In this blog, I've described a scenario where an MVC application is separated into areas. Those areas all use different services, due to the business logic being different for them. In addition, the repositories themselves are also different.

Therefore, you should have come away with the following points

  1. A base controller can have multiple implementations in different areas
  2. Areas can have different services being injected into the controllers
  3. The services can even have repositories of differing concrete types injected as well.
This analysis took several weeks to get working. I wasn't able to find it possible with any other framework, but if you do find one that does, let me know!