【问题标题】:The best way of adding Services with multiple instances but same IOptions model添加具有多个实例但 IOptions 模型相同的服务的最佳方式
【发布时间】:2021-09-08 06:32:25
【问题描述】:

我已经实现了 IndexBuilderSettings,它是一些服务的模型,比如当我为它创建一个模型时,如下所示

services.AddSingleton<SearchBrandBuilder>()
    .Configure<IndexBuilderSettings>((settings) =>
    {
        Configuration.GetSection("SearchBrandConfig").Bind(settings);
    });

SearchBrandBuilder 正在使用 IOptions 构造

public class SearchBrandBuilder : SearchIndexBuilder<SearchBrandModel>
{
    public SearchBrandBuilder(IOptions<IndexBuilderSettings> opts);
}

但问题是当我必须创建另一个时,例如 SearchUnitBuilder 具有相同的 IOptions 构造函数,Configure() 似乎无济于事,因为它会由于其单例模式而被覆盖,请注意我会喜欢保持这样的设置json

  "SearchBrandConfig": {
    "AdminApiKey": "456",
    "IndexName": "new-brands",
    "SearchServiceName": "search_name"
  },
  "SearchUnitConfig": {
    "AdminApiKey": "123",
    "IndexName": "new-units",
    "SearchServiceName": "search_name"
  },

我也不想将库模型重写为 IOptionsSnapshot,它将通过名称注入来区分。 https://andrewlock.net/using-multiple-instances-of-strongly-typed-settings-with-named-options-in-net-core-2-x/

所以链接中的指南是这样的,但我不会喜欢

public void ConfigureServices(IServiceCollection services)
{
    services.Configure<SlackApiSettings>("Dev", Configuration.GetSection("SlackApi:DevChannel")); 
    services.Configure<SlackApiSettings>("General", Configuration.GetSection("SlackApi:GeneralChannel")); 
    services.Configure<SlackApiSettings>("Public", Configuration.GetSection("SlackApi:PublicChannel")); 
}

所以,我想在启动时临时使用配置,我发现可以通过下面的代码来完成,有点解决方法

services.AddSingleton<SearchBrandBuilder>(
    new SearchBrandBuilder(Options.Create(Configuration.GetSection("SearchBrandConfig").Get<IndexBuilderSettings>())));

services.AddSingleton<SearchUnitBuilder>(
    new SearchUnitBuilder(Options.Create(Configuration.GetSection("SearchUnitConfig").Get<IndexBuilderSettings>())));

我看到这是一个验收解决方案,但我想问是否有更好的解决方案?类似于内置函数的东西:

services.AddSingletonWithSetting<SearchUnitBuilder, IndexBuilderSettings>(settings => 
    Configuration.GetSection("SearchUnitConfig").Bind(settings));

或者有任何常规的解决方案来处理这种情况?

【问题讨论】:

  • 取决于具体情况,但如果个人有其他依赖项,我只会创建 Search{Type}Config 的继承自 IndexBuilderSettings 并单独注册/注入它们。

标签: c# dependency-injection singleton


【解决方案1】:

我个人更喜欢创建继承自包含所有属性的基本配置的子配置实例:

public class SearchBrandConfig : IndexBuilderSettings {}
public class SearchUnitConfig : IndexBuilderSettings {}

然后相应地注册和解决它们:

services.Configure<SearchBrandConfig>(Configuration.GetSection("SearchBrandConfig"));
services.Configure<SearchUnitConfig>(Configuration.GetSection("SearchUnitConfig "));

public class SearchBrandBuilder : SearchIndexBuilder<SearchBrandModel>
{
    public SearchBrandBuilder(IOptions<SearchBrandConfig> opts);
}

public class SearchUnitBuilder : SearchIndexBuilder<SearchUnitModel>
{
    public SearchUnitBuilder(IOptions<SearchUnitConfig> opts);
}

如果有很多这样的构建器 - 您甚至可以通过一些反射部分自动化注册。

【讨论】:

  • 感谢@Guru,我之前也检查过这个技术,但它基本上需要重写库,我可以改变它,但想象如果库来自其他公众会很糟糕。所以我只是更喜欢我们使用范围的实现
【解决方案2】:

您也可以将命名选项IOptionsSnapshot 一起使用...我个人不是超级粉丝,但它们在某些用例中很有用。 ¯\_(ツ)_/¯

给定

services.Configure<Config>("SomeConfig", Configuration.GetSection("SomeSection"));
services.Configure<Config>("AnotherConfig", Configuration.GetSection("AnotherSection"));

实施

public class SomeClass 
{
   ...

   public SomeClass(IOptionsSnapshot<Config> namedOptionsAccessor)
   {
      _config1 = namedOptionsAccessor.Get("SomeConfig");
      _config2 = namedOptionsAccessor.Get("AnotherConfig");
   }

   ...

其他资源

【讨论】:

  • OP 在问题中提到了这种技术,但写道它并不可取。
  • 谢谢@TheGeneral,我之前检查过这个技术,但它基本上需要重写库,我可以改变它,但如果库来自其他公众,那就不好了。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-01-10
  • 1970-01-01
  • 1970-01-01
  • 2015-04-29
  • 2023-01-18
  • 2021-03-16
  • 2019-03-27
相关资源
最近更新 更多