I'm migrating a tool to a .net 5 console app. I would like to change my DI system, currently a modified version of TinyIoC, to use if possible the built-in DI. Currently, my tool will load and auto-register any dll it finds in its config file. First-in wins, so a user-provided implementation of one of my interfaces will take precedence over my default one, loaded last.
Additionally, I need to be able to register several variants of a given interface, and have my DI system choose between them based on configuration. Currently, this works with a RegistrationName attribute that I've added to Tiny. When tiny auto-registers everything in a dll, it includes this name in it's registration.
So, for example, I have a IProvider interface, with methods including IDbConnection GetConnection(string connectionString);
. I have several default implementations for SQL Server, Postgres, etc. and users can provide other implementations in dlls that I don't know about when compiling my tool.
Here is how I declare my SQL Server provider...
[RegistrationName("System.Data.SqlClient")]
class SqlClient : IProvider
{
Here is how I specify provider in my qfconfig.json...
{
"defaultConnection": "Data Source=localhost;Initial Catalog=Northwind;Integrated Security=True",
"provider": "System.Data.SqlClient"
}
Here is how I ask Tiny for a concrete instance...
// RegistrationName is passed to the Resolve method.
// Tiny returns the implementation whose RegistrationName matches.
_provider = _tiny.Resolve<IProvider>(config.provider);
So I want to keep this possibility, but find a less personal way of doing it.
I fear I've strayed out into the forest on this subject. The docs and tutos that I find all cover much simpler scenarios, where there's one registered implementation of an interface, and everything is explicitly registered in code. Can someone point me back on the road please?
If I understand your use case correctly, you have possible multiple
IProvider
implementations, but always only need one at runtime, which is based on the the configured value that maps to theRegistrationName
attribute.There's nothing built-in to the MS.DI framework that simplifies such use case, but as you only need to register one at runtime, you can achieve this by iterating the assemblies and finding that specific implementation and register that:
This way registration is based on the name, while resolving can be done in a keyless fashion:
In the case I misunderstood your question, and you need multiple
IProvider
implementations at runtime, and need to resolve them by their key... well, that's certainly possible, but you will have to write more code. And here's the takeaway from everything that follows,ActivatorUtilities
is your friend:ActivatorUtilities.CreateInstance
is MS.DI's extension point that allows the creation of unregistered classes, while injecting them with dependencies that are part of the providedIServiceProvider
instance.ActivatorUtilities.CreateInstance
comes with a lot of unfortunate subtle downsides, such as the inability to check for cyclic dependencies, which could lead to nasty stack overflow exceptions instead. But this is the best we can achieve with MS.DI. Other DI Containers are more mature and feature rich in this respect.