Click here to Skip to main content
13,737,584 members
Click here to Skip to main content
Add your own
alternative version

Stats

15.9K views
504 downloads
33 bookmarked
Posted 13 Mar 2018
Licenced CPOL

Dependency Injection using Unity: Resolve dependency of dependencies

, 13 Mar 2018
Rate this:
Please Sign up or sign in to vote.
This article will not be very theoretical, but would be more of a tutorial to showcase the existing problems with tightly coupled applications, how to make the application loosely coupled and achieve inversion of control via dependency injection.

Contents

Introduction

Inversion of control (IOC) and Dependency Injection (DI) work hand in hand and make our application more loosely coupled and easy to expand. It is strongly recommended to follow SOLID principles when it comes to development. This article will not be very theoretical, but would be more of a tutorial to showcase the existing problems with tightly coupled applications, how to make the application loosely coupled and achieve inversion of control via dependency injection, solve the problem of resolving dependency of a dependency in a three-layered/n layered project architecture using unity framework.

Problem

While working on a three-layered architecture there could be situations where applications higher level modules/layers/classes are dependent on lower level modules. If we consider a basic simplistic example, an application can have a presentation layer, a business layer and a data access layer in a three-layered architecture. Presentation layer talks to business layer, business layer in turn talks to data access layer. To call a method of business layer in presentation layer, it is needed to create an object of business layer class in presentation layer and then call that needed method. To call the method of a class in data access layer, business layer class needs to create an object of class in data access layer and then invoke that method. Business layer communicates with data access layer because due to architectural constraints and flexibility, we do not want our presentation layer talking to data access layer as shown below.

Considering that the readers of this tutorial already know how to resolve dependency of modules, it is needed that presentation layer itself does not create an object of business layer but delegates this responsibility to any third-party container and when the instance is needed then that object is created for the presentation layer. For example, dependency injected and methods of business layers could be accesses via that. Same goes for communication between business layer to data access layer. This is where Unity comes into the picture. Unity acts as a container for this object creation and dependency resolution. Our problem is a bit different. Using unity at presentation layer, we can resolve the dependency of business layer, but what about the data access layer? How do we resolve dependency of data access layer in business layer? Do we need to again write a container in business layer? One way to achieve this is already explained in one of my previous articles where we can make use of MEF (Managed Extensibility Framework), but in this tutorial, we’ll even use a very simple way to do this. Let’s write some code, first to see the problem and then towards step by step solution.

Create Application Without DI

Since the focus area of this article is to explain the use of unity to resolve dependency of dependency in a simplistic way, we’ll not create a large application, but a small three-layered console application with only one class in each layer and one method in each class for the proof of concept. One can create MVC or any other application or can use this solution as a POC in any big application.

Step 1. Open Visual Studio. Create a console application and name it Presentation, add a class library and name it Business, add another class library and name that Data. So, we segregate our layers with these class libraries, where Business class library is for Business layer and Data class library is for Data access layer. The solution should look like something as follows. I have named the solution as "WithoutDI".

Remove the default classes named Class1.cs from Business and Data class libraries.

Step2. Now add a class named BusinessClass in Business project and DataClass in Data project. Add a method named GetData in the DataClass and return any string from that method. So, the implementation of the class looks like this:

namespace Data
{
  public class DataClass
  {
    public string GetData()
    {
      return "From Data";
    }
  }
}

Step3. Now our requirement is to get this data coming from the GetData method into our presentation layer and show on console. The straightforward way is that we call a method of Business class from presentation’s layer Program.cs that in turn will call this GetData method from DataClass. To achieve this, we first need to add the reference of Business project in the Presentation console application and reference of Data project in Business class library.

Step4. Now to access GetData method of Data project’s DataClass from BusinessClass, we first need the instance of DataClass in BusinessClass. We create the instance in BusinessClass constructor and then there should be a method in Business class that then makes use of this DataClass instance to call GetData method to return the string. So the implementation looks as follows.

using Data;
namespace Business
{
  public class BusinessClass
  {
    DataClass _dataClass;
    public BusinessClass()
    {
       _dataClass = new DataClass();
    }
    public string GetBusinessData()
    {
      return _dataClass.GetData();
    }
  }
}

Step5. Now to class this GetBusinessData method of Business class from presentation layer, we again need to create an instance of BusinessClass class in presentation layer and call the method. So create an instance of BusinessClass in main method or Program.cs and call the GetBusinessData via that instance as follows.

using System;

namespace Presentation
{
  class Program
  {
    static void Main(string[] args)
    {
      Business.BusinessClass businessClass = new Business.BusinessClass();
      string data = businessClass.GetBusinessData();
      Console.WriteLine(data);
      Console.ReadLine();
    }
  }
}

