.NET Core依赖注入 -> 获取接口的所有实现

50

我有一个名为IRule的接口,以及多个实现该接口的类。我想使用.NET Core依赖注入容器来加载所有实现了IRule的规则。

可惜我无法实现这个目标。我知道可以将IEnumerable<IRule>注入到我的控制器构造函数中,但我不知道如何在Startup.cs中注册这个设置。

3个回答

77

只需要逐个注册所有的IRule实现,Microsoft.Extensions.DependencyInjection (MS.DI)库会将其解析为一个IEnumerable<T>。例如:

这只是简单地将所有IRule的实现都注册一遍;Microsoft.Extensions.DependencyInjection (MS.DI)库可以将其解析为一个IEnumerable<T>

services.AddTransient<IRule, Rule1>();
services.AddTransient<IRule, Rule2>();
services.AddTransient<IRule, Rule3>();
services.AddTransient<IRule, Rule4>();

消费者:

public sealed class Consumer
{
    private readonly IEnumerable<IRule> rules;

    public Consumer(IEnumerable<IRule> rules)
    {
        this.rules = rules;
    }
}

注意:MS.DI支持的唯一集合类型是IEnumerable<T>


解决IEnumerable<T>是否存在性能问题?与通过注入抽象工厂,然后按名称解析单个服务相比,有大约十几个问题。谢谢。 - Whoever
@任何人:这是一个无法回答的问题。您必须自己测量,以确定性能特征在您特定的环境中是否正常。测量,测量,测量。 - Steven
@Steven 谢谢。我又仔细看了一下。实际上,即使是我有的这两种情况也是不同的。在其中一个情况中,我循环遍历并调用所有实现,因此注入IEnumerable非常合理;在另一个情况中,每个调用者只需要一个特定的实现,但只有两个实现且初始化成本可以忽略不计,所以我决定把这个抽象留到以后再说 ^_^ - Whoever
@steven 你会如何对规则进行排序呢?比如我有rule1、rule2和rule3。如果你想要的顺序是rule3、rule1、rule2,或者在另一个部分中你想要的顺序是rule2后面是rule1。我想到的一种方法是将它们用逗号分隔成列表,然后循环数组并与规则名称匹配,但这似乎有些笨拙。你有什么建议吗? - DotnetShadow
请注意,对于AddSingleton来说,这也可以正常工作。 - solublefish
请勿省略接口类型(例如 services.AddTransient<Rule1>(); services.AddTransient<Rule2>();) - solublefish

17

对于其他寻找答案的人。您还可以通过查看汇编并注册实现特定接口的所有类:

// Get all implementations of IRule and add them to the DI
var rules = typeof(Program).Assembly.GetTypes()
    .Where(x => !x.IsAbstract && x.IsClass && x.GetInterface(nameof(IRule)) == typeof(IRule));

foreach (var rule in rules)
{
    services.Add(new ServiceDescriptor(typeof(IRule), rule, ServiceLifetime.Transient));
    // Replace Transient with whatever lifetime you need
}

这也会为您的依赖注入提供一个 IEnumerable<IRule>。这种解决方案的优点是,您不需要将每个规则都添加到您的依赖注入中。您只需向项目中添加 IRule 的新实现,它就会自动注册。


谢谢你提出这个想法!你可能会考虑使用IsAssignableFrom而不是GetInterface - Vincent Bitter
谢谢你的提示。那会更好。不幸的是,它不起作用。我猜原因是IsAssignableFrom指的是引用,这里无法解析。因此,必须选择通过名称的解决方法。 - Sebastian
5
@Sebastian 不太确定你的意思。这个可以正常工作:.Where(x => typeof(IRule).IsAssignableFrom(x)); - Dennisch

8

Sebastian和Dennisch的答案合并:

var rules = Assembly.GetExecutingAssembly().GetTypes()
             .Where(x => !x.IsAbstract && x.IsClass && typeof(IRule).IsAssignableFrom(x));

foreach (var rule in rules)
{
    services.Add(new ServiceDescriptor(typeof(IRule), rule, ServiceLifetime.Transient));
}

此外,请注意,您可以使用执行程序集来搜索类型。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接