如何从 IServiceCollection 获取相同服务的不同实例

How to get different instances of same service from IServiceCollection

我有一个 Bar class 定义为这个

public class Bar: IBar
{
    private readonly IFoo _foo;
    private readonly string _keyname;

    public Bar(IFoo, string keyName)
    {
        //assign class field
    }
}

keyname字符串区分Bar的对象。 Bar 的方法使用此键名从 class.

外部获取不同的 settings/config

我希望 class 的客户端在单个 class 中具有不同的 Bar 实例。如果我们不使用服务,客户端可以做这样的事情

var bar1 = new Bar(foo, "key1");//instantiate with new
var bar2 = new Bar(foo, "key2");

为了注册 Bar class,我创建了一个这样的扩展方法

public static IServiceCollection AddMyService(this IServiceCollection services, string keyName)
{
    services.AddSingleton<IFoo, Foo>();
    services.AddTransient<IBar>(sp => 
       ActivatorUtilities.CreateInstance<Bar>(sp, keyName)
    );
    return services;
}

我想让客户端像这样注册多个实例

services.AddMyService("key1");
services.AddMyService("key2");

但我不确定他们将如何通过依赖注入访问 class 中的两个注册服务。这是解决这个问题的正确方法吗?

开箱即用的 dotnet 核心依赖注入不允许使用命名服务。

在您的情况下,您可以做的最简单的事情是注册所有 Bar 服务,然后将它们作为 IEnumerable 注入:

 var factory = ActivationUtilities.CreateFactory(typeof(Bar), new Type[] { typeof(string) });
 services.AddSingleton<IFoo, Foo>();
 services.AddTransient<IBar>(sp => 
   (IBar) factory(sp, new object[] { "key1" })
 );
 
 services.AddTransient<IBar>(sp => 
   (IBar) factory(sp, new object[] { "key2" })
 );
 services.AddTransient<AClient>();

 public class AClient {
      private IEnumerable<IBar> _bars;
      public AClient(IEnumerable<IBar> bars)
      {
          _bars = bars
      }

      public IBar GetBarByKey(string key) => _bars.FirstOrDefault(x => x.Key == key);
 }

ActivationUtilities.CreateFactory 方法 returns ObjectFactory 委托的一个对象,它表示一个函数,该函数接受 IServiceProvider 和参数列表的输入。通过这种方式,您可以创建对象,从服务提供者那里获取一些构造参数,而一些参数是直接给出的。

您可以通过注入一个为给定键创建 IBar 的工厂来解决此问题:

public interface IBarFactory { IBar Create(string key); }

public class BarFactory : IBarFactory {
    private readonly IFoo _foo;
    public BarFactory(IFoo foo) => _foo = foo;
    public IBar Create(string key) => new Bar(_foo, key);
}

客户端代码可以注入这个工厂:

public class Client1 {
    private readonly IBar _bar1;
    public Client1(IBarFactory barFactory)
        => _bar1 = barFactory.Create("key1");
}

注意BarFactory需要注入并转发Bar的所有依赖。

只有 IBarFactory 需要在您的 DI 容器中注册。不同的 IBar 不需要注册(当然,除了像 IFoo 这样的依赖项)。

一个缺点是当你想在 Client1 的单元测试中模拟 IBar 时需要额外的设置。您必须模拟并设置 IBarFactory 到 return 模拟 IBar.