原文: https://andrewlock.net/ihostingenvironment-vs-ihost-environment-obsolete-types-in-net-core-3/
做者: Andrew Lock
譯者: Lamond Luhtml
本篇是如何升級到ASP.NET Core 3.0
系列文章的第二篇。java
IHostingEnvironment
VS IHostEnvironment
- .NET Core 3.0中的廢棄類型(本篇)在本篇博客中,我將描述與以前版本相比,ASP.NET Core 3.0中已經被標記爲廢棄的類型。我將解釋一下爲何這些類型被廢棄了,它們的替換類型是什麼,以及你應該何時使用它們。web
在ASP.NET Core 2.1中引入了新的通用主機(Generic Host), 它是藉助Microsoft.Extension.*
程序集來進行程序配置,依賴注入,以及日誌記錄來構建非HTTP應用的一種方式。 雖然這是一個至關不錯的點子,可是引入主機抽象在基礎上與ASP.NET Core使用的HTTP主機不兼容。這致使了多種命名空間的衝突與不兼容,因此在ASP.NET Core 2.x版本中,我一直儘可能不使用通用主機。c#
在ASP.NET Core 3.0中,開發人員做出了巨大的努力,將Web主機與通用主機兼容起來。ASP.NET Core的Web主機如今能夠做爲IHostedService
運行在通用主機中,重複抽象的問題(ASP.NET Core中使用一套抽象,通用主機使用另外一套抽象)獲得了根本解決。服務器
固然,這還不是所有。當你從ASP.NET Core 2.x升級到3.0, ASP.NET Core 3.0並不強迫你當即使用新的通用主機。若是你願意,你能夠繼續使用舊的WebHostBuilder
,而不使用新的HostBuilder
。雖然在ASP.NET Core 3.0的官方文檔中一直暗示這是必須的,可是在當前的階段,這是一個可選配置,若是你須要,能夠繼續使用Web主機,而不使用通用主機。app
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的實現類以下:
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; } } }namespace Microsoft.AspNetCore.Hosting { internal
那麼你到底應該使用哪一個接口呢?最簡單的答案是"儘量使用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, 你並不須要立刻替換它們,由於他們如今的行爲依然相同,可是在未來的版本中會被替換掉,所以若是能夠的話,最好對其進行更新。在某些場景中,它還使你的應用之間共享代碼更加容易,所以值得研究一下。