Click here to Skip to main content
15,891,597 members
Articles / Programming Languages / C#

Exploring Dependency Injection in C# and Top 3 DI Containers – Part 2

Rate me:
Please Sign up or sign in to vote.
3.04/5 (8 votes)
8 Apr 2018CPOL7 min read 10.4K   5   3
Exploring dependency injection in C# and top 3 DI containers - part 2

In the previous article, we covered some topics that are important for understanding Dependency Injection. We considered that Dependency Injection is not just one principle, but an orchestration of many good practices, like Single Responsibility Principle and Dependency Inversion Principle. Apart from that, we could see how one can implement Dependency Injection manually and how taking care of all aspects leads us to more loosely coupled and more testable code.

Image 1

Just as a little reminder, Dependency Injection is a technique which is used to manage dependencies of objects. When we use it, one object supplies the dependencies of another object. A dependency is an object that can be used, for example, as a service. Passing of that dependency into an object that will use it, for example a client, is what injection is. Of course, other good practices have to be followed in order to achieve this.

Dependency Injection Patterns

There are three ways in which we can achieve Dependency Injection:

  • Constructor Injection
  • Property Injection
  • Method Injection

As you can see, the difference between these patterns is the place where dependency is injected. Let’s consider a simple example; we have a Client class that has a dependency on Service class. Service class has one method – ServiceMethod(). We want to call this method from our Client class via InitiateServiceMethod() method.

In a nutshell, we want to inject Service class into Client class. And we will do that in three different ways. This is our Service class:

C#
public interface IService
{
    void ServiceMethod();
}

public class Service : IService
{
    public void ServiceMethod()
    {
        Console.WriteLine("Service Method initiated");

        // Service Method implementation.
    }
}

When we use Constructor Injection, we are injecting dependency through the constructor. We are doing it because we want our Service to be available in the entire Client object. Therefore, we will implement our Client class like this:

C#
public class Client
{
    private readonly IService _service;

    public Client(IService service)
    {
        _service = service;
    }

    public void InitiateServiceMethod()
    {
        _service.ServiceMethod();
    }
}

We created the field of IService type – _service. This field is initialized inside of the constructor using the passed parameter. Apart from injection, we followed other important principles. Constructor Injection is the most commonly used approach in handling Dependency Injection. The only downside of this pattern is that the entire dependency tree needs to be initialized at application startup.

If we already have some default value for dependency in our class, but we want to give a user of our class the possibility to inject its own, that Property Injection is a good choice. For example, let’s say that we have two implementations of IService interface we already mentioned. Take a look:

C#
public class Service1 : IService
{
    public void ServiceMethod()
    {
        // Service Method implementation.
    }
}

public class Service2 : IService
{
    public void ServiceMethod()
    {
        // Service Method implementation.
    }
} 

Now, we know by default that we will use Service1 implementation of the IServiceinterface in our Client class, but we also want to be able to inject Service2 implementation in some situations. Apart from that, we want to keep our code loosely coupled and easily tested. Then, we will implement our Client class like this:

C#
public class Client
{
    public IService Service { get; set; }

    public Client()
    {
        Service = new Service1();
    }

    public void InitiateServiceMethod()
    {
        Service.ServiceMethod();
    }
}

As you can see, we now have the property of type IService in our Client class. The default value for this property has been set inside of the constructor. However, we can always use set option to inject some other values. This is why this pattern is also called Setter Injection. Here is how we can inject different dependency by using this property:

C#
using Moq;
using Xunit;

namespace ClientServicePropertyExample
{
    public class ClientTest
    {
        [Fact]
        public void InitiateServiceMethod_NoConditions_ProperMethodsCalled()
        {
            var serviceMock = new Mock<IService>();

            var client = new Client
            {
                Service = serviceMock.Object
            };

            client.InitiateServiceMethod();

            serviceMock.Verify(x => x.ServiceMethod());    
        }
    }
}

Our class is still testable and the code is still loosely coupled. That is why we could put mock object instead of the Service1 implementation in Client class. The downside of this approach is of course that the user can forget to set proper dependency and use the default one in every situation.

Method Injection is yet another way we can implement Dependency Injection.
This approach is used when we don’t need dependency object in every operation, or we don’t want to carve dependency in stone in the dependent class. As you can already assume, dependency is passed as a parameter of the method. Client class looks like this:

C#
public class Client
{
    public void InitiateServiceMethod(IService service)
    {
        service.ServiceMethod();
    }
}

