Dependency Injection

Other topics

Remarks:

The whole point of Dependency Injection ( DI ) is to reduce code coupling. Imagine any kind if interaction which involves newing up something like in the "Hard coded dependency example".

A big part of writing code is the ability to test it. Every time we new up a new dependency, we make our code difficult to test because we have no control over that dependency.

How would you test code which depends on DataTime.Now for example? It always changes so you have no reference. This is when you inject a stable parameter as your starting point. You can control it, you can write tests based on various values and make sure you always get the right result.

A good option therefore is to pass an interface or an abstract class as a parameter in the constructor DI.

An interface represents a well defined contract, you can always rely on the methods to be there and you can always rely on the method signatures.

Once you start using DI other aspects will open up. For example, even if you pass an interface at some point you will need a real implementation to actually do any work. This is where other concepts appear. We can use IOC ( Inversion of Control ) to resolve our dependencies. This means that we instruct our code to always use a specific implementation for any contract. Of course there are other ways of doing this. We could always instantiate each contract with a specific implementation and from that point onwards our code can use that part :

public ILogging Logging { get; set }

at some point we initialise it.

Logging = new FileLogging();

this will always work as long as our class fulfils the expected contract :

public class FileLogging : ILogging

from the initialise moment onwards we always use the Logging object. This makes lif easier because if we ever decide to change and use a DatabaseLogging for example, we only have to change the code in one place and this is exactly where we initialise the Logging class.

Is DI only good for testing? No, DI is important when writing maintainable code as well. It allows the separation of concerns to be clear.

When you write any code, think ... is it testable, can I write a test, that's when injecting a DateTime value instead of using DateTime.Now makes sense.

Ninject Configurations

After the install of an IoC (Inversion of Control) container, some tweaks are needed to make it work. In this case, I'll use Ninject. In the NinjectWebCommon file, that is located in the App_Start folder, substitute the CreateKernel method with:

private static IKernel CreateKernel()
    {  
        // Create the kernel with the interface to concrete bindings           
        var kernel = RegisterServices();
        try
        {
            kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel);
            kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();
           
            return kernel;
        }
        catch
        {
            kernel.Dispose();
            throw;
        }
    }

And the RegisterServices method with:

 private static StandardKernel RegisterServices()
        {
            Container container = new Container();
            // encapsulate the interface to concrete bindings in another class or even in another layer 
            StandardKernel kernel = container.GetServices();
            return kernel;
        } 

Create a new class to to the binding that in this case is called Container:

public class Container
{
    public StandardKernel GetServices()
    {
        // It is good practice to create a derived class of NinjectModule to organize the binding by concerns. In this case one for the repository, one for service and one for app service bindings
        return new StandardKernel(new NinjectRepositoryModule(),
            new NinjectServiceModule(),
            new NinjectAppServiceModule());
    }
}

Finally in each derived NinjectModule class modify the bindings overloading the Load method like:

public class NinjectRepositoryModule: NinjectModule
{
    public override void Load()
    {
        // When we need a generic IRepositoryBase<> to bind to a generic RepositoryBase<>
        // The typeof keyword is used because the target method is generic
        Bind(typeof (IRepositoryBase<>)).To(typeof (RepositoryBase<>));

        // When we need a IUnitOfWorkbind to UnitOfWork concrete class that is a singleton
        Bind<IUnitOfWork>().To<UnitOfWork>().InSingletonScope();
    }
}

Another example of derived NinjectModule:

public class NinjectServiceModule :NinjectModule
{
    public override void Load()
    {
        // When we need a IBenefitService to BenefitService concrete class           
        Bind<IBenefitService>().To<BenefitService>();

        // When we need a ICategoryService to CategoryService concrete class  
        Bind<ICategoryService>().To<CategoryService>();

        // When we need a IConditionService to ConditionService concrete class  
        Bind<IConditionService>().To<ConditionService>();           
    }
}

Utilization of the interfaces

In the concrete class that need the service, use the interface to access the service instead of its implementation like:

 public class BenefitAppService
{
    private readonly IBenefitService _service;
    public BenefitAppService(IBenefitService service)
    {
        _service = service;
    }      
   
    public void Update(Benefit benefit)
    {
        if (benefit == null) return           
        _service.Update(benefit);
        _service.Complete();
    }        
}

Now if you need something in the concrete class, won't interfere in the the code above. You may change the service implementation for another completely difference, and as long its satisfies the interface you are good to go. Also it makes it very easy to test it.

Constructor dependency injection

The Constructor Dependency Injection requires parameters in the constructor to inject dependencies. So you have to pass the values when you create a new object.

public class Example
{
    private readonly ILogging _logging;

    public Example(ILogging logging)
    {
        this._logging = logging;
    }
}

Hard coded dependency

public class Example
{
    private FileLogging _logging;

    public Example()
    {
        this._logging = new FileLogging();
    }
}

parameter DI

public DateTime SomeCalculation()
{
     return DateTime.Now.AddDays(3);
}

vs