When we run the application, we get our desired output as shown below.

That means that our data comes from DataClass’s GetData() method. Our application runs fine, but the question is that is it following SOLID principles? Is my application loosely coupled and autonomous, is my application robust? If we look closely and try to answer these questions, the answer is NO.

Application violates Single Responsibility Principle where classes are also doing an additional job of creating objects of dependent classes.

Application violates the Open Close principle as, in case the constructor implementation changes in the classes, the calling class will have to modify its code to that change which may break the application or may not be feasible always.

Application violates Interface Segregation Principle indirectly as well as the testability of the application is very poor as we do not have interfaces defined for the classes that serves as contract.

Application violated Dependency Inversion principle as classes are dependent on other classes instances and are directly creating instances in the class. Imagine a situation where instance creation of DataClass fails, due to which Business class constructor will also fail to initialize and throw error. Which is not at all acceptable.

So, four out of five principles are violated. Moreover, the application is not testable w.r.t. unit tests. The application is tightly coupled. Let’s try to resolve these issues one by one with the help of Unity Framework and also proceed to see how we can resolve dependency of dependency.

Introduce Unity in the Application

Time to introduce Unity in the application. Unity Framework comes as a Nuget package, so we’ll install it via Package Manager Console. Open Tools in Visual Studio, navigate to Nuget Package Manager and open Package Manager Console as shown in the below image.

When the package manager console is opened, type command Install-Package Unity to get the latest version of Unity framework. Make sure the Presentation project is selected as the default project in the console. After typing command when we press enter, Unity package is installed for the presentation project. The latest version that I got is 5.7.3. it may vary and depends when you are implementing this based on the latest versions released.

There would be one packages.config file created in the application where this Unity package would be referenced and the package itself would be downloaded in the file system in the packages folder and the same would be referenced automatically in the Presentation project.

Dependency Injection via Unity

Let’s do some modification in Presentation layer and instead of calling BusinessClass method from Main method, let’s add a class named Initiator and call the method from there. We could have done this earlier, but we missed it, so let’s do it now. This is just to understand more clearly, how we resolve dependencies. So, add a class named Initiator and add following code.

using Business;
namespace Presentation
{
  public class Initiator
  {
    BusinessClass _businessClass;
    public Initiator()
    {
      _businessClass = new BusinessClass();
    }
    public string FetchData()
    {
      return _businessClass.GetBusinessData();
    }
  }
}

Code for Program.cs becomes as follows:

using System;

namespace Presentation
{
  class Program
  {
    static void Main(string[] args)
    {
      Initiator initiator = new Initiator();
      string data = initiator.FetchData();
      Console.WriteLine(data);
      Console.ReadLine();
    }
  }
}

The output and logic remains same, we just moved few pieces from Program.cs to Initiator class and in the Main method now use this Initiator class instance to fetch data.

So, the problem here now is, Program.cs Main() method creates instance of Initiator class, Initiator class creates instance of BusinessClass, and Business class creates instance of DataClass to fetch data. Let’s give this responsibility of instance creation to someone else, for example, Unity, and we inject the dependency in the defined constructors via Interfaces. Yes, we now need to create interfaces and define the respective methods. Let’s do it step by step.

Step1. Create interface named IData in Data project and define the GetData () method there as shown below.

namespace Data
{
  public interface IData
  {
    string GetData();
  }
}

Now, implement this interface in DataClass as shown below:

namespace Data
{
  public class DataClass : IData
  {
    public string GetData()
    {
      return "From Data";
    }
  }
}

Step2. Now come to Business project and define an interface named IBusiness and define the GetBusinessData() method there as shown below.

namespace Business
{
  public interface IBusiness
  {
    string GetBusinessData();
  }
}

Implement this interface in BusinessClass as follows:

using Data;
namespace Business
{
  public class BusinessClass
  {
    DataClass _dataClass;
    public BusinessClass()
    {
       _dataClass = new DataClass();
    }
    public string GetBusinessData()
    {
      return _dataClass.GetData();
    }
  }
}

Now, let’s assume that Business class will no more take this responsibility of object creation and this dependency would be injected in BusinessClass constructor on run time and we’ll have an object at runtime to call GetData method of DataClass. So, our Business class looks something like the following:

using Data;

namespace Business
{
  public class BusinessClass : IBusiness
  {
    IData _dataClass;

    public BusinessClass(IData dataClass)
    {
      _dataClass = dataClass;
    }
   public string GetBusinessData()
    {
      return _dataClass.GetData();
    }
  }
}