There are no properties and no fields that need initializing. We are passing an object of the class that implements IService directly into the method. Dependency can be changed at every method call. The method can be still easily tested:

C#
using Moq;
using Xunit;

namespace ClientServiceExample
{
    public class ClientTest
    {
        [Fact]
        public void InitiateServiceMethod_NoConditions_ProperMethodsCalled()
        {
            var serviceMock = new Mock<IService>();

            var client = new Client();

            client.InitiateServiceMethod(serviceMock.Object);

            serviceMock.Verify(x => x.ServiceMethod());    
        }
    }
}

Although we can consider Constructor Injection to be the default approach when doing Dependency Injection, this pattern is good when developing frameworks.

DI Containers

Back in the day when there was no clear distinction between Inversion of Control and Dependency Injection, these containers were called Inversion of Control Containers, or IoC Containers. Since now we know that Dependency Injection is one form of Inversion of Control, Dependency Injection Containers or DI Containers became a more appropriate term.

What are these containers? Well, they are one of the tools that make Dependency Injection easier to use. During previous examples, we have done DI manually, meaning we haven’t used any tools, and that is good to get the overview of the concept. However, for big applications and complex systems, this approach can easily get out of hand. This is where DI Containers come to play!

Image 2

There are many DI Containers for .NET available on the market. However, their approach and APIs are rather similar and once you get hold of using one of them, it is quite easy to switch from one to another. They all use the same principle that I like to call RRR. Of course, it is an acronym that stands for: Register – Resolve – Release.

The first step, when using any DI Container, is registration of the object. In this step, we define which object of which class or interface will be created once dependency is encountered. At this moment, we also define a lifespan of that instance, meaning whether we will use the same instance for all other dependencies or will a new object be created for every dependency of that object, etc.

The second step is resolving an object from the container. In this step, we are asking our container for an object of the certain class. Behind the scenes, a container will resolve all dependencies, and give us a fully initialized object that is ready for use. Finally, we can release an object from the container, and by this, delete it from the container.

Image 3

As mentioned, there are many available options on the market for DI Container in .NET. Let’s check out some of them and see how we can use them. For this purpose, we will use an example that is a little bit more complicated. We will have two interfaces IService1 and IService2, and their respective implementations, Service1 and Service2.

C#
public interface IService1
{
    void Service1Method();
}

public interface IService2
{
    void Service2Method();
}

public class Service1 : IService1
{
    public void Service1Method()
    {
        // Service Method Implementation
    }
}

public class Service2 : IService2
{
    public void Service2Method()
    {
        // Service Method Implementation
    }
}

Client class will use both of these services, and for that purpose, we will use Constructor Injection. Here is how that class looks like:

C#
public class Client
{
    private readonly IService1 _service1;
    private readonly IService2 _service2;

    public Client(IService1 service, IService2 service2)
    {
        _service1 = service;
        _service2 = service2;
    }

    public void InitiateServiceMethods()
    {
        _service1.Service1Method();
        _service2.Service2Method();
    }
}

Cool, let’s see how we can use containers to resolve all these dependencies.

Castle Windsor

In the fierce competition, Castle Windsor is still one of the most popular DI Frameworks and the one that many engineers use by default. Its API is pretty straightforward. Take a look:

C#
var container = new WindsorContainer();
container.Register(Component.For<Client>());
container.Register(Component.For<IService1>()
    .ImplementedBy<Service1>());
container.Register(Component.For<IService2>()
    .ImplementedBy<Service2>().LifestyleSingleton());

var client = container.Resolve<Client>();
client.InitiateServiceMethods();

container.Release(client);

Firstly, we created an object of WindsorContainer. This object is our container and we will register objects in it. Notice that we use Register method of this object and Component class. In the second line, we register an object for Client class. Then we register the IService1 object. In that third line of the code, we say that any time the container finds a dependency on IService1, it should create an object of the type Service1.

A similar thing is done for IService2, but notice that there is the additional call of LifestyleSingleton method. Using this, we define that any time dependency of IService2 is encountered the same object of Service2 will be used. Then we call Resolve method for Client class. What will happen there is that the container will create all dependencies, properly initialize and return the object of the Client class. That object is regularly used.

You can find more information about this container at Castle project website.

Autofac

Autofac is another popular DI Container. It has a similar API to others and it is easy to use. There are some differences, however. Take a look at this code snippet, which does the same thing as the previous one, just with Autofac:

