对比IHostingEnvironment与IHostEnvironment .NET及Core 3.0中的过时类型

译者: Akini Xu

原文: IHostingEnvironment vs IHostEnvironment - obsolete types in .NET Core 3.0

作者: Andrew Lock

此文是 升级至 ASP.NET Core 3.0 第2篇:

  1. 转换.NET Standard 2.0类库到.NET Core 3.0
  2. 对比IHostingEnvironment与IHostEnvironment .NET及Core 3.0中的过时类型
  3. 不要在Startup类的构造函数中使用依赖注入
  4. 将末端中间件转换为端点路由
  5. 将集成测试升级至.NET Core 3.0

在本文中,我将介绍.NET Core 3.0中已过时的类型与ASP.NET Core之间的差异。 说明它们变化的原因,并介绍什么时候及什么地方使用它们。

ASP.NET Core与通用主机合并

ASP.NET Core 2.1引入了一个新对象通用主机(GenericHost),它可以用来构建非HTTP的应用程序。Microsoft.Extensions.*的相关扩展方法可以用于应用程序配置,依赖项注入和日志记录等。 尽管这是一个非常不错的想法,但是在基础架构上,通用主机定义的一些抽象对象与Web主机(ASP.NET Core中使用)根本不兼容。 这导致了名称空间冲突和不兼容,大多数情况下,我都避免使用通用主机。

在ASP.NET Core 3.0中,开发团队重构了Web主机的基础架构,以使其与通用主机兼容。 合并了之前的2个不同主机下的抽象对象(ASP.NET Core下的Web主机抽象对象与通用主机的抽象对象),ASP.NET Core Web主机可以作为IHostedService在通用主机之上运行。

从2.x升级到3.0时,ASP.NET Core 3不会强迫您立即转换为新的基于通用主机方式。 可以继续使用WebHostBuilder而不是HostBuilder迁移文档上提示是必需的,但实际上,如果您出于某种原因希望继续使用它。

如果可能的话,我建议您升级使用HostBuilder。虽然现在WebHostBuilder未被标记为已过时([Obsolete]),但是我认为在将来某个版本,它会被完全删除。

通用主机重构后,以前一些重复定义的类型已被标记为过时,并且引入了新类型。 最好的例子是IHostingEnvironment。

IHostingEnvironment,IHostEnvironment,IWebHostEnviornment3者对比

IHostingEnvironment是.NET Core 2.x中最令人讨厌的接口之一,因为它同时存在于Microsoft.AspNetCore.HostingMicrosoft.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; }
}
}

之所以存在2个接口的原因是,Microsoft.AspNetCore.Hosting.IHostingEnvironment早已存在,但Microsoft.Extensions.Hosting.IHostingEnvironment是随ASP.NET Core 2.1中的通用主机一起引入的。Microsoft.Extensions.Hosting.IHostingEnvironment没有关于静态文件wwwroot目录的相关属性(因为它用于承载非HTTP服务),因此它缺少WebRootFileProviderWebRootPath属性。

出于向后兼容的原因,需要单独的抽象。 但是,这样做真正令人烦恼的结果是,无法编写同时适用于此接口2个版本的扩展方法。

在ASP.NET Core 3.0中,这两个接口都被标记为过时的。 您仍然可以使用它们,但是在编译时会收到警告。 同时引入了两个新接口:IHostEnvironmentIWebHostEnvironment。 尽管它们仍位于各自独立的命名空间下,但接口名词是不一样的,IWebHostEnvironment继承自IHostEnvironment

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)上任然适用。 本质上,IHostEnvironmentIWebHostEnvironment的实现是相同的,另外还实现了新接口。 例如,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应用程序时

尽可能使用IHostEnvironment,当需要访问WebRootPathWebRootFileProvider属性时使用IWebHostEnvironment

构建被通用主机或.NET Core使用的类库项目时

使用IHostEnvironment。 您的类库仍可与ASP.NET Core 3.0应用程序一起使用。

构建被ASP.NET Core 3.0应用程序使用的类库项目时

和之前一样,最好使用IHostEnvironment,因为您的类库可能会被其他通用宿主应用程序使用,而不仅仅针对ASP.NET Core应用程序。 但如果需要访问IWebHostEnvironment的其他属性,则必须将类库目标框架改为netcoreapp3.0而不是netstandard2.0,并添加一个元素,如之前的文章中所述。

