Creating a Simple IoC Container

Inversion of Control (IoC) is a software design principle that describes inverting the flow of control in a system, so execution flow is not controlled by a central piece of code. This means that components should only depend on abstractions of other components and are not be responsible for handling the creation of dependent objects. Instead, object instances are supplied at runtime by an IoC container through Dependency Injection (DI).

IoC enables better software design that facilitates reuse, loose coupling, and easy testing of software components.

At first IoC might seem complicated, but it’s actually a very simple concept. An IoC container is essentially a registry of abstract types and their concrete implementations. You request an abstract type from the container and it gives you back an instance of the concrete type. It’s a bit like an object factory, but the real benefits come when you use an IoC container in conjunction with dependency injection.

An IoC container is useful on all types of projects, both large and small. It’s true that large, complex applications benefit most from reduced coupling, but I think it’s still a good practice to adopt, even on a small project. Most small applications don’t stay small for long. As Jimmy Bogard recently stated on Twitter: “the threshold to where an IoC tool starts to show its value is usually around the 2nd hour in the life of a project”.

There are many existing containers to choose from. These have subtle differences, but all aim to achieve the same goal, so it’s really a matter of personal taste which one you choose. Some common containers in .NET are:

While I recommended using one of the containers already available, I am going to demonstrate how easy it is to implement your own basic container. This is primarily to show how simple the IoC container concept is. However, there might be times when you can’t use one of the existing containers, or don’t want all the features of a fully-fledged container. You can then create your own fit-for-purpose container.

Using Dependency Injection with IoC

Dependency Injection is a technique for passing dependencies into an object’s constructor. If the object has been loaded from the container, then its dependencies will be automatically supplied by the container. This allows you to consume a dependency without having to manually create an instance. This reduces coupling and gives you greater control over the lifetime of object instances.

Dependency injection makes it easy to test your objects by allowing you to pass in mocked instances of dependencies. This allows you to focus on testing the behaviour of the object itself, without depending on the implementation of external components or services.

It is good practice to reduce the number of direct calls to the container by only resolving top-level objects. The rest of object-graph will be resolved through dependency injection. This also prevents IoC-specific code from becoming scattered throughout the code base, making it easy switch to a different container if required.

Implementing a Simple IoC Container

To demonstrate the basic concepts behind IoC containers, I have created a simple implementation of an IoC container.

Download the source and sample code here

This implementation is loosely based on RapidIoc, created by Sean McAlindin. It does not have all the features of a full IoC container, however, it should be enough to demonstrate the main benefits of using a container.

public class SimpleIocContainer : IContainer
{
    private readonly IList<RegisteredObject> registeredObjects = new List<RegisteredObject>();
 
    public void Register<TTypeToResolve, TConcrete>()
    {
        Register<TTypeToResolve, TConcrete>(LifeCycle.Singleton);
    }
 
    public void Register<TTypeToResolve, TConcrete>(LifeCycle lifeCycle)
    {
        registeredObjects.Add(new RegisteredObject(typeof (TTypeToResolve), typeof (TConcrete), lifeCycle));
    }
 
    public TTypeToResolve Resolve<TTypeToResolve>()
    {
        return (TTypeToResolve) ResolveObject(typeof (TTypeToResolve));
    }
 
    public object Resolve(Type typeToResolve)
    {
        return ResolveObject(typeToResolve);
    }
 
    private object ResolveObject(Type typeToResolve)
    {
        var registeredObject = registeredObjects.FirstOrDefault(o => o.TypeToResolve == typeToResolve);
        if (registeredObject == null)
        {
            throw new TypeNotRegisteredException(string.Format(
                "The type {0} has not been registered", typeToResolve.Name));
        }
        return GetInstance(registeredObject);
    }
 
    private object GetInstance(RegisteredObject registeredObject)
    {
        if (registeredObject.Instance == null || 
            registeredObject.LifeCycle == LifeCycle.Transient)
        {
            var parameters = ResolveConstructorParameters(registeredObject);
            registeredObject.CreateInstance(parameters.ToArray());
        }
        return registeredObject.Instance;
    }
 
