C# 通过.NET核心中的密钥解析动态注册的服务

C# 通过.NET核心中的密钥解析动态注册的服务,c#,.net-core,dependency-injection,tinyioc,C#,.net Core,Dependency Injection,Tinyioc,我正在将工具迁移到.net 5控制台应用程序。我想改变我的DI系统,目前是TinyIoC的一个修改版本,如果可能的话,使用内置的DI。目前,我的工具将加载并自动注册它在配置文件中找到的任何dll。首先在wins中,因此用户提供的我的一个接口的实现将优先于最后加载的默认接口 此外,我需要能够注册给定接口的几个变体,并让我的DI系统根据配置在它们之间进行选择。目前,它与我添加到Tiny的RegistrationName属性一起工作。当tiny自动注册dll中的所有内容时,它会在其注册中包含此名称 例

我正在将工具迁移到.net 5控制台应用程序。我想改变我的DI系统,目前是TinyIoC的一个修改版本,如果可能的话,使用内置的DI。目前,我的工具将加载并自动注册它在配置文件中找到的任何dll。首先在wins中,因此用户提供的我的一个接口的实现将优先于最后加载的默认接口

此外,我需要能够注册给定接口的几个变体,并让我的DI系统根据配置在它们之间进行选择。目前,它与我添加到Tiny的RegistrationName属性一起工作。当tiny自动注册dll中的所有内容时,它会在其注册中包含此名称

例如,我有一个,方法包括
IDbConnection GetConnection(stringconnectionstring)。我有几个SQL Server、Postgres等的默认实现,用户可以在DLL中提供编译工具时不知道的其他实现

下面是我如何声明我的SQL Server提供程序

[RegistrationName("System.Data.SqlClient")]
class SqlClient : IProvider
{
下面是我如何在qfconfig.json中指定提供者

{
    "defaultConnection": "Data Source=localhost;Initial Catalog=Northwind;Integrated Security=True",
    "provider": "System.Data.SqlClient"
} 
下面是我如何要求Tiny提供一个具体的例子

// RegistrationName is passed to the Resolve method.
// Tiny returns the implementation whose RegistrationName matches.
_provider = _tiny.Resolve<IProvider>(config.provider);
//RegistrationName被传递给Resolve方法。
//Tiny返回注册名称匹配的实现。
_provider=\u tiny.Resolve(config.provider);
所以我想保留这种可能性,但要找到一种不那么个人化的方式


恐怕我在这个问题上误入歧途了。我发现的文档和教程都涵盖了更简单的场景,其中有一个注册的接口实现,所有内容都在代码中显式注册。有人能给我指点路吗?

如果我正确理解您的用例,您可能有多个
IProvider
实现,但在运行时总是只需要一个,它基于映射到
RegistrationName
属性的配置值

MS.DI框架中没有内置任何东西可以简化此类用例,但由于您只需要在运行时注册一个,因此可以通过迭代程序集并找到特定的实现并注册来实现这一点:

var提供者=
从程序集中的程序集
来自程序集中的类型。GetExportedTypes()
其中typeof(IProvider).IsAssignableFrom(type)
哪里类型.IsAbstract
让attr=type.GetCustomAttribute()
其中attr?.Name==config.provider
选择类型;
AddTransient(typeof(IProvider),providers.Single());
这样,注册基于名称,而解析可以通过无键方式完成:

serviceProvider.GetRequiredService();
如果我误解了您的问题,那么您需要在运行时使用多个
IProvider
实现,并且需要通过它们的键来解决它们。。。当然,这是可能的,但是您必须编写更多的代码。下面是所有内容的要点,
ActivatorUtilities
是你的朋友:

//查找所有“名称->提供程序”映射
变量提供程序定义=
从程序集中的程序集
来自程序集中的类型。GetExportedTypes()
其中typeof(IProvider).IsAssignableFrom(type)
哪里类型.IsAbstract
让name=type.GetCustomAttribute()?.name
名字在哪里!=无效的
选择新的{name,type};
//生成IProvider工厂委托的帮助器方法
Func BuildProviderFactory(类型)=>
provider=>(IProvider)ActivatorUtilities.CreateInstance(提供程序,类型);
//创建将名称映射到提供程序工厂的词典
字典提供程序工厂=
providerDefinitions.ToDictionary(
keySelector:i=>i.name,
elementSelector:i=>BuildProviderFactory(i.type));
//可能的用途
Func factory=providerFactories[config.provider];
i供应商=工厂(服务供应商);
ActivatorUtilities.CreateInstance
是MS.DI的扩展点,允许创建未注册的类,同时向它们注入属于所提供的
IServiceProvider
实例的依赖项


ActivatorUtilities.CreateInstance
有许多令人遗憾的微妙缺点,例如无法检查循环依赖关系,这可能会导致严重的堆栈溢出异常。但这是我们与迪女士所能达到的最好成绩。其他DI容器在这方面更成熟,功能更丰富。

内置DI是基于代码的;若你们想要一个基于XML的提供者,你们需要考虑开源的替代方案。若问题更具体一些,那个就更好了。就像我有这个接口和这个默认实现一样,然后第三方可以使用这样那样的配置注册另一个实现。细节补充@为什么XML如此重要?这是“可配置”的缩写吗?如果不推荐工具,你能推荐一个工具吗?@bbsimonbb Yes;格式并不重要,但您提到您使用了一个“配置文件”,我使用过的所有DI在需要DI配置文件时都使用XML。这是黄金信息,谢谢。在不推荐工具的情况下,什么DI容器可以简化这种情况?我发现这个问题很难回答,因为它取决于您的用例,但我要说的是,大多数现代、成熟的DI容器使上述用例比MS.DI更简单、更易重新测试。