【问题标题】:Where should I perform custom application initialization steps in ASP.NET Core?我应该在哪里执行 ASP.NET Core 中的自定义应用程序初始化步骤?
【发布时间】:2019-04-16 12:04:35
【问题描述】:

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

但是其他特定于我的 Web 应用程序的初始化步骤呢?这些应该去哪里,特别是如果它们有需要注入的依赖项?

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

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

我尝试将初始化逻辑封装在单独的类中,然后为IWebHostBuilder 创建将执行此代码的扩展方法,但是如何使框架将IConfiguration 和其他自定义依赖项注入此扩展方法中?另外,我能确定这段代码会在Startup.ConfigureServices()之后所有的依赖都注册好后执行吗?

是否有更好或推荐的方法来执行此类任务?

【问题讨论】:

    标签: c# asp.net-core


    【解决方案1】:

    您可以为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 代表 root IServiceProvider,它不支持解析 作用域 实例。 IConfiguration 是一个单例,所以这不是问题,但是如果你有 scoped 依赖项,你需要在你的扩展方法中创建一个显式的范围。

    【讨论】:

    • 感谢@Kirk Larkin 的解释。我想知道如果在这个“用户定义”初始化过程中发生错误/异常,最佳做法是什么?
    • @povke 我不确定这里是否有“最佳实践”,但我希望在调用SomeExtensionAsync 时会出现某种try/catch(甚至是整个Main 正文)。这可以将异常记录到第三方服务,例如,通知您应用程序无法启动。或者,无论您的应用运行什么,都可能将它在 stderr 上看到的任何输出发送到另一个服务,以类似的方式通知您。
    • 是的,谢谢@Kirk Larkin 我理解那部分,但我更好奇应该如何处理虚拟主机?它应该启动还是只是选择退出应用程序?我知道这取决于托管环境,让我担心/困惑的是topic。你对此有什么意见吗? :)
    • @povke 我自己没有遇到过这个问题,但我想我会跳过主机并让进程以非零状态码退出。我希望针对不同的部署场景,解决方案可能会有所不同。
    • 非常感谢,我可能需要在 Azure 上测试这两种方案,看看它的表现如何
    【解决方案2】:

    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
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-03-26
      • 1970-01-01
      • 1970-01-01
      • 2021-01-29
      • 2018-07-07
      • 2011-02-12
      • 2012-11-23
      相关资源
      最近更新 更多