public DateTime SomeCalculation(DateTime inputDate)
{
    return inputDate.AddDays(3);
}

Ninject Dependency Injection

Dependency resolver is used to avoid tightly-coupled classes, improve flexibility and make testing easy. You can create your own dependency injector (not recomended) or use one of well-written and tested dependency injectors. In this example I am going to use Ninject.

Step one: Create dependency resolver.

First of all, download Ninject from NuGet. Create folder named Infrastructure and add class named NinjectDependencyResolver:

using Ninject;
using System;
using System.Collections.Generic;
using System.Web.Mvc;

public class NinjectDependencyResolver
    : IDependencyResolver
{
    private IKernel kernel;

    public NinjectDependencyResolver()
    {
        // Initialize kernel and add bindings
        kernel = new StandardKernel();
        AddBindings();
    }

    public object GetService(Type serviceType)
    {
        return kernel.TryGet(serviceType);
    }

    public IEnumerable<object> GetServices(Type serviceType)
    {
        return kernel.GetAll(serviceType);
    }

    private void AddBindings()
    {
        // Bindings added here
    }
}

The MVC Framework will call the GetService and GetServices methods when it needs an insance of a class to service an incoming request.

Step two: Register dependency resolver.

Now we have our custom dependency resolver and we need to register it in order to tell MVC framework to use our dependency resolver. Register dependency resolver in Global.asax.cs file:

protected void Application_Start()
{
    AreaRegistration.RegisterAllAreas();
    DependencyResolver.SetResolver(new NinjectDependencyResolver());
    
    // .....
}

Step three: Add bindings.

Imagine that we have following interface and implentation:

public interface ICustomCache
{
    string Info { get; }
}

public class CustomCache : ICustomCache
{
    public string Info
    {
        get
        {
            return "Hello from CustomCache.";
        }
    }
}

If we want to use CustomCache in our controller without tightly-coupling our controller with CustomCache, then we need to bind ICustomCache to CustomCache and inject it using Ninject. First things first, bind ICustomCache to CustomCache by adding following code to AddBindings() method of NinjectDependencyResolver:

private void AddBindings()
{
    // Bindings added here
    kernel.Bind<ICustomCache>().To<CustomCache>();
}

Then prepare your controller for injection as below:

public class HomeController : Controller
{
    private ICustomCache CustomCache { get; set; }

    public HomeController(ICustomCache customCacheParam)
    {
        if (customCacheParam == null)
            throw new ArgumentNullException(nameof(customCacheParam));

        CustomCache = customCacheParam;
    }

    public ActionResult Index()
    {
        // cacheInfo: "Hello from CustomCache."
        string cacheInfo = CustomCache.Info;

        return View();
    }
}

This is example of costructor injection and it is one form of dependency injection. As you see, our Home controller does not depend on CustomCache class itslef. If we want to use another inplementation of ICustomCache in our application, the only thing we need to change is to binding ICustomCache to another implentation and that is the only step we need to take. What happened here is, MVC Framework asked our registered dependency resolver to create instance of HomeController class via GetService method. GetService method ask Ninject kernel to create requested object and Ninject kernel examines the type in its term and finds out that constructor of HomeController requeires an ICustomCache and binding has already been added for ICustomCache. Ninject creates instance of binded class, uses it to create HomeController and returns it MVC Framework.

Dependency chains.

When Ninject tries to create type, it examines other depenencies between type and other types and if there is any Ninject tries to create them also. For example, if our CustomCache class requires ICacheKeyProvider and if bining added for ICacheKeyProvider Ninject can provide it for our class.

ICacheKeyProvider interface and SimpleCacheKeyProvider implentation:

public interface ICacheKeyProvider
{
    string GenerateKey(Type type);
}

public class SimpleCacheKeyProvider
    : ICacheKeyProvider
{
    public string GenerateKey(Type type)
    {
        if (type == null)
            throw new ArgumentNullException(nameof(type));

        return string.Format("{0}CacheKey", type.Name);
    }
}

Modified CustomCache class

public class CustomCache : ICustomCache
{
    private ICacheKeyProvider CacheKeyProvider { get; set; }

    public CustomCache(ICacheKeyProvider keyProviderParam)
    {
        if (keyProviderParam == null)
            throw new ArgumentNullException(nameof(keyProviderParam));

        CacheKeyProvider = keyProviderParam;
    }

    ...........
}

Add binding for ICacheKeyProvider:

private void AddBindings()
{
    // Bindings added here
    kernel.Bind<ICustomCache>().To<CustomCache>();
    kernel.Bind<ICacheKeyProvider>().To<SimpleCacheKeyProvider>();
}

Now when we navigate to HomeController Ninject creates instance of SimpleCacheKeyProvider uses it to create CustomCache and uses CustomCache instance to create HomeController.

Ninject has number of great features like chained dependency injection and you should examine them if you want to use Ninject.

Contributors

Topic Id: 6392

Example Ids: 21827,21828,22001,22002,22003,22128

This site is not affiliated with any of the contributors.