Alfred Luu
Alfred Luu

Reputation: 2026

The best way of adding Services with multiple instances but same IOptions model

I have implemented the IndexBuilderSettings which is a model for some Services, such like when I created one for that will like below

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

And the SearchBrandBuilder is using IOptions construction

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

But the problem is when I have to create another one, like SearchUnitBuilder with the same IOptions constructor, the Configure() seems can't help because it will be overwritten due to its singleton pattern, please noticed that I would like to keep the settings json like this

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

I also don't want to rewrite the library model as IOptionsSnapshot which will inject via the name to distinguish. https://andrewlock.net/using-multiple-instances-of-strongly-typed-settings-with-named-options-in-net-core-2-x/

So the guide from link would be like this, but I won't prefer

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")); 
}

So, I would like to use Configuration as temporary in the Startup, I found out that it could be done by below code, a little bit workaround

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

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

I see it is a acceptance solution, but I would like to ask if whether any better solution for that? Something like a built-in function:

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

Or any regular solution for dealing this case?

Upvotes: 2

Views: 4011

Answers (2)

TheGeneral
TheGeneral

Reputation: 81573

You can also use named options with IOptionsSnapshot... I'm personally not huge fan, however they are useful in certain use cases. ¯\_(ツ)_/¯

Given

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

Implementation

public class SomeClass 
{
   ...

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

   ...

Additional Resources

Upvotes: 3

Guru Stron
Guru Stron

Reputation: 143243

Personally I prefer creating child config instances inheriting from base ine containing all properties:

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

And then registering and resolving them accrodingly:

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

And

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

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

If there are a lot of such builders - you can even partly automate registration with some reflection.

Upvotes: 2

Related Questions