profile for TheDaveJay at Stack Overflow, Q&A for professional and enthusiast programmers

Friday 25 March 2011

Dependancy Injection / Inversion of Control (IOC) in .net c#

Introduction

DI is one of the most  things that is assumed to be difficult, and without understanding it can be one of those things which you will never try again if you get it wrong. I myself was one of those people. 

I have been working with a colleague who insists on using it, and being open minded to the suggestion again, I asked him to show me what its about  - in a correct implementation. He also gave me a fantastic book to read by Mark Seemann called Dependency Injection in .net.

I am now a fan of DI, and can see the use for it. This article explains DI, and how to do it practically, along with the reasoning behind it.

What is Dependency Injection

DI by definition is "a set of software design principles and patterns that enable us to develop loosely coupled code" 

Within OOP, this means that collaborating classes should rely on the infrastructure to supply the necessary services. This leads to code that is more maintainable and easier to test.

What is Loose Coupling and how does it fit with DI?

Loose coupling in a design pattern is "Programming to an interface, and an implementation". Following this design pattern makes your code more extensible and therefore leads to higher maintainability. 
DI is a technique that enables Loose Coupling in your code.

DI and Loose Coupling in the real world

The best "real world" analogy I have come across when it comes to explaining Loose Coupling is the  plug and socket.

For anyone that has gone to a hospital, they would most likely seen something like this:

TV wired directly to the wall


As you can see, the TV is wired directly to the wall. Normally it would make no practical sense to have your electrical device wired directly to the wall outlet. If your device broke, you would need an skilled electrician to come and disconnect the device from the wall, and replace it for you. This would come at a practical cost.

What would make more sense is to have a plug an socket, where any old handy man can come in, unplug it, and swap it out - meaning no electrician required :
Simple Plug and socket

This kind of analogy can apply to our programming. The socket in this case is an Interface defining what shape the plug needs to be. Not only does this make it more maintainable, it means we are no longer constrained to one type of device. We could easily swap it out for a new version HD 3D tv, or even a projector of some sorts.

To give a real life example as to how to implement this within your application, think about caching.

Scenario


You are developing a web application which only sits on one server. You know that within one year, the site will grow to be on a web farm and some of the caching needs to distributed evenly. For now you don't have the time to develop and test that the caching works, so you are just going to stick to the "Session" cache.

You want to be able to swap this caching mechanism out in the next release to be a distributed caching system, such as SQL or Appfabirc, NCache etc.

Solution


Below is an example of how we would structure the application so it has a loosely coupled caching mechanism:

1) Create an interface that has the Add and Get from cache functionality:


 public interface ICacheProvider
 {
        void AddToCache<TValue>(string key, TValue value);
        TValue GetFromCache<TValue>(string key);
 }

As you can see, we have a generic "AddToCache" which takes a key and object. We also have the "GetFromCache" which takes a key and returns a generic type.

2) Create an implementation of the ICacheProvider. In this case it will be something that adds to the Session Cache, so we will call it SessionCacheProvider:

    public class SessionCacheProvider : ICacheProvider
    {
 
        public void AddToCache<TValue>(string key, TValue value)
        {
            Session[key] = value;
        }
 
        public TValue GetFromCache<TValue>(string key)
        {
            return (TValue)Session[key];
        }
    }



3) In your code behind, implement the ICacheProvider



public class SomeCodeBehindFile
    {
        ICacheProvider cacheProvider;
 
        public SomeCodeBehindFile(ICacheProvider cacheProvider)
        {
            this.cacheProvider = cacheProvider;
        }
 
        public void AddItemToCache(string itemClicked)
        {
            this.cacheProvider.AddToCache<string>("SomeItemKey", itemClicked);
        }
 
        public string RetrieveItemFromCache()
        {
            return this.cacheProvider.GetFromCache<string>("SomeItemKey");
        }        
    }

 As you can see, the SomeCodeBehindFile class is completely unaware of where the cache is, or what its doing with this cache. All it knows is that is can add to some cache, and get data from some cache.

What ever creates an instance of this class can also pass in any cache provider they want. In this case, we would pass through an instance of the session cache provider: i.e.

SomeCodeBehindFile scbf = new SomeCodeBehindFile(new SessionCacheProvider());
This method of setting the implementing class is called Constructor Injection. There are different techniques as to how to set the implementation class. We will cover the different ways later. For now, just know that we can easily switch the implementation to be a AppfabricCacheProvider without having to make any code changes in the SomeCodeBehindFile class - this makes it highly maintainable!



The mis-conceptions of DI

There are 4 common misconceptions about DI, which I myself fell prey to. Below are the actual facts on DI
  • DI IS NOT only relevant for late binding. 
Late binding refers to the ability to replace parts of an application at runtime. Late binding is only one of the aspects of DI
  • DI IS NOT only relevant for unit testing - if anything it promotes it - if you so choose to do it (which you should)
  • DI IS NOT an abstract factory
This is one of the biggest myths I had to unlearn. DI itself is not a  dependency  factory. 
It doesn't resolve dependency objects for you. It is not a Service Locator - DI is in fact the exact opposite.
DI is a way to structure code, so that the developers never imperatively ask for the dependencies, but in fact force consumers to supply them.
  • DI DOES NOT require a DI container.
The DI container is an optional library the can make life easier when assigning components on the application wire-up. Its is by no means a required library. Latter on we can look at the differences between using a container and doing it manually.

...TO BE CONTINUED





3 comments:

  1. Nice Post

    It definately looks like it would make system upgrades a lot easier in addition to being able to use a development team more efficiently, the only problem being getting the team leader / company to use it.

    ReplyDelete
  2. To be continued... when? :D Nice post, does help give me an understanding of why I would want to use this

    ReplyDelete