Nowadays, we are building applications around reusable software components and wiring them to create a more cohesive architecture. This wiring is done using Inversion of Control (IoC), under the more specific form of Dependency Injection (DI), but managing dependencies between components can become a difficult task, as size and complexity of the application increases. In this blog post, we’ll see how DI is implemented, and the benefits you get when using IoC containers for dependency resolutions.

Before we look into DI, there are two things that you should know of - Dependency Inversion Principle (DIP) and Inversion of Control.

Dependency Inversion Principle

The Dependency Inversion Principle is a software design principle which promotes the design of loosely coupled modules. According to the principles of DIP:

  1. High-level modules should not depend on low-level modules. Both should depend on abstractions.
  2. Abstractions should not depend upon details. Details should depend on abstractions.

To better understand DIP, let's look at an example. We have an OrderService class which is used to process orders for a particular system. The ProcessOrder method invokes the Save method from the OrderRepository class that saves orders to the database.

class diagram 1

The code:

public sealed class OrderService
{
    private readonly OrderRepository repository;
    
    public OrderService()
    {
        this.repository = new OrderRepository();
    }
    
    public void ProcessOrder(Order order)
    {
        // Validate order...
        
        this.repository.Save(order);
    }
}

public sealed class OrderRepository
{
    public void Save(Order order)
    {
        // Save to database
    }
}

The code above seems fine - but it does not to respect DIP. OrderService which is the higher-level module relies on the lower-level module OrderRepository. The fact that the two classes are tightly coupled means that your code is rigid, thus difficult to change. One way to decouple these two components is by applying Inversion of Control.

Inversion of Control

Inversion of Control is the actual mechanism that implements the dependency inversion principle. It is a software design that allows you to decouple your system by removing direct dependencies between software components.

Coming back to our example, we'll create an abstraction that the OrderService class can depend upon rather than relying on a concrete implementation of the OrderRepository class.

class diagram 2

This way, OrderService will no longer know about OrderRepository. It just has a reference to the IOrderRepository interface which is implemented by OrderRepository. The good thing about this design is that you can have another implementation of IOrderRepository - like saving orders to an XML file, making it flexible and testable.

The code for applying inversion of control:

// The abstraction
public interface IOrderRepository
{
    void Save(Order order);
}

// The implementation that saves to a DB
public OrderRepository : IOrderRepository
{
    public void Save(Order order)
    {
        // to save to the database
    }
}

public sealed class OrderService
{
    private readonly IOrderRepository repository;

    public void ProcessOrder(Order order)
    {
        // validate order & other business logics
        
        this.repository.Save(order);
    }
}

Now that OrderService is depending on an abstraction, which conforms to the dependency inversion principle, the question now is, how will it get an instance of the concrete implementation?

I have seen many implementations as such:

public sealed class OrderService
{
    private readonly IOrderRepository repository = new OrderRepository();

    public void ProcessOrder(Order order)
    {
        // validate order & other business logics
        
        this.repository.Save(order);
    }
}

With the code above, we're back to square one. The higher-level module knows how to instantiate the concrete implementation. The solution - get an instance of IOrderRepository at runtime. This is where DI comes into play.

Dependency Injection

Dependency injection is a software design pattern that implements inversion of control and allows a program design to follow the dependency inversion principle. The term was coined by Martin Fowler. An injection is the passing of a dependency (a service) to a dependent object (a client) - Wikipedia on Dependency Injection

Dependency Injection is a great way to reduce tight coupling between components by eliminating the use of hard-coded dependencies (concrete implementations) and instead injecting an instance of the dependency on demand. This means that the client is no longer responsible for creating the service. There are three ways to do dependency injection

  1. Constructor Injection
  2. Property Injection
  3. Method Injection

I will cover only constructor injection in this post, as it is the most common and elegant solution.

public sealed class OrderService
{
    private readonly IOrderRepository repository;
    
    // dependency passed via constructor
    public OrderService(IOrderRepository repository)
    {
        this.repository = repository;
    }
    
    public void ProcessOrder(Order order)
    {
        // validate order & other business logics
        
        this.repository.Save(order);
    }
}

Now to use it:

var svc = new OrderService(new OrderRepository()); // save to database

Another implementation which saves orders to an XML file can be easily changed without altering OrderService

var svc = new OrderService(new XmlOrderRepository());

This is also much easier to unit test your code as you can swap the concrete implementation with for example a fake object.

public sealed class FakeOrderRepository: IOrderRepository
{
    public void Save(Order order)
    {
        // add to a collection in memory...
    }
}

[TestFixture]
public class OrderServiceTest()
{
    [Test]
    public void Is_Order_Successfull()
    {
        // AAA
    
        var svc = new OrderService(new FakeOrderRepository());
        
    }
}

Now that we had a look at what DI solves, let’s see how IoC containers are beneficial. Referencing our example above, OrderService has only one dependency. What if there's more, chained or nested dependencies like this:

var processor = new OrderProcessor(new OrderRepository(), new ProductService(new ProductRepository());

This is where IoC containers come into play.

Inversion of Control Containers

Injecting dependencies is a great way to reduce boilerplate and infrastructure code, but resolving dependencies shouldn't be a manual task, especially when they are chained or nested. Instead, you can use an IoC container to perform dependency resolution automatically, i.e creating instances of your dependencies.

There are many IoC containers out there which you can use, but choosing one is sometimes a daunting task. There are many reasons to consider when opting for an IoC container; support, performance, simplicity, and features. Daniel Palme has written a great blog post on IoC containers performance benchmark comparison which might help you decide what’s best for you.

I’ll be using SimpleInjector as it offers a balance between performance, support and has features such as generic decorators and interception. First, install SimpleInjector via Nuget using the command below:

install-package SimpleInjector

Then, configure the container by registering all your instances for retrieval when requested.

var container = new Container();
container.Register<IOrderRepository, OrderRepository>();
container.Register<IProductService, ProductService>();
container.Register<IProductRepository, ProductRepository>();
container.Verify(); // optionally verify the container’s configuration

// non-generic version
container.Register(typeof(IOrderProcessor), typeof(OrderProcessor));

To resolve an instance, simply call the GetInstance method

var container = new Container();
var repository = container.GetInstance<IOrderRepository>();

// non-generic version
var repository = container.GetInstance(typeof(IOrderRepository));

You can consult the Simple Injector’s documentation for more information.

Conclusion

So far, we have seen how Inversion of control and Dependency Injection aim at decoupling software components by following the Dependency Inversion Principle. We’ve talked about IoC containers and the advantages they provide with dependency resolution.