How to use Pure DI

How to use Pure DI

In my previous posts I talked about how you can decrease dependency on external libraries and frameworks while making your code simpler (not easier, simpler). In this post I want to continue on the same thread and show some of the benefits of Pure DI (as opposed to DI with a container).

DI-containers are beneficial if you have a complex application where you can rely on convention over configuration. If your application is not complex (and you should strive for that) or does not rely on conventions, a simpler approach can be followed by using pure DI. Before I dive in on how we can do this, let’s first iterate over a few of the disadvantages a container brings with it:

Complexity: When you configure a container in your composition root, you will usually be relying on conventions to make sure that the right types are resolved at runtime. How simple or complex this is, is directly proportional to how simple or complex your conventions are. If conventions are very clear, it will be relatively easy to spot how a dependency will be resolved at runtime. If not, it will be difficult to see how the application is composed.

Compiler assistance: Since you’re resolving your dependencies at runtime, the compiler can’t assist you. If you forget to declare a service it will just fail at runtime. This does not only go for registration but also for lifetimes. You can introduce lifetime mistakes and the compiler won’t be able to help you. An example of this is captive dependency (more information see: Captive dependency by Mark Seemann)

**Learning curve: **Every container has a different API. In order to use it effectively, you need to have quite a good knowledge of that API. With so many containers available, it’s possible you’ll encounter different containers in different projects, which means you need to learn a new API once in a while.

The situation where you might want to choose a pure DI approach over a container approach is when you do explicit registration instead of convention over configuration or when your conventions are really complex.

Pure DI in ASP.NET Web API

In order to use pure DI, you need to build your dependency graph whenever you code is being called. In an ASP.NET Web API application, this is when your controller is constructed. To intercept at this point, you need to implement the IHttpControllerActivator interface:

Pure DI in a console application

In a console application, we don’t have the per-request lifetime, but the same principles apply. We need to construct the graph on application start, right inside void main (or extract it to a separate class):

This is obviously a bit more typing work than registering services with a container, but it has certain advantages:

Compile-time safety: You cannot forget to register a service, because the compiler will tell you.

Lifetime configuration: You cannot make a lifetime mistake such as captive dependency. Because you are declaring the singletons in the constructor you can see that the services that get passed in their constructors will also be singletons. Again this makes it easier to read your configuration.

Explicit: Since you have very clearly marked places to declare singletons, per request and transient services, it makes the configuration very explicit. All types are also explicitly constructed so you can easily spot how the application is composed.

Learning curve: This is just object composition, so you don’t need to learn the specific API of a container.

Conclusion

Pure DI leads to more explicit code. You’ll write more code, but again I apply the same mantra: it’s more code, but it’s simpler code, so I’m happy to type a bit more for the sake of simplicity. One part that this approach does not solve is another pet peeve of mine: the over usage of constructor injection. In a next post I’ll show an example of how to use partial application to tackle this problem.