【问题标题】:Binding both services and parameters with dependency injection使用依赖注入绑定服务和参数
【发布时间】:2022-12-18 07:27:07
【问题描述】:

我有一个服务使用一组实现接口IX的服务:

   public class MyService()
   {
       MyService(IEnumerable<IX> xs)
       { 
          // Store xs in some field and later use them repeatedly. 
       }
   }

我想要一个类 MyX 实现 IX 的多个实例:

  public class MyX : IX
  {
    public string Field { get; }   
    public MyX(string field)
    {
      Field = field;
    }
  }

我将其中一些添加为单身人士:

builder.Services.AddSingleton<IX>(new MyX("field value 1"));
builder.Services.AddSingleton<IX>(new MyX("field value 2"));

[更新]...或来自配置:

builder.Services.AddSingleton(configuration.GetSection("xs").Get<IEnumerable<MyX>>());

此实现按预期工作:我的服务现在有一个 IEnumerable,它包含 MyX 的两个不同实例。

但是,我需要向 MyX 类添加一个记录器。我试试这个:

 public class MyX : IX
  {
    ILogger<MyX> _logger;
    public string Field { get; }

    public X(ILogger<MyX> logger, string field)
    {
      _logger = logger;
      Field = field;
    }
  }

但是现在我无法在服务设置期间构造MyX,因为我还没有记录器:

builder.Services.AddSingleton<IX>(new MyX(/* But where would I get a logger? */, "field value 1"));

我多次遇到这个问题的变体。总的来说,感觉 DI 希望我将我的类分成某种形式的多阶段构造(第一个字段,然后在服务解析时添加记录器)。这需要将 MyX 分成 MyXFactoryMyX,但这似乎有点尴尬。

构造一些 MyX 实例以用于依赖项注入的正确方法是什么?

【问题讨论】:

    标签: c# asp.net .net-core dependency-injection


    【解决方案1】:

    您可以使用 AddSingleton&lt;T&gt; 方法的覆盖,它接受一个 lambda 构建器,您可以在其中访问 ServiceProvider

    所以你可以像下面这样使用它:

    builder.Services.AddSingleton<X>(buillder=> 
       new MyX(builder.GetRequiredService<ILogger<MyX>>(),"field 1");
    });
    

    或者,您可以构建一个单例工厂 IXFactory 来实例化并返回相关的 X 实现。


    随着问题的发展: 假设您可以访问提供的IConfiguration。你可以有一个类似下面的配置类:

    public class XConfig
    {
       public string[] FieldValues { get; set; }
    }
    

    您可以将配置读入 XConfig 实例:

    var config = Configuration.Get<XConfig>();
    

    为此,您可能需要使用using Microsoft.Extensions.Configuration;

    现在您可以使用基本的 foreach 为每个 FieldValue 添加一个单例实例

    foreach(var fieldValue in config.FieldValues){
      builder.Services.AddSingleton<X>(buillder=> 
         new MyX(builder.GetRequiredService<ILogger<MyX>>(),fieldValue);
      });
    }
    

    【讨论】:

    • 它接受一个implementation factory,即一个返回实现实例的函数。
    • 非常好谢谢。但是,我如何从配置中读取我的MyX实例?
    • @SørenDebois 你能详细说明一下你的配置是什么样的吗?就像您的配置包含一个值数组并且您想提供一个实例?
    • 当然,谢谢,我已经更新了问题。
    【解决方案2】:

    另一种选择是使用ActivatorUtilities.CreateInstance,它允许解析未在 DI 容器中注册的参数:

    builder.Services.AddSingleton<IX>(
        sericeProvider => ActivatorUtilities.CreateInstance<MyX>(
            serviceProvider,
            new object[] { "field value 1" }));
    

    这将允许使用任意数量的依赖项实例化 MyX,只要只有一个 string 类型即可。

    【讨论】:

      猜你喜欢
      • 2018-10-11
      • 1970-01-01
      • 1970-01-01
      • 2018-02-20
      • 2013-01-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-09-09
      相关资源
      最近更新 更多