1

How to access singletons from ConfigureServices? There's a reason that I can't use appsettings for few configs.

For example, let's say that I want to set swagger title and version from database, not appsettings. My actual problem is I want to set consul address from my database. The problem should be the same, that I need to access my database in ConfigureServices. I have a custom extension like this:

public static IServiceCollection AddConsulConfig(this IServiceCollection services, string address)
{
    services.AddSingleton<IConsulClient, ConsulClient>(p => new ConsulClient(consulConfig =>
    {
        consulConfig.Address = new Uri(address); 
    }));

    return services;
}

I call it from startup:

public void ConfigureServices(IServiceCollection services)
{
    services.AddSingleton<IGlobalParameter, GlobalParameterManager>();
    
    //I want to use IGlobalParameter here directly but without BuildServiceProvider
    //This part is the problem
    var service = ??
    var varTitle = service.GetById("Title").Result.Value;
    var varConsulAddress = service.GetById("ConsulAddress").Result.Value;
    
    services.AddConsulConfig(varConsulAddress);
    
    services.AddControllers();
    services.AddSwaggerGen(c =>
    {
        c.SwaggerDoc("v1", new OpenApiInfo { Title = varTitle, Version = "v1" });
    });
}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    // I can use it here or in the controller no problem
    var service = app.ApplicationServices.GetRequiredService<IGlobalParameter>();
    var varTitle = service.GetById("Title").Result.Value;
    var varConsulAddress = service.GetById("ConsulAddress").Result.Value;
}

I DO NOT want to use BuildServiceProvider as it will make multiple instances, even visual studio gives warning about it. referenced in How to Resolve Instance Inside ConfigureServices in ASP.NET Core

I knew the existence of IConfigureOptions from the following link https://andrewlock.net/access-services-inside-options-and-startup-using-configureoptions/#the-new-improved-answer

But, I can't seem to find how exactly do I use that in ConfigureService:

public class ConsulOptions : IConfigureOptions<IServiceCollection>
{
    private readonly IServiceScopeFactory _serviceScopeFactory;
    public ConsulOptions(IServiceScopeFactory serviceScopeFactory)
    {
        _serviceScopeFactory = serviceScopeFactory;
    }

    public void Configure(IServiceCollection services)
    {
        using (var scope = _serviceScopeFactory.CreateScope())
        {
            var provider = scope.ServiceProvider;
            IGlobalParameter globalParameter = provider.GetRequiredService<IGlobalParameter>();
            var ConsulAddress = globalParameter.GetById("ConsulAddress").Result.Value;

            services.AddConsulConfig(ConsulAddress);
        }
    }
}

Set it in startup:

public void ConfigureServices(IServiceCollection services)
{
    services.AddSingleton<IGlobalParameter, GlobalParameterManager>();
    services.AddSingleton<IConfigureOptions<IServiceCollection>, ConsulOptions>(); // So what? it's not called
    
    services.AddControllers();
}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    // IConsulClient is still null here
}

Any solution to how do I achieve this?

fatihyildizhan
  • 8,614
  • 7
  • 64
  • 88
ShinAn
  • 11
  • 3
  • Assuming `.AddSwaggerGen(Action config)` implements the options pattern properly, you should be able to implement `IConfigureOptions` in your own service, using DI to inject `IGlobalParameter`. – Jeremy Lakeman May 07 '21 at 05:31
  • For your other question `.AddSingleton(p => ` p is the service collection, you can `p.GetService()` in the lambda here. – Jeremy Lakeman May 07 '21 at 05:37
  • Thank you Jeremy, I solved it because of you. Many thanks – ShinAn May 07 '21 at 06:16

1 Answers1

0

Thank you Jeremy, it's as simple as that. I don't know why I spend way too much time figuring out how to set this

The solution is to add singleton :

services.AddSingleton<IConsulClient, ConsulClient>(
    p => new ConsulClient(consulConfig =>
    {
        var ConsulAddress = p.GetRequiredService<IGlobalParameter>().GetById("ConsulAddress").Result.Value;
        consulConfig.Address = new Uri(ConsulAddress);
    }
));
ShinAn
  • 11
  • 3