Since the release of MVC Turbine, I’ve been getting lots of questions about the design decisions I made around the interaction with the Service Locator that ships with the plug-in.  The two main questions are:

  1. Why didn’t you use the Common Service Locator project from CodePlex?
  2. Why are we forced/constrained/etc to use generic component registration API?

These questions are very valid and could be easily explained if I ever write the documentation for the project (which, I’m working on by the way). So in this post, I hope to provide some insight on why these decisions were made and how you’re not really limited on how Turbine handles type registration.

Lack of Common Service Locator Support

With my original prototypes of bundling the Turbine bits, I had Common Service Locator (CSL) as the method for resolving types from the underlying container. This was all fine and great since I was using a different method for registering types into the application.  At the time, I was using Windsor Container (my container of choice) and XML registration since it was the most flexible of the methods for registering types.

Side Note: At one point, I was planning on supporting only support for Windsor. This meant that Turbine would be intimately familiar with the IWindsorContainer. However, after looking at lots of “use IoC with your MVC Application” samples, I made a conscious decision to make it IoC container agnostic and work hard to make things play nice regardless the choice you made with your application.

As I kept playing with the guts of Turbine and the whole “convert flow into useful work” concept, I realized that I needed a way for Turbine to register the types it needed for things to just work. The CSL doesn’t support registration of types, so obviously this was an issue that needed to be addressed. The first thing I did was create a new type to do service registration (originally called IComponentRegistration) but the interaction between IServiceLocator (from CSL) and IComponentRegistration felt and looked weird in the code.  I spent a lot of time of looking at different IoC containers and noticed that they don’t split registration from resolution of types within their APIs. (Yes, some containers provide a nice fluent API that provides more syntactic sugar around the process but underneath the covers they still deal with a kernel).

With all this research under my belt, I made the decision to add the registration methods from IComponentRegistration into IServiceLocator and remove some of the clutter with the API.  Essentially this accomplished the following:

  1. It provided the registration support the plug-in needed to auto-wire components on the application’s behalf.
  2. For new users of MVC and IoC, it provided less indirection (clutter) for them which hopefully would make them understand the need for IoC within their application.
  3. For veteran users of IoC, this is something they should be familiar with their corresponding containers (funny enough, each container does things differently enough with registration that make it hard to make ‘common’).

Once I had these pieces in place, I realized that was I being a bit selfish with all this auto-wiring component (which to me, makes Turbine an attractive consideration) and that I could extend it to your application without causing havoc in your code. From this thought, the IServiceRegistration interface was born; which provides enough power for simple component registration with your application.  After all, Turbine is not an IoC container or a gateway to one (well, it is kind of but that’s just a side effect from the internal registration needs), so it leaves the heavy lifting to the real IoC container under the covers.

Generic Component Registration API

Now that we’ve covered the reasons behind the lack of CSL support and how the IServiceRegistation came to be, let me address how Turbine provides a way for you to take full advantage of your IoC du jour. :)

Out of the box, Turbine scans your assemblies for the following core type implementations:

So, if Turbine finds a type that implements any of those interfaces, it will register it with the default container (impl of IServiceLocator) and then resolve it at the time it’s needed within the pipeline. Which means, if no core type implementation is defined in any assembly within your AppDomain, it will not be called. Let’s pocket this statement for a bit. :)

With this basic knowledge, let’s take a look at the implementation of an IServiceLocator that ships with Turbine. One thing you’ll notice is that each implementation allows you to provide (inject) your own underlying (initialized) IoC container.  For the following examples, we’ll use the WindsorServiceLocator:

   1: public class WindsorServiceLocator{
   2:     // Default constructor that creates an empty underlying container
   3:     public WindsorServiceLocator() : this(new WindsorContainer()) {}
   4:  
   5:     // Constructor that uses an externally initialized container
   6:     public WindsorServiceLocator(IWindsorContainer container){
   7:         Container = container;
   8:     }
   9:  
  10:     public IWindsorContainer Container {get; private set; }
  11: }

