Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ASP.NET Core initialize singleton after configuring DI

So let's say I have a singleton class instance that I register in the DI like this:

services.AddSingleton<IFoo, Foo>();

And let's say the Foo class has a number of other dependencies (mostly repository classes that allow it to load data).

With my current understanding, the Foo instance is not created until it's first used (asked). Is there a way to initialize this class other than the constructor? Like right after ConfigureServices() completes? Or should the initialization code (loading data from db) be done in Foo's constructor?

(It would be nice if this class could load its data before the first use to speed up first time access)

like image 1000
pbz Avatar asked Aug 17 '16 20:08

pbz


3 Answers

Do it yourself during startup.

var foo = new Foo();
services.AddSingleton<IFoo>(foo);

Or "warm it up"

public void Configure(IApplicationBuilder app) 
{
    app.ApplicationServices.GetService<IFoo>();
}

or alternatively

public void Configure(IApplicationBuilder app, IFoo foo) 
{
    ...
}

But this feels just dirty and is more a problem with your design, if you do something that you shouldn't in the constructor. Class instantiation has to be fast and if you do long-running operations within it, you break against a bunch of best practices and need to refactor your code base rather than looking for ways to hack around it

like image 176
Tseng Avatar answered Oct 24 '22 08:10

Tseng


I got the same problem and I find Andrew Lock blog: https://andrewlock.net/running-async-tasks-on-app-startup-in-asp-net-core-3/

He explains how to do this with asp .net core 3, but he also refers to his pages on how to to this with previous version.

like image 27
Jérôme FLAMEN Avatar answered Oct 24 '22 09:10

Jérôme FLAMEN


Lately I've been creating it as an IHostedService if it needs initialization, because to me it seems more logical to let the initialization be handled by the service itself rather than outside of it.

You can even use a BackgroundService instead of IHostedService as it's pretty similar and it only needs the implementation of ExecuteAsync

Here's the documentation for them
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/host/hosted-services

An example of how to add the service so you can inject it directly:

services
    .AddHostedService<MyService>()
    .AddSingleton<MyService>(x => x
        .GetServices<IHostedService>()
        .OfType<MyService>()
        .First());

Example of a simple service:

public class MyService : IHostedService
{
    // This function will be called automatically when the host `starts`
    public async Task StartAsync(CancellationToken cancellationToken)
    {
        // Do initialization logic
    }

    // This function will be called automatically when the host `stops`
    public Task StopAsync(CancellationToken cancellationToken)
    {
        // Do cleanup if needed

        return Task.CompletedTask;
    }
}

Some extension methods I created later on because i needed to use the same pattern again

public static class HostedServiceExtensions
{
    public static IServiceCollection AddHostedServiceAsService<T>(this IServiceCollection services) where T : class, IHostedService
        => services.AddHostedService<T>().AddSingleton(x => x.GetServices<IHostedService>().OfType<T>().First());

    public static IServiceCollection AddHostedServiceAsService<T>(this IServiceCollection services, Func<IServiceProvider, T> factory) where T : class, IHostedService
        => services.AddHostedService(factory).AddSingleton(x => x.GetServices<IHostedService>().OfType<T>().First());
}

Used like

services.AddHostedServiceAsService<MyService>();

// Or like this if you need a factory
services.AddHostedServiceAsService<MyService>(x => new MyService());
like image 5
Moaaz Alkhrfan Avatar answered Oct 24 '22 09:10

Moaaz Alkhrfan