It is pretty straight forward. We declare IData instance local variable and expecting IData instance in the constructor of BusinessClass at runtime, which we assign to our local variable and in the method GetBusinessData(), we make use of this local variable assuming it would be initialized well in advance, we call the GetData() method. Note that no "new" keyword here is used to create instance and now BusinessClass also does not takes this responsibility of creating objects of DataClass. This code will still not work. We need to do some homework in our presentation layer as well.

Step3. Move to Presentation project and do the same thing as we did in Business layer for DataClass, but now in Initiator class for Business layer. So, our Initiator class looks like this:

using Business;
namespace Presentation
{
  public class Initiator
  {
    IBusiness _businessClass;
    public Initiator(IBusiness businessClass)
    {
      _businessClass = businessClass;
    }
    public string FetchData()
    {
      return _businessClass.GetBusinessData();
    }
  }
}

This is also easy to understand. We declare IBusiness instance local variable, then in the constructor, we assume that we get the pre-initialized instance of business class and assign the same to our local variable and call GetBusinessData() method via that. Note that we completely removed the "new" keyword from here and this class also does not take this responsibility to create objects. Our job is still not done. We still have Main() method in program.cs class that creates object of Initiator class via "new" keyword and class the FetchData() method. Our goal is to completely get rid of this "new" keyword and make sure classes follow Single Responsibility Principle.

Step4. Add a new class named DependencyInjector in the Presentation project and add the following code to it:

using Unity;
using Unity.Lifetime;

namespace Presentation
{
  public static class DependencyInjector
  {
    private static readonly UnityContainer UnityContainer = new UnityContainer();
    public static void Register<I, T>() where T : I
    {
      UnityContainer.RegisterType<I, T>(new ContainerControlledLifetimeManager());
    }
    public static void InjectStub<I>(I instance)
    {
      UnityContainer.RegisterInstance(instance, new ContainerControlledLifetimeManager());
    }
    public static T Retrieve<T>()
    {
      return UnityContainer.Resolve<T>();
    }
  }
}

This DependencyInjector class is a generic class that takes care of resolving types and registering types. It makes use of Unity to register instance of any type coming to it. We could have made it specific to our classes and interfaces, but it is always better to be generic to entertain whatever new types come to our application in the future.

To define and register our concrete types, for example, IBusiness and Business class, add another class named Bootstrapper to the presentation project. This class acts as its name i.e. Bootstrapper for our application i.e. first class to resolve dependencies beforehand as soon as our application loads. So, add Bootstrapper.cs and add the following code to it.

using Business;
namespace Presentation
{
  public static class Bootstrapper
  {
    public static void Init()
    {
      DependencyInjector.Register<IBusiness, BusinessClass>();
    }
  }
}

This class just calls generic Register method of DependencyInjector class and requests to resolve dependency of BusinessClass, so that when needed, the instance of Business class would be supplied.

Step5. In the Main method, now just call this Init() method of Bootstrapper class to register the types and get instance of Initiator class via Retrieve method of DependencyInjector class. We need this Initiator instance to call FetchData method of Initiator class. So the following is the code of our Program.cs class:

using System;
namespace Presentation
{
  public class Program
  {
    static void Main(string[] args)
    {
      Bootstrapper.Init();
      Initiator initiator = DependencyInjector.Retrieve<Initiator>();
      string data = initiator.FetchData();
      Console.WriteLine(data);
      Console.ReadLine();
    }
  }
}

Simple, Crisp and clear. Note that we got rid of the "new" keyword from here also and the main responsibility of this class is initialization and application start-up.

In a nut-shell we did the following:

  1. Introduced generic DependencyInjector class to take care of registering/resolving types.
  2. Introduced Bootstrapper class that calls DependencyInjector class methods in its Init() method for registering concrete types.
  3. In the Main method of Program.cs invoked Init method of Bootstrapper class, so that types are registered and resolved at the start of application.
  4. Called our FetchData() method from Initiator class.

To, me everything looks fine and we are all set to run the application.

Step6. Run the application by hitting F5. We assumed everything would work fine as we took care of dependencies, but we got the following run time error.

When you check the InnerException, the error is obvious and self-explanatory. It says, "- InnerException {"The current type, Data.IData, is an interface and cannot be constructed. Are you missing a type mapping?"} System.Exception {System.InvalidOperationException}"

Yes, you are right. We took care of registering BusinessClass with IBusiness. We took care of resolving Initiator class instance, but what about DataClass? Remember, we removed "new" from BusinessClass constructor as well when we try to get DataClass instance, but we have not yet registered the type nor resolved it to be used when needed.

