「探索 .NET 6」02 比较 WebApplicationBuilder 和 Host
cac55 2024-10-11 11:01 27 浏览 0 评论
这是『探索 .NET 6』系列的第二篇文章:
- 01 揭开 ConfigurationManager 的面纱
- 02 比较 WebApplicationBuilder 和 Host
在 .NET 中,有一种新的“默认”方法用来构建应用程序,即使用 WebApplication.CreateBuilder()。在这篇文章中,我将这种方法与以前的方法进行了比较,讨论了为什么要进行这种改变,并看看其影响。在下一篇文章中,我将看一下 WebApplication 和 WebApplicationBuilder 背后的代码,看看它们是如何工作的。
构建 ASP.NET Core 应用:一个历史教训
在我们看 .NET 6 之前,我认为值得看看 ASP.NET Core 应用程序的“启动”过程在过去几年中是如何演变的,因为最初的设计对我们今天的情况有很大的影响。当我们在下一篇文章中查看 WebApplicationBuilder 背后的代码时,这一点将变得更加明显!
即使我们忽略了 .NET Core 1.x(目前完全不支持),我们也有三种不同的范式来配置 ASP.NET Core 应用程序。
- WebHost.CreateDefaultBuilder():配置 ASP.NET Core 应用程序的“原始”方法,截至 ASP.NET Core 2.x。
- Host.CreateDefaultBuilder():在通用 Host 的基础上重新构建 ASP.NET Core,支持其他如 Worker 服务的工作负载。.NET Core 3.x 和 .NET 5 中的默认方法。
- WebApplication.CreateBuilder():.NET 6 中的新热点。
为了更好地了解这些差异,我在下面几节中重现了典型的“启动”代码,这应该会使 .NET 6 的变化更加明显。
ASP.NET Core 2.x:WebHost.CreateDefaultBuilder()
在 ASP.NET Core 1.x 的第一个版本中,(如果我记得没错的话)没有“默认” Host 的概念。ASP.NET Core 的理念之一是一切都应该“按需付费”,也就是说,如果你不需要使用它,你就不应该为该功能的存在消费资源。
在实践中,这意味着“入门”模板包含了大量的模板,以及大量的 NuGet 包。为了不看到所有这些代码就能开始的快速开发,ASP.NET Core 引入了 WebHost.CreateDefaultBuilder()。这为你设置了一大堆的默认值,创建了一个 IWebHostBuilder,并建立了一个 IWebHost。
从一开始,ASP.NET Core 就将 Host 启动与应用程序启动分开。从历史上看,这表现为将你的启动代码分成两个文件,传统上称为 Program.cs 和 Startup.cs。
在 ASP.NET Core 2.1 中,Program.cs 调用 WebHost.CreateDefaultBuilder(),设置你的应用程序配置(例如从 appsettings.json 加载)、日志,以及配置 Kestrel 或 IIS 集成。
public class Program
{
public static void Main(string[] args)
{
BuildWebHost(args).Run();
}
public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.Build();
}
默认模板还引用了一个 Startup 类。这个类并没有明确地实现一个接口。相反,IWebHostBuilder 的实现知道寻找 ConfigureServices() 和 Configure() 方法来分别设置你的依赖注入容器和中间件管道。
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseStaticFiles();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
}
在上面的启动类中,我们将 MVC 服务添加到容器中,添加了异常处理和静态文件中间件,然后添加了 MVC 中间件。MVC 中间件是最初构建应用程序的唯一真正实用的方法,它同时满足了服务器渲染的视图和 RESTful API 端点。
ASP.NET Core 3.x/5:HostBuilder
ASP.NET Core 3.x 给 ASP.NET Core 的启动代码带来了一些重大变化。以前,ASP.NET Core 只能真正用于 Web/HTTP 工作负载,但在 .NET Core 3.x 中,做出了支持其他方法的举措:长期运行的“worker services”(例如,用于消费消息队列)、gRPC 服务、Windows 服务等等。我们的目标是与这些其他类型的应用分享专门为构建 Web 应用(配置、日志、DI)而建立的基础框架。
结果是创建了一个“通用 Host”(相对于 Web Host 而言),并在此基础上对 ASP.NET Core 技术栈进行了“重新平台化”。用 IWebHostBuilder 代替了 IHostBuilder。
这一变化引起了一些不可避免的破坏性变化,但 ASP.NET 团队尽力为所有针对 IWebHostBuilder 而不是 IHostBuilder 编写的代码提供了指引。其中一个变通方法是 Program.cs 模板中默认使用的 ConfigureWebHostDefaults() 方法:
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>();
};
}
}
需要 ConfigureWebHostDefaults 来注册 ASP.NET Core 应用程序的 Startup 类,这是 .NET 团队在提供从 IWebHostBuilder 到 IHostBuilder 的迁移路径时面临的挑战之一。Startup 与 Web 应用密不可分,因为 Configure() 方法是配置中间件的。但 worker service 和许多其他应用程序没有中间件,所以 Startup 类作为一个“通用 Host”级别的概念是没有意义的。
这就是 IHostBuilder 上的 ConfigureWebHostDefaults() 扩展方法的作用。这个方法将 IHostBuilder 包裹在一个内部类中,即 GenericWebHostBuilder,并设置 WebHost.CreateDefaultBuilder() 在 ASP.NET Core 2.1 中的所有默认值。GenericWebHostBuilder 作为旧的 IWebHostBuilder 和新的 IHostBuilder 之间的一个适配器。
ASP.NET Core 3.x 的另一个重大变化是引入了端点路由。端点路由是首次尝试使以前仅限于 ASP.NET Core 的 MVC 部分的路由概念可以通用。这需要对你的中间件管道进行一些重新思考,但在许多情况下,必要的改变是最小的。
尽管有这些变化,ASP.NET Core 3.x 中的 Startup 类看起来与 2.x 版本相当相似。下面的例子几乎等同于 2.x 版本(尽管我换成了 Razor Pages 而不是 MVC)。
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
});
}
}
ASP.NET Core 5 给现有的应用程序带来的变化相对较少,因此,从 3.x 升级到 5 通常只是简单地改变目标框架和更新一些 NuGet 软件包 。
对于 .NET 6 来说,如果你要升级现有的应用程序,也是这样。但是对于新的应用程序来说,默认的启动体验已经完全改变了...
ASP.NET Core 6:WebApplicationBuilder
所有以前的 ASP.NET Core 版本都将配置分成两个文件。在 .NET 6 中,C#、BCL 和 ASP.NET Core 的一系列变化意味着现在所有东西都可以放在一个文件中。
请注意,没有人强迫你使用这种风格。我在 ASP.NET Core 3.x/5 代码中展示的所有代码在 .NET 6 中仍然有效。
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseStaticFiles();
app.MapGet("/", () => "Hello World!");
app.MapRazorPages();
app.Run();
这里有很多变化,但其中最明显的是:
- 顶层语句意味着没有 Program.Main() 的模板。
- 隐式 using 指令意味着不需要 using 语句。
- 没有 Startup 类--所有东西都在一个文件中。
这显然减少了很多代码,但这有必要吗?它又是如何工作的呢?
所有的代码都去哪儿了
.NET 6 的一大重点是“新人”的视角。作为 ASP.NET Core 的初学者,有一大堆的概念需要你快速理解。只要看看我的书的目录就知道了;有很多东西需要你去理解!
.NET 6 的变化主要集中在消除与入门相关的“仪式”,以及隐藏那些可能让新人感到困惑的概念。比如说:
- using 语句在入门时是不必要的。尽管工具化通常使这些在实践中成为一个非问题,但当你开始学习时,它们显然是一个不必要的概念。
- 与此类似,namespace 在你入门时也是一个不必要的概念。
- Program.Main()...为什么叫这个名字?为什么我需要它?因为你需要。只是现在你不需要了。
- 配置没有被分割在两个文件中,Program.cs 和 Startup.cs。虽然我喜欢这种“关注点分离”,但这要向新来者解释为什么这种分割方式。
- 当我们谈论 Startup 时,我们不再需要解释“魔术”方法,这些方法可以被调用,尽管它们没有明确地实现一个接口。
此外,我们还有新的 WebApplication 和 WebApplicationBuilder 类型。这些类型对于实现上述目标并不是严格必要的,但它们确实在某种程度上使配置体验更加干净。
我们真的需要一个新的类型吗
嗯,不,我们不需要。我们可以用通用 Host 来编写一个与上面的例子非常相似的 .NET 6 应用程序:
var hostBuilder = Host.CreateDefaultBuilder(args)
.ConfigureServices(services =>
{
services.AddRazorPages();
})
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.Configure((ctx, app) =>
{
if (ctx.HostingEnvironment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseStaticFiles();
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapGet("/", () => "Hello World!");
endpoints.MapRazorPages();
});
});
});
hostBuilder.Build().Run();
我想你肯定认同,这看起来比 .NET 6 的 WebApplication 版本要复杂得多。我们有一大堆嵌套的 lambda,它将一个(大部分)程序性的启动脚本变成了更复杂的东西。
WebApplicationBuilder 的另一个好处是,启动时的异步代码要简单得多。你可以在你喜欢的时候调用异步方法。
关于 WebApplicationBuilder 和 WebApplication 的巧妙之处在于,它们基本上等同于上述的通用 Host 的设置,但它们用了一个更简单的 API 来实现。
大多数配置在WebApplicationBuilder 中
让我们先来看看 WebApplicationBuilder:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
WebApplicationBuilder 主要负责 4 项工作:
- 使用 builder.Configuration 添加配置。
- 使用 builder.Services 添加服务
- 使用 builder.Logging 配置日志
- 配置 IHostBuilder 和 IWebHostBuilder
依次来看...
WebApplicationBuilder 暴露了 ConfigurationManager 类型,用于添加新的配置源,以及访问配置值,正如我在之前的文章中所描述的。
它还直接暴露了一个 IServiceCollection,用于向 DI 容器添加服务。因此,在通用 Host 中,你必须做的是:
var hostBuilder = Host.CreateDefaultBuilder(args);
hostBuilder.ConfigureServices(services =>
{
services.AddRazorPages();
services.AddSingleton<MyThingy>();
})
使用 WebApplicationBuilder 你可以:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.AddSingleton<MyThingy>();
类似的,对于日志,把:
var hostBuilder = Host.CreateDefaultBuilder(args);
hostBuilder.ConfigureLogging(builder =>
{
builder.AddFile();
})
替换成:
var builder = WebApplication.CreateBuilder(args);
builder.Logging.AddFile();
这有完全相同的行为,只是在一个更容易使用的 API 中。对于那些直接依赖 IHostBuilder 或 IWebHostBuilder 的扩展点,WebApplicationBuilder 分别暴露了 Host 和 WebHost 属性。
例如,Serilog 的 ASP.NET Core 集成了 IHostBuilder 勾子。在 ASP.NET Core 3.x/5 中,你用以下方式添加它:
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseSerilog() // <-- Add this line
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
对于 WebApplicationBuilder,你可以在 Host 属性上调用 UseSerilog():
builder.Host.UseSerilog();
事实上,WebApplicationBuilder 是你做所有配置的地方,除了中间件管道。
WebApplication实现了多种接口
一旦你在 WebApplicationBuilder 上配置了你需要的一切,你就可以调用 Build() 来创建一个 WebApplication 的实例:
var app = builder.Build();
WebApplication 很有趣,因为它实现了多个不同的接口:
- IHost - 用来启动和停止 Host
- IApplicationBuilder - 用于建立中间件管道
- IEndpointRouteBuilder - 用于添加路由端点
后面这两点是非常相关的。在 ASP.NET Core 3.x 和 5 中,IEndpointRouteBuilder 用于通过调用 UseEndpoints() 并向其传递一个 lambda 来添加端点,例如:
public void Configure(IApplicationBuilder app)
{
app.UseStaticFiles();
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
});
}
对于刚接触 ASP.NET Core 的人来说,这种 .NET 3.x/5 模式有一些复杂:
- 中间件管道的构建发生在 Startup 的 Configure() 函数中(你必须知道去看那里)。
- 你必须确保在 app.UseEndpoints() 之前调用 app.UseRouting()(以及将其他中间件放在正确的位置)。
- 你必须使用 lambda 来配置端点(对于熟悉 C# 的用户来说并不复杂,但对于新人来说可能会感到困惑)。
WebApplication 大大简化了这种模式:
app.UseStaticFiles();
app.MapRazorPages();
这显然要简单得多,尽管我发现它有点令人困惑,因为中间件和端点之间的区别远没有 .NET 5.x 等中那么清晰。这可能只是个人看法不同,但我认为这混淆了“顺序很重要”的信息(这适用于中间件,但一般不适用端点)。
我还没有展示的是 WebApplication 和 WebApplicationBuilder 是如何构建的。在下一篇文章中,我将揭开幕布,让我们看到幕后的真实情况。
总结
在这篇文章中,我描述了 ASP.NET Core 应用程序的启动从 2.x 版本一直到 .NET 6 的变化。我展示了 .NET 6 中引入的新的 WebApplication 和 WebApplicationBuilder 类型,讨论了它们被引入的原因,以及它们带来的一些优势。最后,我讨论了这两个类所扮演的不同角色,以及它们的 API 如何使启动体验更简单。在下一篇文章中,我将看一下这些类型背后的一些代码,看看它们是如何工作的。
原文:bit.ly/3fDZlS9
作者:Andrew Lock
翻译:精致码农
相关推荐
- 高中生又来卷我们了!手搓 Android 浏览器,可高度定制+脚本支持
-
回想一下,你曾经的暑假,是怎么度过的?可能是无尽的娱乐时光,或者是懒洋洋的休息日。然而,对于这位Gitee上的高中生来说,他选择在这个暑假里独立开发一款Android浏览器——Vie浏览器,...
- 网页加载CAD图纸的两个方案对比说明(网页浏览编辑DWG)
-
一.说明梦想控件提供两种技术在网页中加载CAD图纸,一个是OCX技术方案,另一个是HTML5技术方案,它们各有优缺点,用户需根据实际情况进行选择,下边分别说明一下。1、ocx技术方案(1)OCX技术是...
- 前后端分离的开源在线考试系统调试实战
-
开篇在我们的教育生涯中,或多或少的都接触过在线考试系统。例如大学里最常见的各种软件考试,上机考试等,那么有没有开源的这样的系统呢?当然是有了,今天就来调试个开源的在线考试系统。本文重点是调试,因为很多...
- 网友:小松鼠长大了!UC浏览器推出18周年专版logo引热议
-
近日,互联网厂商logo更新再次引发热议。作为国内手机浏览器的代表性厂商,UC浏览器的标志性logo小松鼠悄然发生了变化,在网友中引发了关注和讨论。依照UC微博官方账号的说法,这个全新的形象是UC18...
- 超多案例!谷歌AI模型Nano Banana的5个实用+趣味玩法
-
再不用这个AI修图神器,你的同行明天就把订单抢光了。谷歌刚放出的NanoBanana,能在一张照片里把背景、姿势、衣服一次换完,脸还是那张脸。实测把地铁照改成海边大片,只用一句话,三秒出图,不用PS来...
- 2025年最佳Windows数据恢复软件解决方案前5名
-
您是否正在寻找互联网上排名前五的WindowsPC最佳数据恢复软件解决方案?其实,网上有很多工具可以恢复已删除的文件。但并非所有应用程序都值得使用。值得信赖的文件恢复工具可以帮助您快速检索丢失、删...
- 电脑数据恢复软件推荐:10个顶级数据恢复软件分享
-
在数字化的工作与生活中,电脑文件误删除的情况时有发生,这不仅会引发我们的焦虑情绪,更可能导致重要数据的丢失。不过,幸运的是,借助正确的数据恢复软件,我们仍有机会找回那些被误删的文件。10个顶级数据恢复...
- 更懂国内APP的开源智能体!感知定位推理中文能力全面提升
-
更懂国内APP的开源智能体!感知定位推理中文能力全面提升“帮我点外卖,别点到广告位。”一句话,说出了多少人对手机自动化的真实期待。浙大和美团刚扔出来的开源项目UItron,就是冲着这句吐槽来的——它真...
- 美光首家推出采用EUV技术的1γ DDR5 DRAM芯片
-
美光科技宣布已开始向部分生态系统合作伙伴和客户出货1γ(1-gamma)16GbitDDR5DRAM芯片。美光声称,它是第一个采用1-gamma(1γ)节点的公司,该节点指的是DRAM工艺技术的第...
- DDR4的PCB设计及仿真_ddr pcb
-
以下文章来源于鼎阳硬件智库,作者王彦武DDR4关键技术和方法分析1.1DDR4与DDR3不同之处相对于DDR3,DDR4首先在外表上就有一些变化,比如DDR4将内存下部设计为中间稍微突出,边缘变...
- DDR4和DDR5内存的性能差距有哪些?
-
DDR4和DDR5内存的性能差距主要体现在带宽、延迟、能效及未来扩展性上,以下是关键差异的总结及选择建议:1.带宽与频率DDR4:主流频率为2133MHz–3600MHz,带宽约25.6–30.2...
- DDR5内存一根和两根的区别,建议收藏观看。
-
大家好,我是海韵,DDR5内存条,单条和双条有什么区别,如何选择,DDR5单条和双条内存在性能上存在差距,单条内存保持在64个通道,但内部升级为32乘以2,虽然出口速度相同,但内部运行略有提升,...
- Kingston FURY叛逆者DDR5 RGB CUDIMM内存评测 强势突破9000MT/s!
-
【ZOL中关村在线原创评测】当8000MT/s从当年的液氮超频艰难达成,到如今XMP轻松开启,DDR5内存频率的极限探索似乎看不到终点。在早先,我们曾为大家带来KingstonFURY品牌的叛逆者D...
- SK海力士将在年内推出1bnm 32Gb DDR5内存颗粒
-
IT之家4月25日消息,据韩媒NEWSIS报道,SK海力士在今日的2024年一季度财报电话会议上表示将在年内推出1bnm32GbDDR5内存颗粒。32Gb颗粒意味着消费级的...
- DRAM史上最大代际倒挂继续:三星将延长DDR4生产期限至2026年
-
IT之家8月6日消息,韩媒TheElec今天(8月6日)发布博文,报道称三星决定延长DDR41zDRAM的生产期限至2026年,一方面在DRAM史上最大代际倒挂中进...
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- 如何绘制折线图 (52)
- javaabstract (48)
- 新浪微博头像 (53)
- grub4dos (66)
- s扫描器 (51)
- httpfile dll (48)
- ps实例教程 (55)
- taskmgr (51)
- s spline (61)
- vnc远程控制 (47)
- 数据丢失 (47)
- wbem (57)
- flac文件 (72)
- 网页制作基础教程 (53)
- 镜像文件刻录 (61)
- ug5 0软件免费下载 (78)
- debian下载 (53)
- ubuntu10 04 (60)
- web qq登录 (59)
- 笔记本变成无线路由 (52)
- flash player 11 4 (50)
- 右键菜单清理 (78)
- cuteftp 注册码 (57)
- ospf协议 (53)
- ms17 010 下载 (60)