Click here to Skip to main content
16,017,881 members
Articles / Programming Languages / C#
Technical Blog

Using Unity for IoC and DI

Rate me:
Please Sign up or sign in to vote.
4.76/5 (14 votes)
4 Nov 2012CPOL5 min read 83.3K   31   4
In this article I will try to describe all about using unity in our .NET projects and thus how to implement IoC (inversion of control) and DI(Dependency Injection).

In this article I will try to describe all about using unity in our .NET projects and thus how to implement IoC (inversion of control) and DI (Dependency Injection).

Description of the Objects

Going straight to a class library project (.NET) which will we follow through out this article. In the project we have a Book class which is considered as our model class, a BookService class which will be considered as business layer class and a BookRepository class which will be considered as a data access layer class. Each of this class has their interface as IBook, IBookService, and IBookRepository.

Fig:1 Objects in the container

Here in this project we have all objects those will be contained in a container (i.e. Unity in our case). There are lots of containers out there in the web but we have chosen Unity because it is a moderate container in all respect. Find more about other containers here.

Next we create another .net mvc3 website project as our view layer. We have to reference our class-library project from this project because we are going to create our container in this mvc3 website project. Well there are many ways to install the Unity container but I  would prefer to install it from Package Manager Console. In the Package Manager Console just type PM> Install-Package Unity. It will install unity in your project and will automatically add reference for Microsoft.Practices.Unity , Microsoft.Practices.Unity.Configuration and other necessary packages.  Up to here you are ready with Unity and now you will have to use the Unity container.

Unity container can be configured in two ways - run-time configuration and design-time configuration.

Run-time configuration

Run-time configuration is easy, just put the following code in the Application_Start() function of the web application’s global.asax.cs file.

C#
// Container initialization by code
MvcApplication._myContainer = new UnityContainer();
MvcApplication._myContainer.RegisterType();
MvcApplication._myContainer.RegisterType();
MvcApplication._myContainer.RegisterType();

After instantiating the UnityContainer you just need to register all the model objects (Book with IBook), Business layer objects (BookService with IBookService) and repository objects (BookRepository with IBookRepository). After registration you can use them by calling the resolve function anywhere in your application like bellow.

IBook _b = _cc.Resolve();

There are some certain advanced functions for DI with constructor, property of interface. You can get more about here.

Design-time-configuration

Here in this article we will more concentrate with the design-time-configuration because design-time-configuration has certain advantages over run-time-configuration. For example you can change the DAL layer of SQL server to Oracle, just by changing a bit in the configuration file and without recompiling a single line of code.

To configure unity at design time – put the following code in the <configuration> element of the web.config file.

<configSections>
<section name="unity"
type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection,
Microsoft.Practices.Unity.Configuration"/>
</configSections>
<unity configSource="unity.config"/>

With this configuration we actually relocate our main container configuration in a separate file unity.config. Now lets see how we have configured all the objects of Model, BAL and DAL layers in this file.

<?xml version="1.0" encoding="utf-8"?>
<unity xmlns="http://schemas.microsoft.com/practices/2010/unity">
  <typeAliases>
    <!-- Models-->
    <typeAlias alias="IBook" type="BusinessBackend.IBook, BusinessBackend" />
    <typeAlias alias="Book" type="BusinessBackend.Book, BusinessBackend" />
    <!-- Services -->
    <typeAlias alias="IBookService" type="BusinessBackend.IBookService, BusinessBackend" />
    <typeAlias alias="BookService" type="BusinessBackend.BookService, BusinessBackend" />
    <!-- Repositories -->
    <typeAlias alias="IBookRepository" type="BusinessBackend.IBookRepository, BusinessBackend" />
    <typeAlias alias="BookRepository" type="BusinessBackend.BookRepository, BusinessBackend" />
  </typeAliases>
  <container>
    <register type="IBook" mapTo="Book" />
    <register type="IBookRepository" mapTo="BookRepository" name="SQLrepo" />
    <register type="IBookService" mapTo="BookService" >
      <constructor>
        <param name="br" dependencyName="SQLrepo">
        <!--<param name="br" dependencyType="BookRepository">-->
        <!--<dependency type="BookRepository" />-->
        <!--<dependency name="SQLrepo" />-->
        </param>
      </constructor>
    </register>
  </container>
</unity>

If you look in detail of the unity.config file you will be able to see that; in <typeAliases> section I just have given a short hand name of a particular class.

<typeAlias alias="[short hand name]" type="[namespace].[class], [assembly name]" />

Then in the <container> section I have registered all the objects with its interface. Optionally you can give a name of a registration to use it further in the configuration file. Like I have registered BookRepository with IBookRepository and named it “SQLrepo”.

<register type="[interface]" mapTo="[class]" name="[name of the registration]" />

Notice our BookService class which have a parameterized constructor which takes an object of type IBookRepository.

C#
public class BookService : IBookService
{
    IBookRepository BookRepo;
    public BookService(IBookRepository br)
    {
        BookRepo = br;
    }

    public IBook getBookById(int id)
    {
        return BookRepo.getBookById(id);
    }
}

This is how we inject dependency on BookRepository object of the BookService object. This means while creating a BookService object you will have to provide a BookRepository object referenced by IBookRepository. Unity will do this for us, if we properly configure the constructor of BookService with the proper dependency. To configure the constructor add a <constructor> element in the  registration of BookService. Then set the parameters with <param> element.

You can set the dependencyName attribute of the <param> element to any named registration.

