What is dependency injection?

21 February 2017 Published By: Stephen Bradbury
Dependency Injection

Dependency injection is a design pattern that allows us to develop loosely coupled code. It has many advantages such allowing unit testing to be easier, decoupling your class libraries, allowing much easier code reuse and allowing us to write less code.

The Basics

To understand why we use dependency injection and the advantages it give us we must 1st look at what we would do without it. In our pretend world example we are going to write a class with a method which takes 2 words and returns a concatenated string:

public interface IStringService
{
  string GetConcatenation(string firstString, string secondString);
}

public class StringService : IStringService
{
  public string GetConcatenation(string firstString, string secondString)
  {
    return string.Format("{0}{1}", firstString, secondString);
  }
}

If we need to use this class we would expect our application code to look something like this:

public class Application
{
  IStringService stringService = new StringService();

  public void DoSomething(string firstString, string secondString)
  {
    stringService.GetConcatenation("Hello", "World");
  }
}

So what would we do if our client comes back to us and says "I loved the work you did with concatenating those words, but we would love the words to be the opposite way round". We could change the StringService and hope the client doesn't change his or her mind again or we could write a new class which implements IStringService but with a different implementation:

public class ReverseStringService : IStringService
{
  public string GetConcatenation(string firstString, string secondString)
  {
     return string.Format("{0}{1}", secondString, firstString);
  }
}

We then need to change our application code where-ever we use IStringService...sounds like it could be a lot of work if we have a large application.

Or how about if our client comes back to us and says they want the StringService to 'remember' the last string that was concatenated. We may decide we need a singleton so yet again we will need to re-write the class to look something like this:

public class StringService
{
    private static StringService _instance;

    private string firstString { get; set; }
    private string secondString { get; set; }

    protected StringService()
    {
    }

    public static StringService GetStringService()
    {
        if (_instance == null)
        {
            _instance = new StringService();
        }
        return _instance;
    }

    public string GetConcatenation(string firstString, string secondString)
    {
        this.firstString = firstString;
        this.secondString = secondString;
        return string.Format("{0}{1}", firstString, secondString);
    }
}

(I personally avoid singletons at all cost. The example is for illustration purposes only)

And again we will need to change where we use this method. Or what if we need a single instance per request life cycle? Or we want to warm up a cache when the application starts? Phew, this is getting quite complex.

Dependency Injection to the rescue

With the 1st example of the simple string concatenation we can use constructor injection to get an already resolved instance of the interface:

public class Application
{
    private readonly IStringService stringService;

    public Application(IStringService stringService)
    {
	this.stringService = stringService;
    }

    public void DoSomething(string firstString, string secondString)
    {
       this.stringService.GetConcatenation("Hello", "World");
    }
}

This is achieved in the dependency injection layer by using the following (the below examples are using AutoFac as the Dependency injection container, startup code can be found here)

builder.RegisterType<StringService>().As<IStringService>();

So if our client instead wants us to use the reverse string service was can simply change this registration in a single place:

builder.RegisterType<ReverseStringService>().As<IStringService>();

This means the rest of our application doesn't care about the implementation, just the interface.

So how about the singleton instance? We can totally remove the boilerplate singleton code (creating a static instance) and instead just concentrate on the implementation. Remembering the last string that was entered:

public class StringService : IStringService
{
    private string firstString { get; set; }
    private string secondString { get; set; }

    public string GetConcatenation(string firstString, string secondString)
    {
        this.firstString = firstString;
        this.secondString = secondString;
        return string.Format("{0}{1}", firstString, secondString);
    }
}

And then to make it a singleton:

builder.RegisterType<StringService>().As<IStringService>().SingleInstance();

14 lines of code eliminated and we didn't need to change our implementation to make it a singleton!

What isn’t dependency injection

This is similar to some actual code I have seen running on a client’s live website (Anonymised to protect the guilty)

public class DependencyInjection
{
    public ServiceA serviceA { get; set; }
    public ServiceB serviceB { get; set; }
    ...etc

    public DependencyInjection()
    {
        this.serviceA = new ServiceA();
        this.serviceB = new ServiceB();
        ...etc
    }
}

public class Application
{
    DependencyInjection di = new DependencyInjection();

    public void DoSomething()
    {
       di.serviceA.DoSomething();
    }
}