原文: 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, 你并不须要立刻替换它们,由于他们如今的行为依然相同,可是在未来的版本中会被替换掉,所以若是能够的话,最好对其进行更新。在某些场景中,它还使你的应用之间共享代码更加容易,所以值得研究一下。