XML
<param name="[name of the param]" dependencyName="[name of a registration]">

Or set the dependencyType attribute of the <param> element to the object type upon which the constructor is depending on.

XML
<param name="[name of the param]" dependencyType="[type of the param]">

Or you can put a <dependency> in the <param> element and set the name attribute or type attribute.

XML
<dependency type="[type of the param]" />
<dependency name="[name of a registration]" />

Up to now we are done with the configuration. Now we need to create a unity container in the code from this configuration. We will create a container in the Application_Start() by the following code.

// Container initialization by web.config and unity.config
var section = (UnityConfigurationSection)ConfigurationManager.GetSection("unity");
IUnityContainer container = new UnityContainer().LoadConfiguration(section);

Using the container

The best place to use this container is in a Controller-Factory of a MVC3 web application. Here is my Controller-Factory class MyControllerFactory which is derived from DefaultControllerFactory.

C#
public class MyControllerFactory: DefaultControllerFactory
{
    IUnityContainer _container;
    public MyControllerFactory(IUnityContainer c)
    {
        _container = c;
    }

    protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
    {
        if (controllerType == 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.IsRegistered(p.ParameterType))
                    throw new ApplicationException("Can't instanciate controller '" + 
                       controllerType.Name + "', one of its parameters is unknown to the IoC Container");

                parameters[i] = _container.Resolve(p.ParameterType);
            }
        }

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

Observe the constructor MyControllerFactory(IUnityContainer c) is taking the container and preserves it to a local variable. Then, while creation of a new controller this preserved container is used to spawn the appropriate service-level-object (BAL object) to the controller through parameterized-constructor.

So each controller takes a service-level-object (BAL object) as constructor parameter. For example see the HomeController takes IBookService as constructor parameter. Then this controller consumes this service-level-objects to get the book information.

C#
public class HomeController : Controller
{
    IBookService _bookSrv;
    public HomeController(IBookService bs)
    {
        _bookSrv = bs;
    }

    public ActionResult Index()
    {         
        IBookService _bks = _bookSrv;
        ViewBag.Message = _bks.getBookById(2).BookName;

        return View();
    }     
}

Extending the container with another Repository

At this point we want to insert another repository class which will handle Oracle database for example. So we add another class OracleBookRepository implementing the same interface IBookRepository in our class library project.

Then we will have to register this new class in our unity.config also.

XML
<?xml version="1.0" encoding="utf-8"?>
<unity xmlns="http://schemas.microsoft.com/practices/2010/unity">
  <typeAliases>
    <!-- Models-->
    <typeAlias alias="IBook" type="BusinessBackend.IBook, BusinessBackend" />
    <typeAlias alias="Book" type="BusinessBackend.Book, BusinessBackend" />
    <!-- Services -->
    <typeAlias alias="IBookService" type="BusinessBackend.IBookService, BusinessBackend" />
    <typeAlias alias="BookService" type="BusinessBackend.BookService, BusinessBackend" />
    <!-- Repositories -->
    <typeAlias alias="IBookRepository" type="BusinessBackend.IBookRepository, BusinessBackend" />
    <typeAlias alias="BookRepository" type="BusinessBackend.BookRepository, BusinessBackend" />
    <typeAlias alias="OracleBookRepository" type="BusinessBackend.OracleBookRepository, BusinessBackend" />
  </typeAliases>
  <container>
    <register type="IBook" mapTo="Book" />
    <register type="IBookRepository" mapTo="BookRepository" name="SQLrepo" />
    <register type="IBookRepository" mapTo="OracleBookRepository" name="ORACLErepo" />
    <register type="IBookService" mapTo="BookService" >
      <constructor>
        <param name="br" dependencyName="ORACLErepo">
        <!--<param name="br" dependencyType="OracleBookRepository">-->
        <!--<dependency type="OracleBookRepository" />-->
        <!--<dependency name="ORACLErepo" />-->
        </param>
      </constructor>
    </register>
  </container>
</unity>

First we will have to make a type alias for the class OracleBookRepository. Then register the type of OracleBookRepository with IBookRepository. Next to use this new DAL object OracleBookRepository in our application we will have to edit the constructor of the BookService object so that it depends on OracleBookRepository instead of BookRepository. Observe the bold texts in the unity.config above. Now that the new object OracleBookRepository is added in our container.

The fun part with this is – if you want to go back to use BookRepository instead of OracleBookRepository just change back the constructor of BookRepository in the configuration file and no recompile is needed. This is the sweet part of decoupling with DI.

This is how you can create a Unity container and use it in the .NET MVC web application. Thus you also have implemented IoC when you have introduced Unity in your custom controller factory. You also have implemented DI while injecting BookRepository in the BookService class. Now there are less dependency between the container and the consumer and so you can easily test the controllers with custom service objects in a test project.

Comments are appreciated.

License

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


Written By
Software Developer
Sweden Sweden
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralMy vote of 1 Pin
anilchanna22-Apr-15 22:35
anilchanna22-Apr-15 22:35 
GeneralMy vote of 2 Pin
webbsk17-Feb-14 9:45
webbsk17-Feb-14 9:45 
QuestionDownload Source Code Pin
londhess29-Oct-13 20:05
londhess29-Oct-13 20:05 
Please provide the sample project source code.Thanks
Questioncode download please :) Pin
wcdeich422-Jul-13 5:24
wcdeich422-Jul-13 5:24 

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

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