    private IEnumerable<object> ResolveConstructorParameters(RegisteredObject registeredObject)
    {
        var constructorInfo = registeredObject.ConcreteType.GetConstructors().First();
        foreach (var parameter in constructorInfo.GetParameters())
        {
            yield return ResolveObject(parameter.ParameterType);
        }
    }
}

The SimpleIocContainer class has two public operations: Register and Resolve.

Register is used to register a type with a corresponding concrete implementation. When type is registered, it is added to a list of registered objects.

Resolve is used to get an instance of a type from the container. Depending on the Lifecycle mode, a new instance is created each time the type is resolved (Transient), or only on the first request with the same instance passed back on subsequent requests (Singleton).

Before a type is instantiated, the container resolves the constructor parameters to ensure the object receives its dependencies. This is a recursive operation that ensures the entire object graph is instantiated.

If a type being resolved has not been registered, the container will throw a TypeNotRegisteredException.

Using the Simple IoC Container with ASP.NET MVC

Now I am going to demonstrate using the container by creating a basic order processing application using ASP.NET MVC.

First we create a custom controller factory called SimpleIocControllerFactory that derives from DefaultControllerFactory. Whenever a page is requested, ASP.NET calls GetControllerInstance to get an instance of the page controller. We can then pass back an instance of the controller resolved from our container.

public class SimpleIocControllerFactory : DefaultControllerFactory
{
    private readonly IContainer container;
 
    public SimpleIocControllerFactory(IContainer container)
    {
        this.container = container;
    }
 
    protected override IController GetControllerInstance(Type controllerType)
    {
        return container.Resolve(controllerType) as Controller;
    }
}

We now need to set SimpleIocControllerFactory as the current controller factory in the global Application_Start handler.

public class MvcApplication : System.Web.HttpApplication
{
    protected void Application_Start()
    {
        var container = new SimpleIocContainer();
 
        BootStrapper.Configure(container);
 
        ControllerBuilder.Current.SetControllerFactory(new SimpleIocControllerFactory(container));
    }
}

In order for the SimpleIocControllerFactory to resolve an instance of the OrderController, we need to register the OrderController with the container.

Here I have created a static Bootstrapper class for registering types with the container.

public static class BootStrapper
{
    public static void Configure(IContainer container)
    {
        container.Register<OrderController, OrderController>();
    }
}

The controllers do not contain state, therefore we can use the default singleton lifecycle to create an instance of the controller only once per request.

When we run the application, the OrderController should be resolved and the page will load.

It is worth noting that the controller factory should be the only place we need to explicitly resolve a type from the container. The controllers are top-level objects and all our other objects stem from these. Dependency Injection is used to resolve dependencies down the chain.

To place an order we need to make a call to OrderService from the controller. We inject a dependency to the order service by passing the IOrderService interface into the OrderController constructor.

public class OrderController : Controller
{
    private readonly IOrderService orderService;
 
    public OrderController(IOrderService orderService)
    {
        this.orderService = orderService;
    }
 
    public ActionResult Create(int productId)
    {
        int orderId = orderService.Create(new Order(productId));
 
        ViewData["OrderId"] = orderId;
 
        return View();
    }
}

When we build and run the application we should get an error: “The type IOrderService has not been registered”. This means the container has tried to resolve the dependency, but the type has not been registered with the container. So we need to register IOrderService and its concrete implementation, OrderService, with the container.

public static class BootStrapper
{
    public static void Configure(IContainer container)
    {
        container.Register<OrderController, OrderController>();
        container.Register<IOrderService, OrderService>();
    }
}

The OrderService in turn has a dependency on IOrderRepository which is responsible for inserting the order into a database.

public class OrderService : IOrderService
{
    private readonly IOrderRepository orderRepository;
 
    public OrderService(IOrderRepository orderRepository)
    {
        this.orderRepository = orderRepository;
    }
 
