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.
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 that 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
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:
When we use Constructor Injection, we are injecting dependency through the constructor. We are doing it so because we want our Service to be available in the entire Client object. Therefore, we will implement our Client class like this:
We created the field of IService type – _service. This field is initialized inside of the constructor using the passed parameter. Apart form injection, we followed other important principles, but you can read more about that here. 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:
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 Service2implementation 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:
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:
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:
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:
Although we can consider Constructor Injection to be the default approach when doing Dependency Injection, this pattern is good when developing frameworks.
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 hands. This is where DI Containers come to play!
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.
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 IServ
Client class will use both of these services, and for that purpose, we will use Constructor Injection. Here is how that class looks like:
Cool, let’s see how we can use containers to resolve all these dependencies.
In the fierce competition, Castle Windsor is still one of the most popular DI Frameworks and the one that many engineers use by default. It’s API is pretty straightforward. Take a look:
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 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:
Basically, it uses ContainerBuilder for creating a container and registering the objects. More information about this DI Container can be found here.
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:
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.
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. If you want to learn more about good practices that would make your code better, check out my video course – Introduction to TDD in C#.
Thanks for reading!
Read more posts from the author at Rubik’s Code.