Click here to Skip to main content
Click here to Skip to main content
Technical Blog

Tagged as

A simple container used in MVC web applications with DI (Dependency Injection)

, 15 Oct 2012 CPOL
Rate this:
Please Sign up or sign in to vote.
A simple container used in MVC web applications with DI.

If you have a good understanding of IoC (Inversion of Control), DI(Dependency Injection) and Multi-layer architecture then continue reading this article otherwise have a look at this article – An idea of a three tier application using IoC (Inversion of Control), DI (Dependency Injection), and perfectly decoupled layers

Container

A ‘Container’ as the name implies – will contain some objects for further use in your project. These  stored objects are mainly the Business Layer Objects (BLL) and the Data Access Layer Objects (DAL).  We store them and then retrieve them as needed in the Application.

Sometimes a BLL object may depend on a DAL object. The Container will load (instantiate) them separately. Then the problem is: how will we inform the Container about their inter dependency. Here comes the role of Dependency Injection. The Container will use DI (Dependency Injection) to inject the dependent-object  in to the depende-object mostly through the constructor. I will not go in detail of DI as there are lots of article about it the web.

I will stick with the Container. Container mainly implements IoC(Inversion of control): another software engineering terminology which I am not going to discus here but you can find a clear concept from my post here. In short with IoC we are actually controlling the loading of all the objects in the Container. Basically this is the concept behind all the Containers like UnityCastle Windsor or StructureMap.

Dependency Injection and use of IoC container is getting popular day by day. But have we been ever curious about this – why are they useful? This is all about improving the performance and maintainability of code. Using IoC Container you can have these two benefits

  1. Your code is easy to maintain
    • Your code is easy to maintain because all your BLL and DAL objects are loaded is a centralized place called Composition-Root. So if you want to replace a particular object –  just do it in Composition-Root.
  2. Gives you extra performance
    • In the memory allocation perspective – every new heap allocation takes a significant time and is subject to a task of garbage collector. Hence uncontrolled object loading and releasing affects the performance. But a IoC container will help us to minimize the scattered object instantiation and thus the performance will improve.

In this article I am going to implement a very basic Container in a three tier Web-application. The design is as bellow

Fig: A three tier application architecture

The detail of this design has been described in the post – An idea of a three tier application using IoC(Inversion of Control), DI(Dependency Injection) and perfectly decoupled layers.

I am not going to describe this design here, rather I am going to modify the design and introduce a Container in our Composition-Root (i.e., CRoot) layer. We will call our container object – ControlsContainer. Possibly it will be a single-tone. There will be another object Root that will use this ControlsContainer object.

A three tier application with Container

This ControlsContainer will maintain a dictionary of objects where all the BLL and DAL layer objects will be registered and indexed. These objects will be fetched as needed throughout the project.

Let's start with a very preliminary skeleton of our ControlsController class.

public sealed class ControlsContainer
{
    // variable for singletone instance
            private static readonly ControlsContainer _cc = new ControlsContainer();
    // To return a singletone instance
    public static ControlsContainer CContainerInstance
    {
        get {
            return _cc;
        }
    }

    // This is the delegate that we will
    // be used to create objects
    // (don't mind the reason of the input parameter for now)
    // This perameter will be used to Inject Dependency as a perameter.
    public delegate object CreateInstance(ControlsContainer container);

    // Our dictionary that will hold the couples:
    // (type, function that creates "type")
    Dictionary Factories;

    // Default Private Constructor - To make Singletone
    private ControlsContainer()
    {
        // Create an Empty dictionary
        Factories = new Dictionary();
    }

    // Other codes coming...
}