    public int Create(Order order)
    {
        int orderId = orderRepository.Insert(order);
        return orderId;
    }
}

As the OrderService was resolved from the container, we simply need to register an implementation for IOrderRepository for OrderService to receive its dependency.

public static class BootStrapper
{
    public static void Configure(IContainer container)
    {
        container.Register<OrderController, OrderController>();
        container.Register<IOrderService, OrderService>();
        container.Register<IOrderRepository, OrderRepository>();
    }
}

Any further types are that required simply need to be registered with the container then passed as an argument on the constructor.

Most full-featured IoC containers support some form of auto-registration. This saves you from having do to a lot of one-to-one manual component mappings.

I hope I have demonstrated that IoC containers are not magic. They are in fact a simple concept that, when used correctly, can help to create flexible, loosely-coupled applications.

About these ads

18 Responses to “Creating a Simple IoC Container”


  1. 1 Razvan January 30, 2010 at 10:18 am

    Great article. Simple and well explained. Thank you

  2. 2 timross January 30, 2010 at 11:16 pm

    Hi Razvan, thanks for your comment. Pleased to hear you liked the article! :-)

  3. 3 chutzpah10 August 11, 2010 at 1:54 am

    Good Article but I think would be a hard for a layman to understand who does not know about generics and lambda in C#
    So, i think you should also include a comment on that the top suggesting readers to rev up their skills in generics to be able to grasp what is going on
    Thanks

  4. 4 Anwar Javed October 12, 2010 at 2:47 pm

    We developed a open source Light Weight Ioc / Dependency Injection framework, Can you have a look?

    http://blog.isilverlabs.com/2010/08/lightweight-ioc-dependency-injection-framework/

  5. 5 Pavel April 17, 2011 at 9:28 pm

    Hello,
    You have created not an IoC Container, you have implemented a Service Locator pattern. IoC container is more than this. The main difference is that IoC container can auto wire up dependencies, automatically taking them from its registry (so you don’t call Resolve manually); and with Service Locator you take dependencies yourself, manually calling the Resolve method.

  6. 6 timross April 17, 2011 at 9:56 pm

    Hi Pavel, most IoC containers require you to manually resolve the top-level object (e.g. controller factory). All other objects down the chain automatically receive their registered dependencies through constructor/property injection.

  7. 7 Prahlad May 5, 2011 at 10:44 am

    Nice article.Clear and concise explanation.
    Also I found this http://www.objectmentor.com/resources/articles/dip.pdf as a good explanation for Dependency Inversion.

  8. 9 Matilda September 12, 2012 at 12:56 am

    Many thanks for the article. I’ve been reading lots of articles to understand the content of IoC Container & DI but the more I read the more confused I got. This article is the only clear & simple one that I could find after all.
    Many many thanks.

  9. 10 Alexander September 30, 2012 at 3:14 pm

    Lot of Thanks. Even for me it was been undersdand.


  1. 1 The Morning Brew - Chris Alcock » The Morning Brew #524 Trackback on January 25, 2010 at 8:41 am
  2. 2 Tweets that mention Creating a Simple IoC Container « Tim Ross – .NET Developer -- Topsy.com Trackback on January 26, 2010 at 5:32 pm
  3. 3 Excellent introduction to building a dependency injection container - Sean McAlinden's Blog Trackback on January 26, 2010 at 10:41 pm
  4. 4 Excellent introduction to building a dependency injection container | I love .NET! Trackback on January 27, 2010 at 12:17 am
  5. 5 Blue Ray Plus - Latest Technology News Trackback on February 8, 2012 at 10:12 am
  6. 6 Inversion of Control | Pearltrees Trackback on March 20, 2012 at 4:19 pm
  7. 7 Building a Simple IoC Container in C# Trackback on July 30, 2012 at 12:29 pm
  8. 8 Understanding IOC - Gareth Reid Trackback on January 24, 2014 at 1:39 pm

Leave a Reply

Please log in using one of these methods to post your comment:

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





Follow

Get every new post delivered to your Inbox.

%d bloggers like this: