Dependency Injection in C#

Dependency Injection (DI, wikipedia) is a design pattern that reduces hard-coded dependencies between your classes by injecting these dependencies at run-time, instead of during design-time. Technically, Dependency Injection is a mechanism that allows the implementation of another, more high-level, design pattern called Inversion of Control (IoC, wikipedia). The purpose of both patterns is to reduce hard-coded dependencies (or ‘coupling’) between your classes.

 What are dependencies?

 Suppose that you are building a web application that is going to send e-mails to visitors that have entered a form. In object-oriented code, it is important to separate responsibilities. So you’ll probably end up with a class that handles the form input (FormHandler) and a class that is responsible for sending the e-mails (MailSender).

The MailHandler class looks like this:

public class MailSender
{
    public void Send(string toAddress, string subject)
    {
        Console.WriteLine(“Sending mail to [{0}] with subject [{1}]”, toAddress, subject);
    }
}

your FormHandler class will look like this:

public class FormHandler
{
   public void Handle(string toAddress)
     {
        MailSender mailSender = new MailSender();
        mailSender.Send(toAddress, “This is concrete code implementation example”);
     }
}

Although there’s nothing wrong with this code, it is creating a dependency between the FormHandler and MailSender classes.

The dependency is created at line MailSender mailSender = new MailSender();.

Using the New keyword in your code to instantiate a class implies that you are creating a dependency. From a practical point of view, you are telling your FormHandler class to use a concrete implementation of the MailSender class. There’s no flexibility. ‘But why’, you ask, ‘is this a bad thing?’. Let’s take a look at some reasons …

Why are dependencies a bad thing?

  • You can’t use multiple implementations of the MailSender class
  • It makes unit testing (nearly) impossible

The bottom-line is that your FormHandler shouldn’t know what concrete implementation of the MailSender is used. What concrete implementation is used, should be determined outside of your classes. That way, you can swap out the MailSender with another implementation if the need arises. If you are writing unit tests for the FormHandler, you can swap out the MailSender class with a mocked version.

we have to do is rewrite our code to use Interfaces. This is required for Dependency Injection, but is good practice anyways. An interfaces is basically a contract between classes that force one class to behave exactly as described by the interface. The contract can be implemented by other implementations (different class, same behavior).

We create a very straightforward interface for the MailSender class:

public interface IMailSender
{
    void Send(string toAddress, string subject);
}

We also rewrite the MailSender class to implement the IMailSender interface:

public class MailSender : IMailSender
{
    public void Send(string toAddress, string subject)
    {
        Console.WriteLine(“Sending mail to [{0}] with subject [{1}]”, toAddress, subject);
    }
}

We’ve basically told C# that the concrete implementation of our MailSender class follows the IMailSender interface (or contract). It is now possible to create other concrete implementations that follow the same interface but do different things ‘under the hood’,

for example:

public class MockMailSender : IMailSender
{
    public void Send(string toAddress, string subject)
   {
       Console.WriteLine(“Mocking mail to [{0}] with subject [{1}]”, toAddress, subject);
   }
}

Right now, we can change which concrete implementation is used by our FormHandler by rewriting the code where the class is instantiated:

public class FormHandler
{
    public void Handle(string toAddress)
    {
         IMailSender mailSender = new MockMailSender();
        mailSender.Send(toAddress, “This is still a concrete implementation “);
    }
}

Of course, this already adds a lot of flexibility to our code, as we can swap out which concrete implementation of IMailService is used by changing line IMailSender mailSender = new MockMailSender();.

Although C# will now force you to implement the IMailSender interface to the letter, your code will already improve a lot by using interfaces. The next step is to implement manual dependency injection to get rid of this code change that is required to change which implementation is used.

Implementing manual dependency injection:

 Basically, we’re going to pass the dependency in through the constructor of the FormHandler class. This way, the code that is using the FormHandler can determine which concrete implementation of IMailSender to use:

public class FormHandler
{
     private readonly IMailSender mailSender;

     public FormHandler(IMailSender mailSender)
     {
          this.mailSender = mailSender;
     }

    public void Handle(string toAddress)
     {
          mailSender.Send(toAddress, “This is manual dependency injection”);
      }
}

The code that creates our FormHandler has to pass in a concrete implementation of IMailService. This means that control is now inverted; instead of the FormHandler deciding which implementation to use, the calling code does. This is the whole point of Inversion of Control, of which Dependency Injection is just one approach. The calling code (the code that uses the FormHandler and now controls the dependencies) looks like this:

class Program
{
    static void Main(string[] args)
    {
        IMailSender mailSender = new MockMailSender();
        FormHandler formHandler = new FormHandler(mailSender);
        formHandler.Handle(“contact@uzval.com”);

        Console.ReadLine();
     }
}

 Concluding thoughts:

 Dependency Injection is a difficult concept to grasp if you’ve never used it before. Just give it a try, and you’ll see how flexible it makes your code. Especially when you’re writing unit tests you’ll quickly see the benefits. In that case, you can easily swap in mock implementations of dependencies. If anything, it makes your code a lot cleaner and sort of forces you to write better code.

Advertisements