Wednesday, 13 June 2018

DotNet Core Dependency Injection does not have named instances

I have seen various people asking about named instances on forums for Microsoft Dependency injection and although other DI frameworks support them and they seem useful, it might also be a code smell that can usually be solved by abstracting things a but more!

An Example

You might want something like this:

var db1connstring = "whatever";
var db2connstring = "something else";
services.AddSingleton(s => new DBConn(db1connstring)).Named("db1");
services.AddSingleton(s => new DBConn(db2connstring)).Named("db2");

services.AddSingleton(s => new Service(s.GetNamedService("db1"), s.GetNamedService("db2")));

It all seems legit, I mean after all, our service requires two db connections of the same type, so we need to disambiguate don't we? We can't do this in Dotnet core either because it isn't supported yet or they took the decision that it can cause more harm than good.

Workaround 1 - Concrete types

There is a common mistake that types are added to the services collection just for the sake of constructing something else that is explicitly created in registration when we could potentially just pass the types in directly after creating them. Something which is not fully DI but which works for simple cases. In the above example, we could.

var db1connstring = "whatever";
var db2connstring = "something else";
var db1 = new DBConn(db1connstring);
var db2 = new DBConn(db2connstring);

services.AddSingleton(new Service(db1, db2));

This works fine as long as nothing else that is being automatically created requires the types IDBConnection since they are no longer registered in the services collection. You could register them as concrete objects in services as well but then they would need to be Singletons since you are only creating a single instance when using a concrete type (you'll notice that only AddSingleton has an overload that takes an object). It would also potentially cause ambiguity again, although it would be fine if they were only used from GetServices() elsewhere.

Workaround 2 - Abstracting the Config

If you ever have more than one of the same type in a DI constructor and this is very common with strings, you can instead abstract the whole lot to a configuration type instead of separate items. In our example, we could easily create a ServiceConfiguration type with named properties of the relevant types and inject this configuration instead. In the following example, we have a single Constructor parameter on Service and since the ServiceConfiguration type is available in services, we do not need a lambda for the creation of Service but can use the simple form below.

var serviceConfiguration = Configuration.Get();
serviceConfiguration.MainDB = new DBConn(serviceConfiguration.MainConfig);
serviceConfiguration.BackupDB = new DBConn(serviceConfiguration.BackupConfig);
services.AddSingleton<ServiceConfiguration>();
services.AddSingleton();

Post a Comment