原文: https://andrewlock.net/ihostingenvironment-vs-ihost-environment-obsolete-types-in-net-core-3/
做者: Andrew Lock
譯者: Lamond Luhtml
本篇是如何升級到ASP.NET Core 3.0
系列文章的第二篇。web
IHostingEnvironment
VS IHostEnvironment
- .NET Core 3.0中的廢棄類型(本篇)在本篇博客中,我將描述與以前版本相比,ASP.NET Core 3.0中已經被標記爲廢棄的類型。我將解釋一下爲何這些類型被廢棄了,它們的替換類型是什麼,以及你應該何時使用它們。c#
在ASP.NET Core 2.1中引入了新的通用主機(Generic Host), 它是藉助Microsoft.Extension.*
程序集來進行程序配置,依賴注入,以及日誌記錄來構建非HTTP應用的一種方式。 雖然這是一個至關不錯的點子,可是引入主機抽象在基礎上與ASP.NET Core使用的HTTP主機不兼容。這致使了多種命名空間的衝突與不兼容,因此在ASP.NET Core 2.x版本中,我一直儘可能不使用通用主機。服務器
在ASP.NET Core 3.0中,開發人員做出了巨大的努力,將Web主機與通用主機兼容起來。ASP.NET Core的Web主機如今能夠做爲IHostedService
運行在通用主機中,重複抽象的問題(ASP.NET Core中使用一套抽象,通用主機使用另外一套抽象)獲得了根本解決。app
固然,這還不是所有。當你從ASP.NET Core 2.x升級到3.0, ASP.NET Core 3.0並不強迫你當即使用新的通用主機。若是你願意,你能夠繼續使用舊的WebHostBuilder
,而不使用新的HostBuilder
。雖然在ASP.NET Core 3.0的官方文檔中一直暗示這是必須的,可是在當前的階段,這是一個可選配置,若是你須要,能夠繼續使用Web主機,而不使用通用主機。asp.net
PS: 不過我仍是建議你將可能將
HostBuilder
做爲你將來的升級計劃。我可是在將來的某個時間點WebHostBuilder
將被移除,即便如今它尚未被標記爲[Obsolete]
。ide
做爲重構的通用主機的一部分,一些在以前版本中重複的類型被標記爲廢棄了,一些新的類型被引入了。在這些類型中,最好的例子就是IHostingEnvironment
。visual-studio
IHostingEnvironment
VS IHostEnvironment
VS IWebHostEnviornment
IHostingEnvironment
是.NET Core 2.x中最讓人討厭的一個接口,由於它存在於兩個命名空間中, Microsoft.AspNetCore.Hosting
和Microsoft.Extensions.Hosting
.這兩個接口有少量不一樣,且不兼容。測試
namespace Microsoft.AspNetCore.Hosting { public interface IHostingEnvironment { string EnvironmentName { get; set; } string ApplicationName { get; set; } string WebRootPath { get; set; } IFileProvider WebRootFileProvider { get; set; } string ContentRootPath { get; set; } IFileProvider ContentRootFileProvider { get; set; } } } namespace Microsoft.Extensions.Hosting { public interface IHostingEnvironment { string EnvironmentName { get; set; } string ApplicationName { get; set; } string ContentRootPath { get; set; } IFileProvider ContentRootFileProvider { get; set; } } }
之因此有兩個同名接口是有歷史緣由的。AspNetCore
版本的接口已經存在了很長時間了,在ASP.NET Core 2.1版本中,通用主機引入了Extensions
版本。Extensions
版本沒有提供用於服務靜態文件的wwwroot
目錄的概念(由於它承載的是非HTTP服務)。因此你可能已經注意到Extensions
缺乏了WebRootFileProvider
和WebRootPath
兩個屬性。ui
出於向後兼容的緣由,這裏須要一個單獨的抽象。可是,這種作法真正使人討厭的後果之一是沒法編寫用於通用主機和ASP.NET Core的擴展方法。
在ASP.NET Core 3.0中,上述的兩個接口都已經被標記爲廢棄了。你依然可使用它們,可是在編譯的時候,你會獲得一些警告。相對的,兩個新的接口被引入進來: IHostEnvironment
和IWebHostEnvironment
。雖然他們出如今不一樣的命名空間中,可是如今它們有了不一樣的名字,並且使用了繼承關係。
namespace Microsoft.Extensions.Hosting { public interface IHostEnvironment { string EnvironmentName { get; set; } string ApplicationName { get; set; } string ContentRootPath { get; set; } IFileProvider ContentRootFileProvider { get; set; } } } namespace Microsoft.AspNetCore.Hosting { public interface IWebHostEnvironment : IHostEnvironment { string WebRootPath { get; set; } IFileProvider WebRootFileProvider { get; set; } } }
這個層次關係更容易理解了,避免了重複,而且意味着接收通用主機版本宿主環境抽象(IHostEnvironment
)的方法如今也能夠接收web版本(IWebHostEnvironment
)的抽象了。在幕後,IHostEnvironment
和IWebHostEnvironment
的實現是相同的 - 除了舊接口,他們還實現了新接口。
例如,ASP.NET Core的實現類以下:
namespace Microsoft.AspNetCore.Hosting { internal class HostingEnvironment : IHostingEnvironment, Extensions.Hosting.IHostingEnvironment, IWebHostEnvironment { public string EnvironmentName { get; set; } = Extensions.Hosting.Environments.Production; public string ApplicationName { get; set; } public string WebRootPath { get; set; } public IFileProvider WebRootFileProvider { get; set; } public string ContentRootPath { get; set; } public IFileProvider ContentRootFileProvider { get; set; } } }
那麼你到底應該使用哪一個接口呢?最簡單的答案是"儘量使用IHostEnvironment
接口"。
可是詳細來講,狀況有不少。。。
儘量是使用IHostEnviornment
接口,但你須要訪問WebRootPath
和WebRootFileProvider
兩個屬性的時候,請使用IWebHostEnvironment
接口。
使用IHostEnvironment
接口。你的類庫依然能夠在ASP.NET Core 3.0應用中可用。
和以前同樣,儘可能使用IHostEnvironment
接口,由於你的類庫可能不只使用在ASP.NET Core應用中,還有可能使用在其餘通用主機應用中。然而,若是你須要訪問IWebHostEnvironment
接口中的額外屬性,那麼你可能不得不更新你的類庫,讓它面向netcoreapp3.0
,而不是netstandard2.0
, 而且添加<FreameworkReference>
元素配置。
這種場景比較難處理,基本上你有兩種可選的方案:
Microsoft.AspNetCore
版本的IHostingEnvironment
。它在2.x和3.0應用中均可以正常工做,你只須要在後續版本中中止使用便可。#ifdef
條件編譯指令,針對ASP.NET Core 3.0使用IHostEnvironment
接口,針對ASP.NET Core 2.x使用IHostingEnviornment
接口。IApplicationLifetime
VS IHostApplicationLifetime
與IHostingEnvironment
接口類似,IApplicationLifetime
接口也有命名空間的衝突問題。和以前的例子相同,這兩個接口分別存在於Microsoft.Extensions.Hosting
和Microsoft.AspNetCore.Hosting
中。可是在這個例子中,這兩個接口是徹底一致的。
// 與Microsoft.AspNetCore.Hosting中的定義徹底一致 namespace Microsoft.Extensions.Hosting { public interface IApplicationLifetime { CancellationToken ApplicationStarted { get; } CancellationToken ApplicationStopped { get; } CancellationToken ApplicationStopping { get; } void StopApplication(); } }
如你所料,這種重複是向後兼容的徵兆。在.NET Core 3.0中新的接口IHostApplicationLifetime
被引入,該接口僅在Microsoft.Extensions.Hosting
命名空間中定義,可是在通用主機和ASP.NET Core應用中均可以使用。
namespace Microsoft.Extensions.Hosting { public interface IHostApplicationLifetime { CancellationToken ApplicationStarted { get; } CancellationToken ApplicationStopping { get; } CancellationToken ApplicationStopped { get; } void StopApplication(); } }
一樣的,這個接口和以前版本是徹底一致的。ApplicationLifetime
類型在通用主機項目的啓動和關閉中扮演了很是重要的角色。很是有趣的是,在Microsoft.AspNetCore.Hosting
中沒有一個真正等價的類型,Extensions
版本的接口處理了兩種不一樣的實現。AspNetCore
命名空間中惟一的實現是一個簡單的封裝類,類型將實現委託給了一個做爲通用主機部分被添加的ApplicationLifetime
對象中。
namespace Microsoft.AspNetCore.Hosting { internal class GenericWebHostApplicationLifetime : IApplicationLifetime { private readonly IHostApplicationLifetime _applicationLifetime; public GenericWebHostApplicationLifetime( IHostApplicationLifetime applicationLifetime) { _applicationLifetime = applicationLifetime; } public CancellationToken ApplicationStarted => _applicationLifetime.ApplicationStarted; public CancellationToken ApplicationStopping => _applicationLifetime.ApplicationStopping; public CancellationToken ApplicationStopped => _applicationLifetime.ApplicationStopped; public void StopApplication() => _applicationLifetime.StopApplication(); } }
幸運的是,選擇使用哪個接口,比選擇託管環境(Hosting Environment)要簡單的多。
使用IHostApplicationLifetime
接口。你只須要引用Microsoft.Extensions.Hosting.Abstractions
, 便可以在全部應用中使用。
如今,你可能又會陷入困境:
Microsoft.Extensions
版本的IApplicationLifetime
。它在2.x和3.0應用中均可以正常使用,可是在將來的版本中,你將不得不中止使用它#ifdef
條件編譯指令,針對ASP.NET Core 3.0使用IHostApplicationLifetime
接口,針對ASP.NET Core 2.x使用IApplicationLifetime
接口。幸運的是,IApplicationLifetime
接口一般使用的比IHostingEnvironment
接口少的多,因此你可能不會在此遇到過多的困難。
IWebHost
VS IHost
這裏有一件事情可能讓你驚訝,IWebHost
接口沒有被更新,它沒有繼承ASP.NET Core 3.0中的IHost
。類似的,IWebHostBuilder
也沒有繼承自IHostBuilder
。它們依然是徹底獨立的接口, 一個只工做在ASP.NET Core中,一個只工做在通用主機中。
幸運的是,這也沒有關係。如今ASP.NET Core 3.0已經被重構使用通用主機的抽象接口, 你能夠編寫使用通用主機IHostBuilder
抽象的方法,並在ASP.NET Core和通用主機應用中共享它們。若是你須要進行ASP.NET Core的特定操做,你能夠依然使用IWebHostBuilder
接口。
例如,你能夠編寫以下的擴展方法,一個使用IHostBuilder
, 一個使用IWebHostBuilder
:
public static class ExampleExtensions { public static IHostBuilder DoSomethingGeneric(this IHostBuilder builder) { // 添加通用主機配置 return builder; } public static IWebHostBuilder DoSomethingWeb(this IWebHostBuilder builder) { // 添加Web託管配置 return builder; } }
其中一個方法在通用主機上進行某些配置(列入,使用依賴注入註冊某些服務),在另一個方法中對IWebHostBuilder
進行某種配置,例如你可能會爲Kestrel服務器設置一些默認值。
若是你在建立了一個全新的ASP.NET Core 3.0應用,你的Program.cs
文件看起來應該是以下代碼:
public class Program { public static void Main(string[] args) => CreateHostBuilder(args).Build().Run(); public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults(webBuilder => { webBuilder .UseStartup<Startup>(); }); }
你能夠添加針對兩個擴展方法的調用。一個在通用IHostBuilder
上調用,另外一個在ConfigWebHostDefaults()
方法中,針對IWebHostBuilder
調用
public class Program { public static void Main(string[] args) => CreateHostBuilder(args).Build().Run(); public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .DoSomethingGeneric() // IHostBuilder擴展方法 .ConfigureWebHostDefaults(webBuilder => { webBuilder .DoSomethingWeb() // IWebHostBuilder擴展方法 .UseStartup<Startup>(); }); }
在ASP.NET Core 3.0中,你能夠對兩種構建器類型進行調用,這意味着,你如今能夠僅依賴通用主機的抽象,就能夠在ASP.NET Core應用中複用它們。而後,你能夠將ASP.NET Core的特性行爲放在頂層,而沒必要像2.x中同樣重複方法。
在本文中,咱們討論了ASP.NET Core 3.0中一些被標記爲廢棄的類型,它們被移動到哪裏去了,以及這麼作的緣由。若是你正在將一個應用升級到ASP.NET Core 3.0, 你並不須要立刻替換它們,由於他們如今的行爲依然相同,可是在未來的版本中會被替換掉,所以若是能夠的話,最好對其進行更新。在某些場景中,它還使你的應用之間共享代碼更加容易,所以值得研究一下。