构建同时被ASP.NET Core 2.x和ASP.NET Core 3.0使用的类库项目时

这种情况,有2种选择:

  • 继续使用Microsoft.AspNetCore版本的IHostingEnvironment。 它可以在2.x和3.0中正常工作,如果将来的版本不支持了,再停止使用它即可。
  • 使用条件编译#if,在ASP.NET Core 3.0中引入IHostEnvironment,在ASP.NET Core 2.0中引入IHostingEnvironment编译即可。

IApplicationLifetime对比IHostApplicationLifetime

与前面的情况一样,IApplicationLifetime接口也存在冲突问题。 Microsoft.Extensions.HostingMicrosoft.AspNetCore.Hosting中都存在此接口。 2个接口都是一样的:

namespace Microsoft.AspNetCore.Hosting
{
public interface IApplicationLifetime
{
CancellationToken ApplicationStarted { get; }
CancellationToken ApplicationStopped { get; }
CancellationToken ApplicationStopping { get; }
void StopApplication();
}
}

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();
}
}

同样,此接口与以前的版本相同,.NET Core 3.0两个版本接口实现都为ApplicationLifetime。 正如之前有关启动过程的文章中所讨论的那样,ApplicationLifetime对象在通用主机的启动和关闭过程中起着关键作用。 Microsoft.AspNetCore.Hosting中则没有与之等效的对象(通过扩展方法方式的有)。 在AspNetCore中唯一的实现是一个简单的包装类型,该类型对ApplicationLifetime进行了简单封装,上相关委托都转发至通用主机的IHostApplicationLifetime接口实现上。

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();
}
}

应用程序生命周期的接口的选择,要比IHostingEnvironment的选择容易很多。

构建.NET Core 3.0或ASP.NET Core 3.0应用程序或类库时

使用IHostApplicationLifetime。 它仅需要引用Microsoft.Extensions.Hosting.Abstractions,并且可在所有应用程序中使用

构建同时被ASP.NET Core 2.x和ASP.NET Core 3.0使用的类库项目时

还有2中方法可以选择:

  • 继续使用Microsoft.Extensions版本的IApplicationLifetime。 它可以在2.x和3.0中正常工作,如果将来的版本不支持了,再停止使用它即可。
  • 使用条件编译#if,在ASP.NET Core 3.0中引入IHostApplicationLifetime,在ASP.NET Core 2.0中引入IApplicationLifetime编译即可。

幸运的是,与IHostingEnvironment相比,IApplicationLifetime的使用频率通常要低得多,因此使用IApplicationLifetime不会有太多困难。

IWebHost对比IHost

IWebHost接口还没有继承自ASP.NET Core 3.0中的IHost。 同样,IWebHostBuilder也没有继承自IHostBulider。 它们仍然是完全独立的接口-一个用于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)
{
// ... add generic host configuration
return builder;
}

public static IWebHostBuilder DoSomethingWeb(this IWebHostBuilder builder)
{
// ... add web host configuration
return builder;
}
}

第一个方法在通用主机上进行某些配置(例如,它使用DI注册某些服务),第二个方法在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调用DoSomethingGeneric扩展方法,再在ConfigureWebHostDefaults()内部的IWebHostBuilder调用DoSomethingWeb扩展方法,通过这种方式,可以同时对两种主机类型进行相关配置。

public class Program
{
public static void Main(string[] args) => CreateHostBuilder(args).Build().Run();

public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.DoSomethingGeneric() // IHostBuilder extension method
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder
.DoSomethingWeb() // IWebHostBuilder extension method
.UseStartup<Startup>();
});
}

可以在ASP.NET Core 3.0中,对两种Builder类型进行调用意味着,可以创建仅依赖于通用主机抽象类型的类库,并在ASP.NET Core应用中引用它们。

总结

在本文中,我讨论了ASP.NET Core 3.0中一些已过时的类型,它们的替代类型以及它们的过时原因。 如果要将应用程序升级为ASP.NET Core 3.0,则不必替换它们,因为它们提供的方法和之前的版本还是一样的。 但是在以后的版本中,这些过时类型会被移除,因此,进行更新还是有必要的。

文章作者: Akini Xu
文章链接: https://blog.ibestread.com/ihostingenvironment-vs-ihost-environment-obsolete-types-in-net-core-3/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 嘉阅
支付宝打赏
微信打赏