The solution is that we need to register type for DataClass as well, so that we get its instance when needed. But how to do it? We cannot access or reference the Data project in presentation layer as it will violate our layered architecture. So, the only place is the Business project. But again, if we do it altogether as we did in Presentation layer, it will not make sense and we would have code duplication and two bootstrappers in the application. One way is MEF that I already explained in one of my previous articles, but that method is a bit complex. Let’s explore how we can overcome this situation without referencing Data project in presentation project, without code duplication, without multiple and confusing/hard to manage bootstrappers and without SOLID/coupling violation.

Resolve Dependency of Dependency using Unity Extensions

Unity provides this flexibility to resolve dependency of dependencies without structure violation via UnityContainerExtensions as shown below:

namespace Unity.Extension
{
  public abstract class UnityContainerExtension: IUnityContainerExtensionConfigurator
  {
    protected UnityContainerExtension();

    public IUnityContainer Container { get; }
    protected ExtensionContext Context { get; }

    public void InitializeExtension(ExtensionContext context);
    public virtual void Remove();
    protected abstract void Initialize();
  }
}

This class is part of Unity.Abstractions dll and contains an abstract method named Initialize(). Abstract method means we can override this method and write our custom initialization logic. So, let’s do that.

Create a class named DependencyOfDependencyExtension in Business project. You can name the class as per your choice. I named the class in this way for the sake of understanding. Install Unity package in the Business project in the same way as we did for presentation project. Do not forget to choose Business as the default project in package manager console as shown below.

Step1. Once Unity is added to the project as was in presentation layer, add following namespaces to our newly created DependencyOfDependencyExtension class

using Data;
using Unity;
using Unity.Extension;

Now, inherit the class from UnityContainerExtension class present in Unity.Extension namespace of Unity.Abstractions assembly and override its Initialize method as follows to register DataClass type with IData.

using Data;
using Unity;
using Unity.Extension;

namespace Business
{
  public class DependencyOfDependencyExtension : UnityContainerExtension
  {
    protected override void Initialize()
    {
      Container.RegisterType<IData,DataClass>();
    }
  }
}

Step2. Now we must make our presentation layer know about this extension. So, move to presentation layer and in the DependencyInjector class add a new generic method named AddExtension() to add new extensions as shown below,

public static void AddExtension<T>() where T : UnityContainerExtension
    {
      UnityContainer.AddNewExtension<T>();
    }

Do not forget to add using Unity.Extension;namespace to this class to access UnityContainerExtension class. Complete the class as follows.

using Unity;
using Unity.Extension;
using Unity.Lifetime;

namespace Presentation
{
  public static class DependencyInjector
  {
    private static readonly UnityContainer UnityContainer = new UnityContainer();
    public static void Register<I, T>() where T : I
    {
      UnityContainer.RegisterType<I, T>(new ContainerControlledLifetimeManager());
    }
    public static void InjectStub<I>(I instance)
    {
      UnityContainer.RegisterInstance(instance, new ContainerControlledLifetimeManager());
    }
    public static T Retrieve<T>()
    {
      return UnityContainer.Resolve<T>();
    }
    public static void AddExtension<T>() where T : UnityContainerExtension
    {
      UnityContainer.AddNewExtension<T>();
    }
  }
}

Step3.

We are almost done, now Add concrete type for this extension in Bootstrapper’s class Init method as follows:

using Business;
namespace Presentation
{
  public static class Bootstrapper
  {
    public static void Init()
    {
      DependencyInjector.Register<IBusiness, BusinessClass>();
      DependencyInjector.AddExtension<DependencyOfDependencyExtension>();
    }
  }
}

Job done, just run the application and we see our desired output as shown below:

In this way we resolved dependency of dependency that is dependency(DataClass) of dependency (BusinessClass) to the presentation layer.

Summary

Let’s do the quick summary of what we did in our application and complete tutorial.

  1. We created a three-layered basic architecture with presentation, business and data access layer. The architecture was tightly coupled and violated SOLID.
  2. We introduced Unity to resolve dependency between presentation and business layer. Thereafter, we used constructor injection to inject dependencies via interfaces.
  3. We resolved dependency of dependency using Unity Extensions so that our architecture is not breached.
  4. We got rid of "new" keywords and delegated the responsibility of object creation to Unity container.
  5. We achieved inversion of control via dependency injection.
  6. Every class has single responsibility and is open to extension and not modification.
  7. We achieved Abstraction with the help of interfaces.

Conclusion

