How to initialize a property or variable once per application lifetime during startup by calling an external API in ASP.NET Core?

Viewed 1265

I have a need to initialize a property or variable (actually a string value) once per application lifetime during startup by calling an external API and then consume it in another class (preferably via DI or may be via some helper class?). What's the best way to achieve this? I'm not sure if I can use a config parameter here since this value is going to be obtained at run time via an external API call. But I want the call to be made only once during startup and value set for the entire application lifetime.

3 Answers

You can access the service injection system after the application starts up.

For example, first write an extension to your IHost like this: (Put this method any static class you like)

        public static IHost PostStart(this IHost host)
        {
            using var scope = host.Services.CreateScope();
            var services = scope.ServiceProvider;
            // You can get your services here.
            var configuration = services.GetService<IConfiguration>();
            
            // And right here, you can initialize your properties. Do anything you like.

            return host;
        }

After writting your PostStart method, call it after your program starts.

Change your Program.cs file.

    public class Program
    {
        public static void Main(string[] args)
        {
            CreateHostBuilder(args)
                .Build()
                .PostStart() // <- add this.
                .Run();
        }

        public static IHostBuilder CreateHostBuilder(string[] args)
        {
            return Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder => webBuilder.UseStartup<Startup>());
        }
    }

As you mentioned, you want to initialize some properties for a service. So just add your data store as a singleton dependency.

// Startup.cs ConfigureServices method.
services.AddSingleton<YourDataStore>();

And in your PostStart method, access it with:

// PostStart method.

var data = services.GetRequiredService<YourDataStore>();

var apiService = services.GetRequiredService<YourAPIService>();

data.YourProperty = apiService.GetResultFromYourDataSource();

The simplest thing to do is to create a class with a static parameter for the variable and only calls the API to get it if it's null. Then in DI you simply declare this class to be a Singleton.

So like

public class MyConfig
{
    private static readonly string _configVal;
    public static readonly string ConfigVal
    {
       get
       {
            if(String.IsNullOrEmpty(_configVal))
            {
                _configVal = GetApiValue();
            }
            return _configVal;
        }
    }
}

and in Startup.cs:

service.AddSingleton<MyConfig>();

Just use in-memory cache and set the priority property of CacheItemPriority to NeverRemove so the cached value is stored for the lifetime of your app.

Cache in-memory in ASP.NET Core

MemoryCacheEntryOptions

Update:

Apps running on a server farm (multiple servers) should ensure sessions are sticky when using the in-memory cache. Sticky sessions ensure that subsequent requests from a client all go to the same server. For example, Azure Web apps use Application Request Routing (ARR) to route all subsequent requests to the same server.

Non-sticky sessions in a web farm require a distributed cache to avoid cache consistency problems. For some apps, a distributed cache can support higher scale-out than an in-memory cache.

Related