日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > asp.net >内容正文

asp.net

ASP.NET Core 3.x启动时运行异步任务(二)

發布時間:2023/12/4 asp.net 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 ASP.NET Core 3.x启动时运行异步任务(二) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

這一篇是接著前一篇在寫的。如果沒有看過前一篇文章,建議先去看一下前一篇,這兒是傳送門

?

一、前言

前一篇文章,我們從應用啟動時異步運行任務開始,說到了必要性,也說到了幾種解決方法,及各自的優缺點。最后,還提出了一個比較合理的解決方法:通過在Program.cs里加入代碼,來實現IWebHost啟動前運行異步任務。

實現的代碼再貼一下:

public?class?Program {public?static?async?Task?Main(string[]?args){IWebHost?webHost?=?CreateWebHostBuilder(args).Build();using?(var?scope?=?webHost.Services.CreateScope()){var?myDbContext?=?scope.ServiceProvider.GetRequiredService<MyDbContext>();await?myDbContext.Database.MigrateAsync();}await?webHost.RunAsync();}public?static?IWebHostBuilder?CreateWebHostBuilder(string[]?args)?=>WebHost.CreateDefaultBuilder(args).UseStartup<Startup>(); }

這個方法是有效的。但是,也會有一點不足。

從.Net Core的最簡規則來說,我們不應該在Program.cs中加入其它代碼。當然,我們可以把這部分代碼轉到一個外部類中,但最后也必須手動加入到Program.cs中。尤其是在多個應用中,使用相同的模式時,這種方式會很麻煩。

也許,我們可以采用向DI容器中注入啟動任務?

二、向DI容器中注入啟動任務

這種方式,是基于IStartupFilter和IHostedService兩個接口,通過這兩個接口可以向依賴注入容器中注冊類。

?

首先,我們為啟動任務創建一個簡單接口:

public?interface?IStartupTask {Task?ExecuteAsync(CancellationToken?cancellationToken?=?default); }

再建一個擴展方法,用來向DI容器注冊啟動任務:

public?static?class?ServiceCollectionExtensions {public?static?IServiceCollection?AddStartupTask<T>(this?IServiceCollection?services)where?T?:?class,?IStartupTask=>?services.AddTransient<IStartupTask,?T>(); }

最后,再建一個擴展方法,在應用啟動時,查找所有已注冊的IStartupTask,按順序執行他們,然后啟動IWebHost:

public?static?class?StartupTaskWebHostExtensions {public?static?async?Task?RunWithTasksAsync(this?IHost?webHost,?CancellationToken?cancellationToken?=?default){var?startupTasks?=?webHost.Services.GetServices<IStartupTask>();foreach?(var?startupTask?in?startupTasks){await?startupTask.ExecuteAsync(cancellationToken);}await?webHost.RunAsync(cancellationToken);} }

這樣就齊活了。

?

還是用一個例子來看看這個方式的具體應用。

三、示例 - 數據遷移

實現IStartupTask其實和實現IStartupFilter很相似,可以從DI容器中注入。如果需要考慮作用域,還可以注入IServiceProvider,并手動創建作用域。

?

例子中,數據遷移類可以寫成這樣:

public?class?MigratorStartupFilter:?IStartupTask {private?readonly?IServiceProvider?_serviceProvider;public?MigratorStartupFilter(IServiceProvider?serviceProvider){_serviceProvider?=?serviceProvider;}public?async?Task?ExecuteAsync(CancellationToken?cancellationToken?=?default){using(var?scope?=?_seviceProvider.CreateScope()){var?myDbContext?=?scope.ServiceProvider.GetRequiredService<MyDbContext>();await?myDbContext.Database.MigrateAsync();}} }

下面,把任務注入到ConfigureServices()中:

public?void?ConfigureServices(IServiceCollection?services) {services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);services.AddStartupTask<MigrationStartupTask>(); }

最后,用上一節中的擴展方法RunWithTasksAsync()來替代Program.cs中的Run():

public?class?Program {public?static?async?Task?Main(string[]?args){//?await?CreateWebHostBuilder(args).Build().RunAsync();await?CreateWebHostBuilder(args).Build().RunWithTasksAsync();}public?static?IWebHostBuilder?CreateWebHostBuilder(string[]?args)?=>WebHost.CreateDefaultBuilder(args).UseStartup<Startup>(); }

?

從功能上來說,跟上一篇的代碼區別不大,但這樣的寫法,又多了一些優點:

  • 任務代碼放到了Program.cs之外。這符合微軟的建議,也更容易理解;

  • 任務放到了DI容器中,這樣更容易添加額外的任務;

  • 如果沒有額外任務,這個代碼和標準的Run()一樣,所以這個代碼可以獨立成一個模板。

  • 簡單來說,使用RunWithTasksAsync()后,可以輕松地向DI容器添加額外的任務,而不需要任何其它的更改。

    ?

    滿意了嗎?好像感覺還差一點點…

    四、不夠完美的地方

    如果要照著完美去做,好像還差一點點。

    這個一點點是在于:任務現在運行在IConfiguration和DI容器配置完成后,IStartupFilters運行和中間件管道配置完成之前。換句話說,如果任務需要依賴于IStartupFilters,那這個方案行不通。

    在大多數情況下,這沒什么問題。以我自己的經驗來看,好像沒有什么功能需要依賴于IStartupFilters。但作為一個框架類的代碼,需要考慮這種情況發生的可能性。

    以目前的方案來說,好像還沒辦法解決。

    應用啟動時,當調用WebHost.Run()時,是內部調用WebHost。看一下StartAsync()的簡化代碼:

    public?virtual?async?Task?StartAsync(CancellationToken?cancellationToken?=?default) {_logger?=?_applicationServices.GetRequiredService<ILogger<WebHost>>();var?application?=?BuildApplication();_applicationLifetime?=?_applicationServices.GetRequiredService<IApplicationLifetime>()?as?ApplicationLifetime;_hostedServiceExecutor?=?_applicationServices.GetRequiredService<HostedServiceExecutor>();var?diagnosticSource?=?_applicationServices.GetRequiredService<DiagnosticListener>();var?httpContextFactory?=?_applicationServices.GetRequiredService<IHttpContextFactory>();var?hostingApp?=?new?HostingApplication(application,?_logger,?diagnosticSource,?httpContextFactory);await?Server.StartAsync(hostingApp,?cancellationToken).ConfigureAwait(false);_applicationLifetime?.NotifyStarted();await?_hostedServiceExecutor.StartAsync(cancellationToken).ConfigureAwait(false); }

    如果我們希望任務是加在BuildApplication()調用和Server.StartAsync()的調用之間,該怎么辦?

    這段代碼能給出答案:我們需要裝飾IServer。¨K16K 首先,我們替換IServer的實現:¨G8G 在這段代碼中,我們攔截StartAsync()調用并注入任務,然后回到內置處理。下面是對應的擴展代碼:¨G9G 這個擴展代碼做了兩件事:在DI容器中注冊了IStartupTask,并裝飾了之前注冊的IServer實例。裝飾方法Decorate()我略過了,有興趣的可以去了解一下 - 裝飾模式。?Program.cs的代碼和第三節的代碼相同,略過。&emsp; 我們終于做到了在應用程序完全構建完成后去執行我們的任務,包括IStartupFilters`和中間件管道。

    現在的流程,類似于下面這個微軟官方的圖:

    (全文完)

    總結

    以上是生活随笔為你收集整理的ASP.NET Core 3.x启动时运行异步任务(二)的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。