Sitemap
iamprovidence

iamprovidence

👨🏼‍💻 Full Stack Dev writing about software architecture, patterns and other programming stuff https://www.buymeacoffee.com/iamprovidence

5 Advanced DI Tips

3 min readOct 3, 2022

--

Press enter or click to view image in full size

DI itself is pretty straightforward topic, but even there exist some stuff that may surprise you. I hope you will learn something new here. Without further delay, let’s just begin 😃

1 IServiceScopeFactory

Most juniors developers been told that there are 3 services lifetimes:

  • Transient — service is created every time it is resolved
  • Scoped — service is created once per request
  • Singleton — service is created once per application

It is only partially correct. The true about Scoped lifetime that it is not bounded to request. It just happens so, that a scope is created for a request. However, there is a little bit more about it. You can create your own scope with IServiceScopeFactory:

In the example above, an instance of scoped IService will be created twice for each scope inside a single request.

This is especially useful when you need to inject Scoped service in Singleton and other situations where the dependency injection scope is not aligned with the context lifetime.

2 Register service with multiple interfaces

Whenever you have a class that implement multiple interfaces:

And should be resolved by each of those separately, there is only one true way to do it:

You need to register a service itself and both interfaces resolving that instance.

However, it is pretty easy to do a mistake here:

With configuration above, you will end up having different instances for each interface.

And this mistake I did by myself. Can you spot what is wrong here?

MessageBus is created outside our DI mechanism. And that single instance will behave like a singleton despite it is registered as Scoped.

Don't be like me, don’t repeat my mistake 😟

3 Inject multiple services

Nothing really stops you from registering multiple services with the same interface:

Moreover, it actually can be quite handy with patterns like Chain of Responsibility services could be resoled all at once. To inject multiple instances, use IEnumerable<>:

4 TryRegister

What if you don't need multiple services in your code, but only a single one? However, somebody accidentally registered it twice.

It might cause lots of headaches, especially when the service contains state that vanish because of the wrong lifecycle. Can you figure out which service will be resolved from the example above? The latest registered service will always have the highest priority, and the lifecycle does not matter here, see:

To avoid hours of debugging, use sets of TryAddXXX methods:

We are safe now. Only the first service will be registered in our DI container.

Get iamprovidence’s stories in your inbox

Join Medium for free to get updates from this writer.

By the way, if you have different implementations (MyService, MyService_2) of the same interface, it will just register the first one in DI.

However, if you want to allow multiple services of the same type to be registered use TryAddEnumerable:

Overall, if you want to register multiple implementations of the same service, use TryAddEnumerable. If you only need a single instance of the service, use TryAddSingleton.

5 Resolve one service with multiple implementation

It could be intended to have multiple implementations of one interface.

And you want to distinguish between those.

One of the ways we already have seen, but now with a little help from Linq:

Personally, I don't like it. You need to inject a collection to get a single item. Not only does it look bad, but it is also inconvenient while testing. You have dependency on direct implementation. Bleah 🤢

Moving my preferences aside, it also could not be possible for your architecture when concrete instances are marked as internal and located in another library.

An alternative approach would be to add a few more marker interfaces:

And register and resolve it with those new interfaces:

Or the third option, if you really want to be a smartass, you could use a factory 😎

With funky usage of it:

In case you want to be modern and fancy, you can get use of keyed services introduced in .Net8:

This can be resolved in the following way:

You are the one to decide which fits your needs best.

Summary

Turned out DI is not so boring as you thought 😃

I hope you learned something new, let me know in a comment what was new and what you already were aware of 💬

You can also support me with the link below ☕️

And don’t forget to follow if you want to continue receiving more interesting stuff about programming ✅

--

--

Written by iamprovidence

👨🏼‍💻 Full Stack Dev writing about software architecture, patterns and other programming stuff https://www.buymeacoffee.com/iamprovidence