Click here to Skip to main content
15,867,890 members
Articles / Web Development / ASP.NET

Integrating a LinFu IoC Container with your ASP.NET MVC Application

Rate me:
Please Sign up or sign in to vote.
0.00/5 (No votes)
31 Jul 2010CPOL3 min read 8.1K   2  
Integrating a LinFu IoC container with your ASP.NET MVC application

From the many available IoC containers out there, LinFu is the one that I like most. This is because it is extremely simple to use, needs almost no configuration at all, and yet it is highly flexible and extensible, if you need to do some more complicated things (you can read two good introductory articles about LinFu IoC here and here). During the last weeks, I was doing some stuff with the ASP.NET MVC framework, and I wondered how easy or complicated it would be to use the application's IoC container throughout the entire MVC stack, thus getting a bit closer to the realm of Interface-based programming and diving a bit deeper into the wonderful world of Dependency Injection - e.g. for model or controller creation. As it turned out, this was not very hard to do. Here's how.

Setting the Stage (the "domain")

Let's say you have the following 'domain model' (which is shamelessly copied from the LinFu examples and then slightly modified...):

DomainModel

Consequently, when using a LinFu ServiceContainer, you will have code similar to this to create a car along with its related engine and driver:

C#
[Test]
public void CanCreateCarWithDependenciesFromContainer()
{
    var car = container.GetService<IVehicle>();
 
    Assert.Multiple(() =>
    {
        Assert.IsNotNull(car);
        Assert.IsNotNull(car.Engine);
        Assert.IsNotNull(car.Driver);
    });
}

This works because the LinFu container sees that the ICar-implementing class has a constructor which takes an IEngine and an IPerson instance as arguments. So far, this is classical constructor-injection...

Using the IoC Container for Controller Creation

Now onto more MVC-specific things: Let's create a custom ControllerFactory class to do controller creation by means of our LinFu container. - In an ASP.NET MCV application, the controller factory is (you already might have suspected that ;-)) responsible for creating controller instances in reaction to URL requests. - MVC's default factory requires a parameterless constructor to be declared on the requested controller class. With our LinFu container, we don't have that limitation, but we can use Dependency injection instead. Here's the declaration of our new factory class:

C#
public class LinFuControllerFactory : DefaultControllerFactory
{
    protected ServiceContainer Container { get; private set; }
 
    public LinFuControllerFactory(ServiceContainer serviceContainer)
    {
        if (serviceContainer == null)
        {
            throw new ArgumentNullException("serviceContainer");
        }
        this.Container = serviceContainer;
    }
 
    protected override IController GetControllerInstance(Type controllerType)
    {
        if (controllerType == null)
        {
            throw new HttpException(404, string.Format(
                "The controller for path '{0}' 
		could not be found or it does not implement IController.",
                RequestContext.HttpContext.Request.Path));
        }
 
        return (IController)controllerType.AutoCreateFrom(this.Container);
    }
 
} // class LinFuControllerFactory

As you can see, creating a controller of the desired type through a LinFu container is essentially a one-liner with the AutoCreateFrom() method. If the controller has a non-default c'tor, then the container will also take care of the controller's dependencies. There is no additional configuration or whatever necessary - it just works, if you have announced the required assembly (containing the controller) to the LinFu container (see below)...

Using the IoC Container for Model Binding

Controller creation is the first area where using an IoC container can be useful, the second one is model binding. What I especially like about it, is that this technique allows for using interfaces instead of concrete classes both in views and controllers.

In the view's .aspx, you can write:

ASP.NET
<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master"
         Inherits="System.Web.Mvc.ViewPage<My.Model.IVehicle>" %>
    ...

...and a related controller action might be declared like this:

C#
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(IVehicle car)
{
    ...

To make this happen, we use a custom model binder that converts the form's values to an ICar instance:

C#
public class VehicleModelBinder : TypedLinFuModelBinder<IVehicle>
{
    public VehicleModelBinder(ServiceContainer serviceContainer) : base(serviceContainer)
    {
    }
 
    protected internal override IVehicle CreateModelFromFormValues
		(NameValueCollection formValues)
    {
        var engine = this.GetService<IEngine>(formValues["Engine.SerialNumber"]);
        var driver = this.GetService<IPerson>(formValues["Driver.Name"],
                                              Convert.ToInt32(formValues["Driver.Age"]));
 
        return this.GetService<IVehicle>(engine, driver);
    }
 
} // class VehicleModelBinder

The generic TypedLinFuModelBinder base class is initialized with a service container instance and declares some convenience methods, particularly the CreateModelFromFormValues() method, which serves to handle the common "take the form's values and create a new object from them" scenario. It is included in the sample solution, which you can download from here. (The internal declaration serves to make testing easier...)

Putting It All Together

First, it's helpful to place the LinFu ServiceContainer behind a singleton facade, so that we have a unique access point to it throughout the entire application. I usually have an extra assembly for such things (IoC, Logging, custom attributes, etc.; named Infrastructure or something similar). The singleton to hold the ServiceContainer instance might look something like this:

C#
public static class DI
{
    public static ServiceContainer ServiceContainer { get; private set; }
 
    static DI() // static c'tor for doing the necessary initializations 
    {
        // this works for web apps, for desktop apps it will be null...
        string directory =
            AppDomain.CurrentDomain.SetupInformation.PrivateBinPath;
        if (string.IsNullOrEmpty(directory))
        {
            // this is for desktop apps (or tests)...
            directory = AppDomain.CurrentDomain.BaseDirectory;
        }
 
        ServiceContainer = new ServiceContainer();
        ServiceContainer.LoadFrom(directory, "My.DomainAssembly.dll");
        ServiceContainer.LoadFrom(directory, "My.AspNetMvcAssembly.dll");
    }
 
} // class DI

The last thing that's left to do is to register both our LinFuControllerFactory and our custom model binder with the application on startup. This is done in the Application_Start() event handler in Global.asax.cs, where these two lines must be added:

C#
ControllerBuilder.Current.SetControllerFactory
	(new LinFuControllerFactory(DI.ServiceContainer));
ModelBinders.Binders[typeof(IVehicle)] = new VehicleModelBinder(DI.ServiceContainer);

The Sample Solution

A sample solution (VS 2008) is available here. It contains the LinFuControllerFactory shown here and the LinFu-specific model binder base classes, which are used in the above example.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) Freelancer
Germany Germany
I'm a freelance software developer/architect located in Southern Germany with 20 years of experience in C++, VB, and C# software projects. Since about ten years I do all my coding exclusively in C# for business application development - and nowadays Python for Data Science, ML, and AI.
I am especially dedicated to Test-driven development, OO architecture, software quality assurance issues and tools.

Comments and Discussions

 
-- There are no messages in this forum --