As you can see from the above snippet, the WindsorServiceLocator allows you provide an externally initialized instance of IWindsorContainer. This means that you can use any method to create and initialize your underlying container then pass it into Turbine to do the registration it requires for pieces to auto-wire components. In other words, if you want to take advantage of any feature of your IoC of choice, you have every liberty to do so! Take for example, this sample code that uses Castle Windsor’s Fluent API to do all domain service registrations:

   1: public class DefaultMvcApplication : TurbineApplication {
   2:     static DefaultMvcApplication() {
   3:         // Get the default container to use with the application
   4:         var defaultContainer = CreateWindsorContainer();
   5:  
   6:         // Pass in the initiated container to the service locator to use
   7:         ServiceLocatorManager.SetLocatorProvider(() => new WindsorServiceLocator(defaultContainer));
   8:     }
   9:  
  10:     /// <summary>
  11:     /// Create the container and do custom initialization
  12:     /// </summary>
  13:     /// <returns></returns>
  14:     static IWindsorContainer CreateWindsorContainer() {
  15:         // Create the empty container to use
  16:         WindsorContainer container = new WindsorContainer();
  17:  
  18:         // Add the array resolver to handle array types
  19:         container.Kernel.Resolver.AddSubResolver(new ArrayResolver(container.Kernel));
  20:  
  21:         // Register fluently
  22:         // For more info, go to http://using.castleproject.org/display/IoC/Fluent+Registration+API
  23:         container
  24:             .Register(Component.For<IMessageService>()
  25:                 .ImplementedBy<DefaultMessageService>()
  26:                 .LifeStyle
  27:                 .Singleton)
  28:  
  29:             .Register(AllTypes.Of<ISimpleMessageService>()
  30:                 .FromAssembly(typeof(DefaultMvcApplication).Assembly)
  31:                 .WithService
  32:                 .FromInterface(typeof(ISimpleMessageService))
  33:                 .Configure(c => c.LifeStyle.Transient));
  34:  
  35:         return container;
  36:     }
  37: }

In this code sample the following is taking place:

  • An ArrayResolver is being added to Windsor to allow types to take in arrays of dependencies via constructor injection.
  • The type of DefaultMessageService is being registered for IMessageService as a Singleton. Which means, the type will be created once and it will be life time of the instance will be handled by the container.
  • All types of ISimpleMessageService are registered as a transient object so they could be fed into the type that needs them.

This fluent type of registration make this type of domain service implementation possible:

   1: public class DefaultMessageService : IMessageService {
   2:     // An array dependency is injected into the constructor (via ArrayResolver)
   3:     public DefaultMessageService(ISimpleMessageService[] services) {
   4:         Services = services;
   5:         InstanceCreated = DateTime.Now;
   6:     }
   7:  
   8:     public ISimpleMessageService[] Services { get; set; }
   9:     private DateTime InstanceCreated { get; set; }
  10:     
  11:     public string GetDefaultMessage() {
  12:         return "Welcome to ASP.NET MVC!";
  13:     }
  14:  
  15:     public string GetAboutMessage() {
  16:         return "Check out ASP.NET MVC at http://www.asp.net/mvc";
  17:     }
  18:  
  19:     public string GetInternalMessage() {
  20:         return string.Format("This instance was created on {0}.", InstanceCreated);
  21:     }
  22:  
  23:     public string[] GetMessages() {
  24:         var messages = new List<string>();
  25:  
  26:         foreach (var messageService in Services) {
  27:             messages.Add(messageService.GetMessage());
  28:         }
  29:  
  30:         return messages.ToArray();
  31:     }
  32: }

From here, the following is generated:

Capture Conclusion

As you can see, MVC Turbine does provide some flexibility on how your application can be built to best suit your needs. The reason why some of these features are not documented, well, that’s all my fault. :)

Please feel free to check out the code sample and try it out for yourself.

Happy Coding!