ASP.NET Core 在单例服务上调用异步初始化

25

我有一个服务,该服务在名为InitAsync的方法中异步地从文件读取一些内容。

Translated text:

我有一个服务,该服务在名为InitAsync的方法中异步地从文件读取一些内容。

public class MyService : IService {
    private readonly IDependency injectedDependency;

    public MyService(IDependency injectedDependency) {
        this.injectedDependency = injectedDependency;
    }

    public async Task InitAsync() {
        // async loading from file.
    }
}

现在这个服务已经被注入到我的控制器中。

public class MyController : Controller {
    private readonly IService service;

    public MyController(IService service) {
        this.service = service;
    }
}

现在我想要一个 MyService 的单例实例。并且我想在启动时调用 InitAsync。

public class Startup {
    public void ConfigureServices(IServiceCollection services) {
        ......
        services.AddSingleton<IService, MyService>();
        var serviceProvider = services.BuildServiceProvider();
        // perform async init.
        serviceProvider.GetRequiredService<IService>().InitAsync();
    }
}

发生的情况是在启动时创建了一个MyService实例,并对其调用了InitAsync()。然后当我调用控制器类时,另一个MyService实例被创建并在随后的调用中重复使用。

我需要的是仅初始化1个实例,在启动时对其调用InitAsync(),并使控制器也可以重复使用它。


1
相关链接:https://dev59.com/wVMI5IYBdhLWcg3wg7up#56058498 - Steven
1
相关:https://dev59.com/B-k5XIcBkEYKwwoY9-p1 - Steven
2个回答

30
在启动时创建一个MyService实例并调用其InitAsync()方法。当调用控制器类时,另一个MyService实例被创建并在后续调用中重复使用。调用BuildServiceProvider()时,创建一个单独的IServiceProvider实例,该实例创建其自己的IService单例实例。用于解析MyController提供的IService的IServiceProvider与您自己创建的不同,因此IService本身也是不同的(且未初始化)。我需要初始化一个名为InitAsync()的实例,并在启动时只初始化1个实例,并使其由控制器重用。不要试图在Startup.ConfigureServices中解析和初始化IService,可以在Program.Main中进行。这样可以做到两件事:使用相同的IService实例进行初始化和后续使用;等待对InitAsync的调用,而在您展示的方法中它是“fire-and-forget”的。以下是Program.Main的示例代码:
public static async Task Main(string[] args)
{
    var webHost = CreateWebHostBuilder(args).Build();

    await webHost.Services.GetRequiredService<IService>().InitAsync();

    webHost.Run();
    // await webHost.RunAsync();
}

此代码使用async Main来启用await的使用,构建了IWebHost并使用其IServiceProvider来解析和初始化IService。代码还展示了如何在偏好RunAsync的情况下使用await,因为该方法现在是async


2

您可以使用NuGet HostInitActions 来简单实现此功能。

    public void ConfigureServices(IServiceCollection services)
    {       
        services.AddSingleton<IService, MyService>();

        services.AddAsyncServiceInitialization()
            .AddInitAction<IService>(async (service) =>
            {
                await service.InitAsync();
            });
    }

这个小技巧可以确保你的初始化操作在应用程序启动之前异步执行。另一个好处是,这种方法可以从安装服务到IServiceCollection的任何地方定义初始化操作(例如,在另一个项目中的扩展方法中安装公共接口的内部实现)。这意味着ASP.NET Core项目不需要知道哪个服务需要如何初始化,它仍然会被正确执行。

这仍然作为托管服务创建/启动的一部分进行初始化,对于需要托管服务依赖项进行初始化的事情来说,这已经太晚了。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接