The code is self descriptive with the comments. Firstly we are creating the object as singleton – so that we don’t have to instantiate it over and over again. Then we put a delegate to hold the pointer of a function that will create a new object (i.e., BLL and DAL object). Then we create a dictionary ‘Factories’ that will store all the objects reference. In the constructor the dictionary has been instantiated. This dictionary will be indexed with the Type of the objects and these objects will be created with the help of the delegate ‘CreateInstance‘. For more understanding on delegate you can read this article ‘Ways of Delegation in .NET (C# and VB)‘.

Now is the time to create a function RegisterInterface in the ControlsContainer object that will register an object by adding the type of the object and the delegate that will create that object in the dictionary. But certainly it will not instantiate the object.

// Add or Register a Delegate and a Return type in the Dictionary
public void RegisterInterface(CreateInstance ci)
{
    if (ci == null)
        throw new ArgumentNullException("ci");

    if (Factories.ContainsKey(typeof(T)))
        throw new ArgumentException("Type already registered");
    // Adding the Type and delegate-function(function pointer)
    Factories.Add(typeof(T), ci);
}

We need another function Resolve that will call the appropriate delegate function according to the Type, instantiate the object and return the instance.

// Drag an Item from the dictioary, call the Delecate function 
//and return the object to the client.
public T Resolve()
{
    if (!Factories.ContainsKey(typeof(T)))
        throw new ArgumentException("Type not registered");

    // retrieve the function that creates
    // the object from the dictionary
    CreateInstance creator = (CreateInstance)Factories[typeof(T)];

    // call it!
    return (T)creator(this);            
}

// We provide an overload that doesn't use generics, to be more
// flexible when the client doesn't know the type he wants to
// retrieve at compile time.
public object Resolve(Type type)
{
    if (type == null)
        throw new ArgumentNullException("type");

    if (!Factories.ContainsKey(type))
        throw new ArgumentException("Type not registered");

    CreateInstance creator = (CreateInstance)Factories[type];
    return creator(this);            
}

We have some utility function that will check the dictionary for a Type.

// Utility function that checks for alrady registered Types 
public bool IsInterfaceRegistered()
{
    return Factories.ContainsKey(typeof(T));
}

// Utility function that checks for alrady registered Types
public bool IsInterfaceRegistered(Type type)
{
    if (type == null)
        throw new ArgumentNullException("type");

    return Factories.ContainsKey(type);
}

Finally our ControlsContainer class will look like this

public sealed class ControlsContainer
{

    private static readonly ControlsContainer _cc = new ControlsContainer();
    // To return a singletone instance
    public static ControlsContainer CContainerInstance
    {
        get {
            return _cc;
        }
    }

    // This is the delegate that we will
    // be used to create objects
    // (don't mind the reason of the input parameter for now)
    // This perameter will be used to Inject Dependency as a perameter.
    public delegate object CreateInstance(ControlsContainer container);

    // Our dictionary that will hold the couples:
    // (type, function that creates "type")
    Dictionary Factories;

    // Default Private Constructor - To make Singletone
    private ControlsContainer()
    {
        // Create an Empty dictionary
        Factories = new Dictionary();
    }

    // Add or Register a Delegate and a Return type in the Dictionary
    public void RegisterInterface(CreateInstance ci)
    {
        if (ci == null)
            throw new ArgumentNullException("ci");

        if (Factories.ContainsKey(typeof(T)))
            throw new ArgumentException("Type already registered");

        Factories.Add(typeof(T), ci);
    }

    // Drag an Item from the dictioary, call the Delecate function 
    //and return the object to the client.
    public T Resolve()
    {
        if (!Factories.ContainsKey(typeof(T)))
            throw new ArgumentException("Type not registered");

        // retrieve the function that creates
        // the object from the dictionary
        CreateInstance creator = (CreateInstance)Factories[typeof(T)];

        // call it!
        return (T)creator(this);            
    }

    // We provide an overload that doesn't use generics, to be more
    // flexible when the client doesn't know the type he wants to
    // retrieve at compile time.
    public object Resolve(Type type)
    {
        if (type == null)
            throw new ArgumentNullException("type");

        if (!Factories.ContainsKey(type))
            throw new ArgumentException("Type not registered");

        CreateInstance creator = (CreateInstance)Factories[type];
        return creator(this);            
    }

    // Utility function that checks for alrady registered Types 
    public bool IsInterfaceRegistered()
    {
        return Factories.ContainsKey(typeof(T));
    }

    // Utility function that checks for alrady registered Types
    public bool IsInterfaceRegistered(Type type)
    {
        if (type == null)
            throw new ArgumentNullException("type");

        return Factories.ContainsKey(type);
    }
}

Root

Now let's look at the Root class that will be using the ControlsContainer object. The Root object will basically instantiate the singleton of the ControlsContainer and then will register all the BLL and DAL objects in the dictionary.

public class Root
{
    // Container property
    public ControlsContainer MyContainer { get; set; }

    public Root()
    {           

        // Declare the Container
        ControlsContainer _container = ControlsContainer.CContainerInstance;
        // Set the container property
        this.MyContainer = _container;

        // Register SqlProductRepository(DAL layer)
        _container.RegisterInterface( ( ControlsContainer _c ) => new SqlProductRepository());
        // Register ProductService(BLL layer) with DI of the DAL (ISqlProductRepository)
        _container.RegisterInterface((ControlsContainer _c ) => new ProductService(_c.Resolve()));

    }
}

Notice that the BLL and DAL objects are registered with the Interface Types – not the original object Types. This actually decouples the view layer from the BLL and DAL totally. If any object needs a change in the BLL and DAL layer then it doesn’t reflect any change in the View layer as long as the BLL and DAL implements the interfaces. Also notice at the last line of the class where we are registering the IProductService in the container – we are also passing the dependency of ISqlProductRepository in the constructor of the ProductService class. Thus we are using dependency injection to decouple the BLL and DAL layer.

View Layer

Now it is time to look at our view. we are implementing  a MVC  web-application in .NET as our view. In our view layer we will just have to use the Root object and the Interfaces.

At the starting point of our web application (Application_Start event at global.asax.ce file) we will have to create our custom controller factory and pass the root object in it. If you are not familiar with custom controller factory hit here – its very simple.  Our custom controller factory - MyControllarFactory object will get the container – ControlsContainer object from the property of the Root object. Then at every request invoke the our controller factory - MyControllarFactory object will do the following jobs:

  1. Create appropriate controller instance.
  2. Query the container to get the BLL object (i.e., ProductSerice) for that controller.
  3. Pass this BLL object to the constructor of the controller as dependency injection.

Note that this BLL object will be referenced by an interface (i.e., IProdctService).

Below is the MyControllarFactory class:

public class MyControllarFactory : DefaultControllerFactory
{
    // Container that will be used through out this application
    private ControlsContainer _container { get; set; }

    private MyControllarFactory()
    {
    }
    // Constructor
    public MyControllarFactory(Root _root)
    {
        if (_root == null)
            throw new ArgumentNullException("root");

        _container = _root.MyContainer;
    }

    // The function that will be called at every Controller instance creation
    protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
    {
        if (controllerType == null)
            return null;
            //throw new System.Web.HttpException(404, 
            //  "Page not found: " + requestContext.HttpContext.Request.Path);

        if (!typeof(IController).IsAssignableFrom(controllerType))
            throw new System.ArgumentException("Type does not subclass IController", "controllerType");

        object[] parameters = null;

        ConstructorInfo constructor = controllerType.GetConstructors().FirstOrDefault(c => c.GetParameters().Length > 0);
        if (constructor != null)
        {
            ParameterInfo[] parametersInfo = constructor.GetParameters();
            parameters = new object[parametersInfo.Length];

            for (int i = 0; i < parametersInfo.Length; i++)
            {
                ParameterInfo p = parametersInfo[i];

                if (!_container.IsInterfaceRegistered(p.ParameterType))
                    throw new ApplicationException("Can't instanciate controller '" + 
                       controllerType.Name + "', one of its parameters is unknown to the IoC Container");
                // Assign appropriate objects from container to the controllers constructor parameter
                parameters[i] = _container.Resolve(p.ParameterType);
            }
        }

        try
        {
            // Create the controller instance and return
            return (IController)Activator.CreateInstance(controllerType, parameters);
        }
        catch (Exception ex)
        {
            throw new InvalidOperationException(String.Format(CultureInfo.CurrentUICulture, 
              "error creating controller", controllerType), ex);
        }
    }
}

Then the controller will be able to use that service instance to query the data from DB.

public class HomeController : Controller
{
    IProductService _ps;
    public HomeController(IProductService prSrv)
    {
        _ps = prSrv;
    }

    public ActionResult Index()
    {

        ViewBag.Message = "Total Product: " + _ps.GetSqlProductList().Count().ToString();

        return View();
    }

    public ActionResult About()
    {
        return View();
    }
}

Notice the HomeController uses the interface to call the service layer (BLL) functionality.

Up to now we have successfully implemented a container – ControlsContainer that will register all our back-end objects and will draw them as par requirements from the view layer.

Here I have used a very simple container to clear up the understanding of how container helps us in our application, so that  in practical projects we can use some more feature rich containers like UnityCastle Windsor or StructureMap.

Please comment <img src= " src="http://s0.wp.com/wp-includes/images/smilies/icon_smile.gif" />

Reference

License

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

Share

About the Author

RizviHasan
Software Developer
Sweden Sweden
No Biography provided

Comments and Discussions

 
QuestionWhy singleton? PinmemberJohn Brett14-Oct-12 23:16 
AnswerRe: Why singleton? PinmemberRizviHasan15-Oct-12 2:38 
GeneralRe: Why singleton? PinmemberJohn Brett15-Oct-12 3:02 
QuestionI keep getting distracted by that hore <sic> PinmemberBigTimber@home12-Oct-12 13:44 
AnswerRe: I keep getting distracted by that hore PinmemberRizviHasan15-Oct-12 2:44 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web04 | 2.8.141015.1 | Last Updated 15 Oct 2012
Article Copyright 2012 by RizviHasan
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid