原文: https://andrewlock.net/ihostingenvironment-vs-ihost-environment-obsolete-types-in-net-core-3/

作者: Andrew Lock

译者: Lamond Lu

本篇是如何升级到ASP.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.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; }
}
}

之所以有两个同名接口是有历史原因的。AspNetCore版本的接口已经存在了很长时间了,在ASP.NET Core 2.1版本中,通用主机引入了Extensions版本。Extensions版本没有提供用于服务静态文件的wwwroot目录的概念(因为它承载的是非HTTP服务)。所以你可能已经注意到Extensions缺少了WebRootFileProviderWebRootPath两个属性。

出于向后兼容的原因,这里需要一个单独的抽象。但是,这种做法真正令人讨厌的后果之一是无法编写用于通用主机和ASP.NET Core的扩展方法。

在ASP.NET Core 3.0中,上述的两个接口都已经被标记为废弃了。你依然可以使用它们,但是在编译的时候,你会得到一些警告。相对的,两个新的接口被引入进来: IHostEnvironmentIWebHostEnvironment。虽然他们出现在不同的命名空间中,但是现在它们有了不同的名字,而且使用了继承关系。

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的应用

尽可能是使用IHostEnviornment接口,但你需要访问WebRootPathWebRootFileProvider两个属性的时候,请使用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.HostingMicrosoft.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, 你并不需要马上替换它们,因为他们现在的行为依然相同,但是在将来的版本中会被替换掉,因此如果可以的话,最好对其进行更新。在某些场景中,它还使你的应用之间共享代码更加容易,因此值得研究一下。

IHostingEnvironment VS IHostEnvironment - .NET Core 3.0中的废弃类型的更多相关文章

  1. 避免在ASP.NET Core 3.0中为启动类注入服务

    本篇是如何升级到ASP.NET Core 3.0系列文章的第二篇. Part 1 - 将.NET Standard 2.0类库转换为.NET Core 3.0类库 Part 2 - IHostingE ...

  2. ASP.NET Core 1.0 中使用 Swagger 生成文档

    github:https://github.com/domaindrivendev/Ahoy 之前文章有介绍在ASP.NET WebAPI 中使用Swagger生成文档,ASP.NET Core 1. ...

  3. 在ASP.NET Core 2.0中使用CookieAuthentication

    在ASP.NET Core中关于Security有两个容易混淆的概念一个是Authentication(认证),一个是Authorization(授权).而前者是确定用户是谁的过程,后者是围绕着他们允 ...

  4. 如何在ASP.NET Core 2.0中使用Razor页面

    如何在ASP.NET Core 2.0中使用Razor页面  DotNetCore2017-11-22 14:49 问题 如何在ASP.NET Core 2.0中使用Razor页面 解 创建一个空的项 ...

  5. ASP.NET Core 1.0 中的依赖项管理

    var appInsights=window.appInsights||function(config){ function r(config){t[config]=function(){var i= ...

  6. 在ASP.NET Core 1.0中如何发送邮件

    (此文章同时发表在本人微信公众号"dotNET每日精华文章",欢迎右边二维码来关注.) 题记:目前.NET Core 1.0中并没有提供SMTP相关的类库,那么要如何从ASP.NE ...

  7. EF Core 1.0中使用Include的小技巧

    (此文章同时发表在本人微信公众号"dotNET每日精华文章",欢迎右边二维码来关注.) 题记:由于EF Core暂时不支持Lazy Loading,所以利用Include来加载额外 ...

  8. 用ASP.NET Core 1.0中实现邮件发送功能

    准备将一些项目迁移到 asp.net core 先从封装类库入手,在遇到邮件发送类时发现在 asp.net core 1.0中并示提供SMTP相关类库,于是网上一搜发现了MailKit 好东西一定要试 ...

  9. Core 1.0中的管道-中间件模式

    ASP.NET Core 1.0中的管道-中间件模式 SP.NET Core 1.0借鉴了Katana项目的管道设计(Pipeline).日志记录.用户认证.MVC等模块都以中间件(Middlewar ...

随机推荐

  1. Oracle中组合索引的使用详解(转)

    在Oracle中可以创建组合索引,即同时包含两个或两个以上列的索引.在组合索引的使用方面,Oracle有以下特点: 1. 当使用基于规则的优化器(RBO)时,只有当组合索引的前导列出现在SQL语句的w ...

  2. uni-app设置 video开始播放进入全屏状态

    有一video标签 <video id="myVideo" :src="videoUrl"></video> 获取 video 上下文 ...

  3. poj 1271 && uva 10117 Nice Milk (半平面交)

    uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem= ...

  4. win7 debug 工具

    x86 处理器中的 CS 与 IP 寄存器介绍与调试: http://blog.sina.com.cn/s/blog_54f82cc2010121yj.html https://www.jianshu ...

  5. localStorage、sessionStorage、cookie的区别

    localStorage: 存储的内容大概20MB 不同浏览器不能共享,但是在同一浏览器的不同窗口中可以共享 永久生效,它的数据是存储的硬盘上,并不会随着页面或者浏览器的关闭而清楚,需手动清除 ses ...

  6. HBuider快捷键

    朋友推荐用Hbuilder编辑器,看了下Hbuilder官网和那视频,感觉牛逼哄哄的, 自己也就体验了一下,打开Hbuilder的快捷键列表,每个快捷键都体验了一下,以下展示出来的,每一个都是精华,每 ...

  7. java UDP传输

    ①:只要是网络传输,必须有socket . ②:数据一定要封装到数据包中,数据包中包括目的地址.端口.数据等信息. 直接操作udp不可能,对于java语言应该将udp封装成对象,易于我们的使用,这个对 ...

  8. H3C 端口绑定典型配置举例

  9. springBoot中“MockMvc”的进行Controller进行单元测试:application/octet-stream' not supported问题小结

    解决方案:这个问题其实是Content-type的问题,只需要在相关的代码加入相关Content-type中就可以了,代码如下: mockMvc.perform(post("/user&qu ...

  10. 文本框(代替input)输入长度限制、提示

    <div class="inform_content_text"> <textarea name="name" placeholder=&qu ...