In this tutorial we tried to learn dependency injection with the help of simple example. The example taken is very basic, but the concept could be applied in MVC, Web API or any enterprise level application to resolve dependencies and achieve inversion of control with dependency injection. We also resolved dependency of dependencies with unity extensions. There could be more methods to resolve dependencies like property injection or service locator. We used constructor injection in our application. One can make use of other containers instead of Unity. I used Unity as I am more comfortable with it. Hope you enjoyed the article. Happy coding 😉

License

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

Share

About the Author

Akhil Mittal
Architect Magic Software Inc.
India India
This member doesn't quite have enough reputation to be able to display their biography and homepage.
Group type: Collaborative Group

690 members


You may also be interested in...

Pro
Pro

Comments and Discussions

 
PraiseGreat Pin
Mahsa Hassankashi18-Apr-18 4:22
mvpMahsa Hassankashi18-Apr-18 4:22 
GeneralRe: Great Pin
hack2root18-Apr-18 4:42
memberhack2root18-Apr-18 4:42 
GeneralRe: Great Pin
Akhil Mittal 18-Apr-18 8:12
mvp Akhil Mittal 18-Apr-18 8:12 
SuggestionUsing NuGet package [dependency_injection_build] Pin
hack2root18-Apr-18 4:06
memberhack2root18-Apr-18 4:06 
QuestionMy DI approach Pin
hack2root18-Apr-18 0:46
memberhack2root18-Apr-18 0:46 
GeneralMy vote of 5 Pin
Ehsan Sajjad12-Apr-18 5:13
mvpEhsan Sajjad12-Apr-18 5:13 
GeneralRe: My vote of 5 Pin
Akhil Mittal 18-Apr-18 8:12
mvp Akhil Mittal 18-Apr-18 8:12 
GeneralMy vote of 5 Pin
prashita gupta22-Mar-18 19:10
memberprashita gupta22-Mar-18 19:10 
GeneralRe: My vote of 5 Pin
Akhil Mittal 25-Mar-18 18:47
mvp Akhil Mittal 25-Mar-18 18:47 
Praise:thumbsup: Pin
Member 1300306418-Mar-18 9:14
memberMember 1300306418-Mar-18 9:14 
GeneralRe: :thumbsup: Pin
Akhil Mittal 18-Mar-18 18:08
mvp Akhil Mittal 18-Mar-18 18:08 
GeneralMy vote of 5 Pin
JoshYates198016-Mar-18 5:43
professionalJoshYates198016-Mar-18 5:43 
GeneralRe: My vote of 5 Pin
Akhil Mittal 16-Mar-18 6:03
mvp Akhil Mittal 16-Mar-18 6:03 
GeneralMy vote of 5 Pin
dmjm-h15-Mar-18 12:38
memberdmjm-h15-Mar-18 12:38 
GeneralRe: My vote of 5 Pin
Akhil Mittal 15-Mar-18 20:40
mvp Akhil Mittal 15-Mar-18 20:40 
SuggestionGraphical halfway Pin
Frederic GIRARDIN14-Mar-18 1:18
memberFrederic GIRARDIN14-Mar-18 1:18 
GeneralRe: Graphical halfway Pin
Akhil Mittal 15-Mar-18 20:44
mvp Akhil Mittal 15-Mar-18 20:44 
QuestionDomain driven design pattern and onion architecture is same Pin
Mou_kol13-Mar-18 22:04
memberMou_kol13-Mar-18 22:04 
AnswerRe: Domain driven design pattern and onion architecture is same Pin
Akhil Mittal 15-Mar-18 20:49
mvp Akhil Mittal 15-Mar-18 20:49 
GeneralRe: Domain driven design pattern and onion architecture is same Pin
Mou_kol15-Mar-18 22:19
memberMou_kol15-Mar-18 22:19 
GeneralRequest for few more articles Pin
Mou_kol13-Mar-18 22:01
memberMou_kol13-Mar-18 22:01 
GeneralRe: Request for few more articles Pin
Akhil Mittal 15-Mar-18 20:39
mvp Akhil Mittal 15-Mar-18 20:39 
GeneralRe: Request for few more articles Pin
hack2root18-Apr-18 0:50
memberhack2root18-Apr-18 0:50 
GeneralMy vote of 5 Pin
Mou_kol13-Mar-18 21:55
memberMou_kol13-Mar-18 21:55 
GeneralRe: My vote of 5 Pin
Akhil Mittal 15-Mar-18 20:35
mvp Akhil Mittal 15-Mar-18 20:35 

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.

Permalink | Advertise | Privacy | Cookies | Terms of Use | Mobile
Web01-2016 | 2.8.180920.1 | Last Updated 14 Mar 2018
Article Copyright 2018 by Akhil Mittal
Everything else Copyright © CodeProject, 1999-2018
Layout: fixed | fluid