Search code examples
c#asp.net.net

How to get a value from database once and reuse it in a scoped service?


I need to get some settings data from database once, and then reuse it in a scoped service OtherService. How to achieve that?

Startup:

public void ConfigureServices(IServiceCollection services)
{            
    services.AddSingleton<IConfiguration>(Configuration);
    services.AddScoped<IDBService, DBService>();
    services.AddScoped<IOtherService, OtherService>();

    services.Configure<OtherSettings>(options =>
    {
        options.Url = Configuration.GetValue<string>("Other:Url");
    });
}

Settings class:

public class OtherSettings
{
    private readonly IDBService _dbService;

    public OtherSettings(IDBService dbService)
    {
       _dbService = dbService;
    }
    public string? Url { get; set; } //this is set in startup and works fine

    private string? cachedName; 
    public string? Name //this causes an issue due to needing a service to be retrieved
    {
        get
        {
            if (cachedName == null)
            {
                cachedName = FetchName();
            }

            return cachedName;
        }
    }

    public string FetchName()
    {
        return _dbService.GetSettings();
    }
}

The service that uses settings:

public class OtherService : IOtherService
{
    private readonly IOptions<OtherSettings> _otherSettings;

    public OtherService(
        IOptions<OtherSettings> otherSettings
    )
    {
        _otherSettings = otherSettings;
    }
}

The issue is that otherSettings needs parameters for a constructor to be injected into OtherService.

But perhaps this whole approach is not right.

Can this be fixed or is there a better way of not calling database over and over again, to get the same settings each time that OtherService is initialized?


Solution

  • Typically you want Scoped services as these are the ones with access to the DB contexts, however they're are pants for persistent data. For this you want a singleton service.

    If you create a new Singleton service, you can inject that into your scoped OtherSettings service. Then, instead of storing the data in the scoped object, you store it in the singleton.

    Do not try to bypass the scoped service and just use the singleton though as you will have to manually set up scope in order to access the DB content, but you could use it directly if you initialise the singleton data externally, eg at program startup, passing in the required data in a non-constructor method.

    An alternative to the custom singleton is to use the memory cache service MS offers, but a custom settings singleton is often much easier to understand for the service it provides.