C# Dependency Injection Runtime (dynamic) registra

2020-06-03 08:11发布

I am using VS 2017 and .NET Core. Using Dependency Injection, I would like to register my service at runtime, dynamically. My goal is to write instances of my service that implement the service interface inside of separate assemblies. The servicename/assembly name will then be added to some sort of configuration file (or db table).

My registration code would do something like this:

var ServiceTypeName = LoadServiceAssembly(AssemblyName); 

var serviceProvider = new ServiceCollection()
 .AddTransient<IDILogger, "ConsoleDILogger">()  // <--- Goal
 .BuildServiceProvider();

var logger = serviceProvider.GetService(IDILogger);

Clearly, the AddTransient line will not work as such a method does not exist. It does, however, depict the idea. I want to register the type by a string name so that the loader application need not be recompiled everytime I add a new service type.

I cannot seem to find how to do this. Any suggestions would be welcome.

TIA

3条回答
Fickle 薄情
2楼-- · 2020-06-03 08:38

You can use factory to achieve that.

services.AddScoped(provider =>
{
    //Resolve some service at runtime.
    var aService = provider.GetService<AServiceType>();
    //Any synchronous logic here
    return new MyDynamicService();
});
查看更多
Emotional °昔
3楼-- · 2020-06-03 08:42

That's obviously not possible as is, however, I used something similar to this in a project to avoid having to add each new type to the container:

var assembly = typeof(YourClass).Assembly; // I actually use Assembly.LoadFile with well-known names 
var types = assembly.ExportedTypes
   // filter types that are unrelated
   .Where(x => x.IsClass && x.IsPublic);

foreach (var type in types)
{
    // assume that we want to inject any class that implements an interface
    // whose name is the type's name prefixed with I
    services.AddScoped(type.GetInterface($"I{type.Name}"), type);
}

For your specific case, you could even make this shorter:

var type = assembly.ExportedTypes.First(x => x.Name == runtimeName);
services.AddScoped(typeof(IDILogger), type);
查看更多
Anthone
4楼-- · 2020-06-03 08:54

You could read configured type from the settings, load the required type via reflection and register it in service collection:

//  Read from config
var assemblyPath = "...";
var typeName = "...";

var assembly = Assembly.LoadFrom(assemblyPath);
var loggerType = assembly.GetType(typeName);

var serviceProvider = new ServiceCollection()
    .AddTransient(typeof(IDILogger), loggerType)
    .BuildServiceProvider();

var logger = serviceProvider.GetService<IDILogger>();

Such dynamic approach will not require any recompilation if you add or reconfigure new logger.

查看更多
登录 后发表回答