IHostingEnvironment VS IHostEnvironment - .NET Core 3.0中的廢棄型別
原文: https://andrewlock.net/ihostingenvironment-vs-ihost-environment-obsolete-types-in-net-core-3/
作者: Andrew Lock
譯者: Lamond Lu
本篇是如何升級到ASP.NET Core 3.0
系列文章的第二篇。
- Part 1 - 將.NET Standard 2.0類庫轉換為.NET Core 3.0類庫
- Part 2 -
IHostingEnvironment
VSIHostEnvironment
- .NET Core 3.0中的廢棄型別(本篇) - Part 3 - 避免在ASP.NET Core 3.0啟動時注入服務
- Part 4 - 將終端中介軟體轉換為ASP.NET Core 3.0中的節點路由
- Part 5 - 將整合測試的轉換為NET Core 3.0
在本篇部落格中,我將描述與之前版本相比,ASP.NET Core 3.0中已經被標記為廢棄的型別。我將解釋一下為什麼這些型別被廢棄了,它們的替換型別是什麼,以及你應該什麼時候使用它們。
ASP.NET Core與通用主機(Generic Host)合併
在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中使用一套抽象,通用主機使用另一套抽象)得到了根本解決。
當然,這還不是全部。當你從ASP.NET Core 2.x升級到3.0, ASP.NET Core 3.0並不強迫你立即使用新的通用主機。如果你願意,你可以繼續使用舊的WebHostBuilder
,而不使用新的HostBuilder
。雖然在ASP.NET Core 3.0的官方文件中一直暗示這是必須的,但是在當前的階段,這是一個可選配置,如果你需要,可以繼續使用Web主機,而不使用通用主機。
PS: 不過我還是建議你將可能將
HostBuilder
作為你未來的升級計劃。我但是在未來的某個時間點WebHostBuilder
將被移除,即使現在它還沒有被標記為[Obsolete]
。
作為重構的通用主機的一部分,一些在之前版本中重複的型別被標記為廢棄了,一些新的型別被引入了。在這些型別中,最好的例子就是IHostingEnvironment
。
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
兩個屬性。
出於向後相容的原因,這裡需要一個單獨的抽象。但是,這種做法真正令人討厭的後果之一是無法編寫用於通用主機和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
介面"。
但是詳細來說,情況有很多。。。
如果你正在編寫的ASP.NET Core 3.0的應用
儘可能是使用IHostEnviornment
介面,但你需要訪問WebRootPath
和WebRootFileProvider
兩個屬性的時候,請使用IWebHostEnvironment
介面。
如果你正在編寫一個在通用主機和.NET Core 3.0專案中使用的類庫
使用IHostEnvironment
介面。你的類庫依然可以在ASP.NET Core 3.0應用中可用。
如果你正在編寫一個在ASP.NET Core 3.0應用中使用的類庫
和之前一樣,儘量使用IHostEnvironment
介面,因為你的類庫可能不僅使用在ASP.NET Core應用中,還有可能使用在其他通用主機應用中。然而,如果你需要訪問IWebHostEnvironment
介面中的額外屬性,那麼你可能不得不更新你的類庫,讓它面向netcoreapp3.0
,而不是netstandard2.0
, 並且新增<FreameworkReference>
元素配置。
如果你正在編寫一個在ASP.NET Core 2.x和3.0中使用的類庫
這種場景比較難處理,基本上你有兩種可選的方案:
- 你可以繼續使用
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)要簡單的多。
如果你正在編寫一個.NET Core 3.0或者ASP.NET Core 3.0應用或者類庫
使用IHostApplicationLifetime
介面。你只需要引用Microsoft.Extensions.Hosting.Abstractions
, 即可以在所有應用中使用。
如果你在編寫一個被ASP.NET Core 2.x和3.0應用共同使用的類庫
現在,你可能又會陷入困境:
- 你可以繼續使用
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, 你並不需要馬上替換它們,因為他們現在的行為依然相同,但是在將來的版本中會被替換掉,因此如果可以的話,最好對其進行更新。在某些場景中,它還使你的應用之間共享程式碼更加容易,因此值得研究一下