Mapping you ApiControllers with Ninject

I am a big fan of Dependency Injection.  I think it can make your life tremendously easier by automating a lot of the wireup within an application.  For Spotted, I am working on a Web API backend that the mobile applications will talk to.  I wanted to use Ninject for this.

Now, WebAPI still being fairly new, and the fact that Microsoft decided to eliminate the commonly used DefaultControllerFactory in favor of a class implementing the IDependencyResolver interface.  I wanted to search for this and hope I could draw some inspiration for this from work done via MVC3 Controller Factory patterns, since it was roughly the same thing.  Needless to say I could not find much and what I did find scared me.  Stuff like this, which insinuate that you create a bevy of Ninject modules in your application, scares me, because there are people out there who wont know any better.  It frightened me so much that I had to blog my solution.

The purpose of Dependency Injection is NOT to simply look cool, quite the contrary, it is a very useful pattern.  However, I have noticed two anti-patterns cropping up around it.  One is that teams seem to define a variety of fluent configuration files all over their application, instead of centralizing these files.  The other, and in my mind more critical, is that people seem OK with defining a ton of mappings, and the configuration files quickly run long.  This can be a problem for MVC like applications if you start mapping each controller individually; creating interfaces for each controller makes this even worse.  This is where you start to look at convention and realize that the interface to class need NOT be one to one, but can be one to many.  Lets look at how Spotted does this:

Here are the important methods from NinjectDependencyResolver:

        public object GetService(Type serviceType)
        {
            // get the controller name
            string controllerName = serviceType.Name.AsControllerName();
            if (string.IsNullOrEmpty(controllerName))
                return null;
            
            return _ninjectKernel.TryGet(
                                       controllerName, new IParameter[0]);
        }

        public IEnumerable<object> GetServices(Type serviceType)
        {
            try
            {
                var list = _ninjectKernel.GetAll(serviceType);
                return list;
            }
            catch
            {
                return new object[0];
            }
        }

        public IDependencyScope BeginScope()
        {
            return this;
        }

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

The most important method in this sample is GetService(Type type), as this is the method which actually returns the constructor as needed.  The important thing to understand is that most DI frameworks, including Ninject, support “metadata” when defining the mappings.  In this case, we associate a name with the mapping.  This allows us to have multiple mappings to the same type (IHttpController) but still have the ability to distinctly identify them using the name (SpotController becomes ‘Spot’).  I used an extension method here (AsControllerName()) to extract the name regardless of circumstance, returning null if it cannot be done.

Central to the ability to extract types in Ninject is the Kernel.  Think of the Kernel as a special class that replaces new.  Asking the kernel for an instance of a class (TryGet) returns an instance based on the rules defined within the kernel, also called the container.  Containers serve many purposes within Dependency Injection, but chiefly type resolution and lifecycle maintenance.

The Kernel is comprised of a number of modules which define the mappings for the Kernel (container).  Here is my module which maps ALL of my controllers to IHttpController with that “name” which is used above to extract the type:

public override void Load()
{
     var applicableTypes = _targetAssembly.GetTypes()
          .Where(t => t.GetInterfaces().Contains(typeof (IHttpController)))
          .ToList();

     applicableTypes.ForEach(hc =>
     {
          if (!string.IsNullOrEmpty(hc.Name))
          {
               var controllerName = hc.Name.AsControllerName();
               Bind().To(hc).Named(controllerName);
          }
     });
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

This is only done once, so its not too much of a performance hit.  By finding all types in the given assembly which implement the IHttpController interface and storing metadata it reduces the amount of code we have to write and maintain.  In addition, adding new controllers to the solution does NOT require any coding changes, just make sure you still write your tests to ensure that your container is returning the right controller.  My first test after creating the controller is to make sure I can get an instance of the controller out of the container.

This concept is part of the “convention over configuration” mentality.  Most every developer defines controllers the same way, in fact frameworks like ASP .NET MVC/WebAPI require controllers be specified in this way, save large amounts of custom code being written.  With that rule in mind, it makes ZERO sense to individually define your mappings.  Do them together as part of a convention, save yourself the time and remove the need to update Ninject modules each time you add new code.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s