【问题标题】:Multiple instances of class with different parameters based on configuration file基于配置文件的具有不同参数的类的多个实例
【发布时间】:2021-09-01 11:20:37
【问题描述】:

所以我有一个简单的配置类PubsubSettings

public class PubSubSettings
{
  public string ProjectId { get; set; }
  public string TopicId { get; set; }
  public int PartnerId { get; set; }
  public string SubscriptionId { get; set; }
}

我以前只在我的 appsettings.json 中配置了其中一个,但现在我希望能够处理任意数量的它们。

我有另一个类PubSub,我通常会在其中注入一个IOptions<PubSubSettings>。而这反过来又被注入到我的Worker 类中。

services.Configure<PubSubSettings>(configuration.GetSection(nameof(PubSubSettings)));
...
services.AddHostedService<Worker>();

所以,我现在想要做的是为我的 AppSettings PubSubSettings 部分中的每个条目添加一个新的 Worker 作为托管服务,并将相关的 IOptions&lt;PubSubSettings&gt; 注入到每个条目中(以及标准 @ 987654330@)。

所以本质上,我想要这个配置块:

 "PubsubSettings": [
    {
      "ProjectId": "project1",
      "TopicId": "topic",
      "PartnerId": 1,
      "SubscriptionId": "sub1"
    },
    {
      "ProjectId": "project2",
      "TopicId": "topic2",
      "PartnerId": 2,
      "SubscriptionId": "sub2"
    }    
  ]

最终创建了两个托管服务,一个使用第一组选项,另一个使用第二组选项。

我看到一些问题正在寻找类似的东西,但我找不到与此完全一致的东西,所以我有点难过。有什么想法吗?

解决方案是 Dotnet 5。

【问题讨论】:

  • 只需多次调用 AddHostedService 并指定您要使用的具体设置。所有 IHostedService 接口都将在您的应用完全启动之前被调用。
  • 是的,这违背了从配置文件中提取它的意义,不是吗?除非我误解了你的意思
  • 我的意思是为配置条目运行一个循环,例如 foreach(var ps in config.pses) 或者如果你希望它通过 DI 进行单元测试,创建 IHostedService,它聚合所有这些并做同样的事情。我会做第二种方法,因为我喜欢自己管理事情,但为简单起见,第一种方法就足够了。

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


【解决方案1】:

所以从我能找到的情况来看,没有办法开箱即用。

但是,这可以使用 ActivatorUtilitiesConfiguration.Bind() 的组合手动完成。

private void CreateWorkers(IServiceCollection services, IConfigurationRoot configuration)
{
    List<PubSubSettings> pubsubSettings = new();
    configuration.Bind(nameof(PubSubSettings), pubsubSettings);
    
    foreach (PubSubSettings setting in pubsubSettings)
    {
        services.AddSingleton<IHostedService>(s => ActivatorUtilities.CreateInstance<Worker>(s, ActivatorUtilities.CreateInstance<PubSub.PubSub>(s, setting)));
    }
}

本质上,您可以使用Bind 从 JSON 中获取配置对象。然后,您可以使用CreateInstance 手动构造Worker 以调用AddHostedService

在这种情况下需要两个调用,一个为工作线程生成PubSub(我们在其中传递设置参数),另一个为自己生成Worker

ActivatorUtilities 基本上注入了对象所需的一切,除了您提供的参数。

我们需要使用.AddSingleton&lt;IHostedService&gt;,因为framework checks for dupesAddHostedService() 的方式相同。

【讨论】:

    【解决方案2】:

    也许您可以尝试仅为对象创建一个类,并让 PubSubSettings 类仅用于数组:

    public class PubSubSettings
    {
      public PubSubObject[] PubSubs { get; set; }
    }
    
    public class PubSubObject
    {
      public string ProjectId { get; set; }
      public string TopicId { get; set; }
      public int PartnerId { get; set; }
      public string SubscriptionId { get; set; }
    }
    

    那么在启动类中你应该使用Bind来获取数组的当前值来为每个PubSub创建一个Worker

    PubSubSettings settings = new PubSubSettings();
    Configuration.GetSection(nameof(PubSubSettings)).Bind(settings);
    ...
    foreach(PubSubObject item in settings.PubSubs)
    {
       services.AddHostedService<Worker>();
    }
    

    然后在PubSub类中你需要在Array里面搜索PartnerId

    或者您可以按照 Microsoft 文档中 Named options support using IConfigureNamedOptions 部分中描述的方法:Options pattern in ASP.NET Core

    【讨论】:

    • 不错的答案,但我想我找到了更接近我需要的东西,不过还是谢谢
    • @Persistence 然后发布解决方案。
    • 话虽这么说...我不能 100% 确定它是否有效,所以我最终可能会删除它
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-06-25
    • 2016-07-24
    • 2011-03-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-12
    相关资源
    最近更新 更多