我应该在ASP.NET Core中的哪里执行自定义应用程序初始化步骤?

16

ASP.NET Core框架为我们提供了两个明确定义的初始化位置: 1. Startup.ConfigureServices()方法用于注册DI服务 2. Startup.Configure()方法用于配置中间件管道

但是,对于我的Web应用程序的其他特定初始化步骤呢?如果它们具有需要注入的依赖项,那么应该放在哪里呢?

例如,我需要基于配置文件appsettings.json中指定的连接字符串初始化数据库ORM。因此,这个初始化代码依赖于IConfiguration和可能在Startup.ConfigureServices()期间注册到DI容器中的其他自定义服务。

因此,根据这些文章的建议:

我试图将初始化逻辑封装在单独的类中,然后为IWebHostBuilder创建扩展方法来执行此代码,但是如何使框架注入IConfiguration和其他自定义依赖项到这些扩展方法中呢?此外,在所有依赖项注册后,可以确保此代码将在Startup.ConfigureServices()之后执行吗?
是否有更好或推荐的方法来执行这种任务?
2个回答

27
你可以为IWebHost添加一个扩展方法(而不是IWebHostBuilder),然后使用IWebHost.Services来解析服务。这里是一个检索IConfiguration的示例:
public static class WebHostExtensions
{
    public static IWebHost SomeExtension(this IWebHost webHost)
    {
        var config = webHost.Services.GetService<IConfiguration>();

        // Your initialisation code here.
        // ...

        return webHost;
    }
}

使用该扩展方法的方式看起来像这样:

CreateWebHostBuilder(args)
    .Build()
    .SomeExtension()
    .Run();

如果您需要SomeExtensionasync版本,您可以将上面的链接分开,并await该扩展方法。以下是可能的示例:

public static async Task SomeExtensionAsync(this IWebHost webHost)
{
    var config = webHost.Services.GetService<IConfiguration>();

    // Your initialisation code here with awaits.
    // ...
}

使用方法看起来像这样:

public static async Task Main(string[] args)
{
    var webHost = CreateWebHostBuilder(args)
        .Build();

    await webHost.SomeExtensionAsync();

    webHost.Run();
}

还有,我可以确定这段代码将在所有依赖项注册后,Startup.ConfigureServices()之后执行吗?

使用上面概述的方法,答案是肯定的。


请注意,IWebHost.Services表示IServiceProvider,不支持解析作用域实例。 IConfiguration是单例,所以对于它来说这不是问题,但如果您有作用域依赖关系,则需要在扩展方法内部创建一个明确的作用域。


感谢@Kirk Larkin的解释。如果在这个“用户定义”的初始化过程中出现错误/异常,最佳实践是什么? - ivanpovazan
@povke 我不确定这里是否有“最佳实践”,但我期望在调用SomeExtensionAsync(或者甚至是整个Main函数体)时加上一些try/catch。这可以将异常记录到第三方服务中,例如通知您应用程序启动失败。另外,运行您的应用程序的任何内容都可以将其在stderr上看到的任何输出发送到另一个服务中,以类似的方式通知您。 - Kirk Larkin
是的,谢谢@Kirk Larkin,我理解了那部分内容,但我更好奇的是应该如何处理Web主机?它应该启动还是只从应用程序中选择退出?我知道这取决于托管环境,而让我担心/困惑的是这个主题。你对此有什么看法吗? :) - ivanpovazan
1
@povke 我自己没有遇到过这个问题,但我认为我会跳过主机并让进程以非零状态代码退出。我预计解决方案可能因不同的部署场景而异。 - Kirk Larkin
非常感谢,我可能需要在Azure上测试这两种情况,以查看其表现如何。 - ivanpovazan

5
Program.cs 文件中,你的 Main 方法中有以下代码:
public static void Main(string[] args)
{
    CreateWebHostBuilder(args).Build().Run();
}

Build()运行之后,您将拥有一个完全配置好的主机。因此,您可以像下面这样简单地执行操作:

var host = CreateWebHostBuilder(args).Build();

// do something with host

host.Run();

主机有一个成员Services,它是IServiceProvider的实例,因此您可以从中获取所需的任何服务,例如:
var config = host.Services.GetRequiredService<IConfiguration>();

请注意,此时还没有内在的作用域,因此如果您需要有作用域的服务,则需要创建一个:

using (var scope = host.Services.CreateScope())
{
    var myScopedService = scope.ServiceProvider.GetRequiredService<MyScopedService>();
    // do something with myScopedService
}

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