C#
var builder = new ContainerBuilder();

builder.RegisterType<Client>();
builder.RegisterInstance(new Service1())
    .As<IService1>();
builder.RegisterInstance(new Service2())
    .As<IService2>().SingleInstance();

var container = builder.Build();
var client = container.Resolve<Client>();

client.InitiateServiceMethods();

Basically, it uses ContainerBuilder for creating a container and registering the objects. More information about this DI Container can be found here.

Structure Map

It wouldn’t be fair to have this list of DI Containers without mentioning Structure Map. In fact, this is the oldest DI Container for .NET. It came out back in 2004 for .NET version 1.1. Here is how it is used:

C#
var container = new StructureMap.Container(x =>
{
    x.For<IService1>().Use<Service1>().Transient();
    x.For<IService2>().Use<Service2>().Singleton();
    x.For<Client>().Use<Client>();
});

var client = container.GetInstance<Client>();

client.InitiateServiceMethods();

container.Release(client);

No magic here, the same thing we have done with previous DI Containers. API may differ a bit, but it is pretty understandable. More information about Structure Map can be found here.

Conclusion

In this article, you had the chance to see different ways of achieving Dependency Injection. Apart from that, we explored some of the available DI Containers for .NET and saw how one can use them. This way we rounded up the story about Dependency Injection.

Thanks for reading!

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) Vega IT Sourcing
Serbia Serbia
Read more at my blog: https://rubikscode.net

Comments and Discussions

 
GeneralMy vote of 1 Pin
Suamere Scalar9-Apr-18 5:03
Suamere Scalar9-Apr-18 5:03 
QuestionSame *old* story Pin
Suamere Scalar9-Apr-18 4:55
Suamere Scalar9-Apr-18 4:55 
You are saying the same things that have been said for a decade, since the creation of the first "DI Containers", and it is just as bad now as it was then. Other than very low level development, the vast majority of programs should be using Dependency Injection. Of those people, 0% should be using "DI Containers"

1. The lie
As a project grows, the "Dependency Tree" can get out of hand. So, "DI Containers" are invented. DI Containers are a great way to handle unruly dependencies.

1. The truth
DI Containers cover up unruly dependencies by putting makeup on DI and making the ugly look pretty.

1. The fix
Doing Pure DI would produce the APPROPRIATE code smell and tell you... hey, your project is too big, maybe you should be spinning off some other projects.


2. The lie
DI Containers also help cache classes (services) so you don't have to instantiate them twice. That way, hefty instantiations don't take a long time.

2. The truth
DI Containers cover up bad constructor practices. No heavy lifting should be performed in constructors. There should only be basic assignment to the fields/properties, and maybe some "pure" work (see pure in Functional).

2. The fix
Doing Pure DI would create situations where services are more-frequently instantiated. If this slows down the application, that is an APPROPRIATE code smell to tell you... hey, you are doing too much work in constructors, cut that out.


3. The lie
Because DI Containers use Reflection and Service Location, it already has access to metadata of classes. So it can go ahead and also allow you to "Property Inject", even into Private Properties sometimes, which allows you to instantiate your own default dependencies, but possibly have them overridden?

3. The truth
Because DI Containers use Reflection and Service Location, you SHOULD NOT USE THEM. Service Location is an Anti-Pattern. DI Containers only get away with it because YOU aren't the one writing the code for it, it is hidden by makeup. Also, Property Injection causes Temporal Coupling.

3. The fix
Doing Pure DI wouldn't allow you to perform Property Injection. This would produce the APPROPRIATE code smell that you are creating cyclic dependencies or ugly code. There is no propery way to have cyclic dependencies. The fix is to not do bad code.


"Containers" also cover up Over-Injection. Need another dependency in a service? Stick it in the DI Container! With Pure DI, you would see that your constructor has more than 2 or 3 dependencies injected (and maybe an extra one or two primitives), and you would see an obvious code smell that you are probably Over-Injecting and need to refactor.

All of the things "Containers" "fix" are really just janky makeup slapped on ugly code to cover up the truth. If you EVER feel that you need a "Container" because of your project/code... the problem is your project/code.

Review Pure Dependency Injection using a Composition Root. Also google how to create smaller projects or "Micro Services". Not necessarily related to API Endpoints.
AnswerRe: Same *old* story Pin
Klaus Luedenscheidt9-Apr-18 19:13
Klaus Luedenscheidt9-Apr-18 19:13 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.