.NET 6 Preview 3 中 ASP.NET Core 的更新和改进

原文:bit.ly/2Qb56NP
做者:Daniel Roth
译者:精致码农-王亮css

.NET 6 预览版 3 现已推出,其中包括许多对新的 ASP.NET Core 改进。如下是本次预览版的新内容:html

  • 更小的 SignalR、Blazor Server、MessagePack 脚本文件
  • 启用 Redis 分析会话
  • HTTP/3 端点 TLS 配置
  • 初步 .NET 热重载支持
  • Razor 编译器再也不生成单独的视图 Assembly
  • IIS 中的浅拷贝支持
  • 适用于 SignalR C++ 客户端的 Vcpkg 端口
  • 减小闲置 TLS 链接的内存占用量
  • 从 SlabMemoryPool 中移除板块
  • 用于 WPF 和 Windows 窗体的 BlazorWebView 控件

开始

要在.NET 6 Preview 3 中开始使用 ASP.NET Core,请安装 .NET 6 SDK[1]webpack

若是你在 Windows 上使用 Visual Studio,咱们建议安装 Visual Studio 2019 16.10 的最新预览版。若是你在 macOS 上,咱们建议安装 Visual Studio 2019 for Mac 8.10 的最新预览版。git

升级现有项目

要将现有的 ASP.NET Core 应用程序从 .NET 6 Preview 2 升级到 .NET 6 Preview 3:es6

  • 更新全部Microsoft.AspNetCore.*包引用至6.0.0-preview.3.*
  • 更新全部Microsoft.Extensions.*包引用至6.0.0-preview.3.*

查看 ASP.NET Core for .NET 6 中的完整中断性更改列表[2]github

更小的脚本文件

得益于 Ben Adams 的社区贡献,SignalR、MessagePack 和 Blazor Server 脚本如今明显变小了,下载体积减小,浏览器解析和编译 JavaScript 的次数减小,启动速度加快。web

这项工做带来的下载体积减小是很是惊人的:redis

Library Before After %↓ .br
signalr.min.js 130 KB 39 KB 70% 10 KB
blazor.server.js 212 KB 116 KB 45% 28 KB

如今你也只须要为 MessagePack 提供@microsoft/signalr-protocol-msgpack包,而不须要包含 msgpack5。这意味着你只须要额外的 29 KB 而不是以前的 140 KB 来使用 MessagePack 而不是 JSON。express

下面说下咱们是如何减小体积的:json

  • 更新 TypeScript 和依赖关系到最新版本.
  • 将 uglify-js 换成了 terser,这是 webpack 的默认版本,支持新的 JavaScript 语言特性(好比class)。
  • 将 SignalR 模块标记为"sideEffects":false,这样 tree-shaking 就更有效了。
  • 丢弃了 "es6-promise/dist/es6-promise.auto.js"的多边填充。
  • 更改 TypeScript 为输出es2019而不是es5,并放弃了es2015.promisees2015.iterable的 polyfill。
  • @msgpack/msgpack移到msgpack5,由于它须要更少的 polyfills,而且是 TypeScript 和模块感知的。

你能够在 GitHub 上 Ben 的 PR[3] 中找到更多关于这些变化的细节。

启用 Redis 分析会话

咱们接受了 Gabriel Lucaci 的社区贡献,在此预览版中使用Microsoft.Extensions.Caching.StackExchangeRedis启用 Redis 分析会话。关于 Redis 分析的更多细节,请参见官方文档[4]。该 API 的使用方法以下:

services.AddStackExchangeRedisCache(options =>
{
    options.ProfilingSession = () => new ProfilingSession();
})

HTTP/3 端点 TLS 配置

HTTP/3 与现有的 HTTP 协议相比具备许多优点,包括更快的链接设置,以及在低质量网络上的性能改进。

在此预览版中,新增了使用UseHttps在单个 HTTP/3 端口上配置 TLS 证书的功能。这使得 Kestrel 的 HTTP/3 端点配置与 HTTP/1.1 和 HTTP/2 一致。

.ConfigureKestrel((context, options) =>
{
    options.EnableAltSvc = true;
    options.Listen(IPAddress.Any, 5001, listenOptions =>
    {
        listenOptions.Protocols = HttpProtocols.Http3;
        listenOptions.UseHttps(httpsOptions =>
        {
            httpsOptions.ServerCertificate = LoadCertificate();
        });
    });
})

初步 .NET 热重载支持

如今,使用dotnet watch的 ASP.NET Core 和 Blazor 项目能够得到对 .NET 热重载的早期支持。.NET 热重载能够在不从新启动应用程序和不丢失应用程序状态的状况下将代码更改应用到你正在运行的应用程序中。

要在现有的基于 .NET 6 的 ASP.NET Core 项目中试用热重载,请将"hotReloadProfile": "aspnetcore"属性添加到你的launchSettings.json文件中。对于 Blazor WebAssembly 项目,使用"blazorwasm"热重载配置文件。

使用dotnet watch运行项目。下面的输出代表热重载已经启用:

watch : Hot reload enabled. For a list of supported edits, see https://aka.ms/dotnet/hot-reload. Press "Ctrl + R" to restart.

在任什么时候候你想强制应用程序从新构建和重启,你能够在控制台输入Ctrl+R来实现。

如今你能够开始对你的代码进行编辑了。当你保存代码更改时,相应的更改几乎会在瞬间自动热重载到运行中的应用程序中。运行中的应用程序中的任何状态都会被保留。

你也能够对你的 CSS 文件进行热重载更改,而不须要刷新浏览器:

有一些代码更改不支持 .NET 执重载。你能够在文档[5]中找到支持的代码编辑列表。在 Blazor WebAssembly 中,目前只支持方法体替换。咱们正在努力扩展 .NET 6 中支持的编辑集。当dotnet watch检测到没法使用热重载应用的更改时,它就会退回从新构建和从新启动应用程序。

这只是 .NET 6 中热重载支持的开始。桌面和移动应用程序的热重载支持将很快在即将到来的预览版中提供,以及在 Visual Studio 中集成热重载。

IIS 中的浅拷贝支持

咱们在 IIS 的 ASP.NET Core 模块中添加了一个新功能,以增长对浅拷贝应用程序程序集的支持。目前,.NET 在 Windows 上运行时锁定了应用程序的二进制文件,使得在应用程序仍在运行时没法替换二进制文件。虽然咱们的建议仍然是使用应用程序脱机文件,但咱们认识到在某些状况下(例如 FTP 部署)不可能这样作。

在这种状况下,你能够经过自定义 ASP.NET Core 模块处理程序设置来启用浅拷贝。在大多数状况下,ASP.NET Core 应用程序的web.config不在源代码版本控制中,你能够修改它(它们一般是由 SDK 生成的)。你能够添加这个web.config示例来开始。

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <!-- To customize the asp.net core module uncomment and edit the following section.
  For more info see https://go.microsoft.com/fwlink/?linkid=838655 -->

  <system.webServer>
    <handlers>
      <remove name="aspNetCore"/>
      <add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModulev2" resourceType="Unspecified"/>
    </handlers>
    <aspNetCore processPath="%LAUNCHER_PATH%" arguments="%LAUNCHER_ARGS%" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout">
      <handlerSettings>
        <handlerSetting name="experimentalEnableShadowCopy" value="true" />
        <handlerSetting name="shadowCopyDirectory" value="../ShadowCopyDirectory/" />
        <!-- Only enable handler logging if you encounter issues-->
        <!--<handlerSetting name="debugFile" value=".\logs\aspnetcore-debug.log" />-->
        <!--<handlerSetting name="debugLevel" value="FILE,TRACE" />-->
      </handlerSettings>
    </aspNetCore>
  </system.webServer>
</configuration>

你须要一个新版本的 ASP.NET Core 模块来尝试这个功能。在自托管的 IIS 服务器上,这须要新版本的托管捆绑包。在 Azure App Services 上,你须要安装新的 ASP.NET Core 站点运行时扩展。

适用于 SignalR C++ 客户端的 Vcpkg 端口

Vcpkg 是一个跨平台的 C 和 C++库的命令行包管理器。最近,咱们为 vcpkg 添加了一个移植版本,为 SignalR C++ 客户端添加了 CMake 本地支持(也适用于 MSBuild 项目)。

你能够用下面的代码来添加 SignalR 客户端到你的 CMake 项目中(假设你已经包含了 vcpkg 工具链文件)。

find_package(microsoft-signalr CONFIG REQUIRED)
link_libraries(microsoft-signalr::microsoft-signalr)

在这以后,SignalR C++ 客户端就能够被#include并用于你的项目中,而不须要任何额外的配置。这个仓库[6]是一个完整的使用 SignalR C++ 客户端的 C++ 应用程序的例子。

减小闲置 TLS 链接的内存占用量

对于只偶尔来回发送数据的 TLS 长链接,咱们已经大大减小了 .NET 6 中 ASP.NET Core 应用程序的内存占用。这应该有助于提升 WebSocket 服务器等场景的可扩展性。这得益于System.IO.PipelinesSslStreamKestrel的众多改进。让咱们来看看促成这一方案的一些改进。

缩减 System.IO.Pipelines.Pipe 大小

对于咱们创建的每个链接,咱们都会在 Kestrel 中分配两个管道:一个是从传输层到应用的请求,另外一个是从应用层到传输的响应。经过将System.IO.Pipelines.Pipe的大小从 368 字节缩减到 264 字节(约 28.2%),咱们为每一个链接节省了 208 字节(每一个 Pipe 节省 104 字节)。

SocketSender 池

SocketSender 对象在运行时约为 350 字节。与其为每一个链接分配一个新的 SocketSender 对象,咱们能够将它们集中起来,由于发送一般很是快,咱们能够减小每一个链接的开销。如今,咱们再也不为每一个链接分配 350 字节,而是只为每一个 IOQueue 分配 350 字节(每一个队列一个,以免争用)。在拥有 5000 个空闲链接的 WebSocket 服务器中,咱们从分配约 1.75 MB(350 字节*5000)到如今只分配约 2.8kb(350 字节*8)给 SocketSender 对象。

SslStream 零字节读取

无缓冲读取是咱们已经在 ASP.NET Core 中采用的一种技术,以免在套接字上没有可用数据时从内存池中租用内存。在这一变化以前,咱们的 WebSocket 服务器有 5000 个空闲链接,在没有 TLS 的状况下须要约 200 MB,而在有 TLS 的状况下须要约 800 MB。其中一些分配(每一个链接 4k)是因为 Kestrel 在等待SslStream上的读取完成时必须保持ArrayPool缓冲区。鉴于这些链接是空闲的,没有一个读取完成并将其缓冲区返回给ArrayPool,迫使ArrayPool分配更多的内存。剩余的分配都在SslStream自己。4k 缓冲区用于 TLS 握手,32k 缓冲区用于正常读取。在预览版 3 中,当用户在SslStream上执行零字节读取,而它又没有可用的数据时,SslStream会在内部对底层的封装流执行零字节读取。在最好的状况下(空闲链接),这些变化致使每一个链接节省了 40 Kb,同时仍然容许消费者(Kestrel)在数据可用时获得通知,而无需保留任何未使用的缓冲区。

PipeReader 零字节读取

一旦SslStream支持无缓冲区读取,咱们就向StreamPipeReader(将Stream适配成PipeReader的内部类型)添加了执行零字节读取的选项。在 Kestrel 中,咱们使用StreamPipeReader将底层的SslStream适配成PipeReader,有必要在PipeReader上暴露这些零字节读取语义。

如今,你可使用如下 API 建立一个PipeReader,支持在任何支持零字节读取语义的Stream上进行零字节读取(例如SslStreamNetworkStream等)。

var reader = PipeReader.Create(stream, new StreamPipeReaderOptions(useZeroByteReads: true));

从 SlabMemoryPool 中移除板块

为了减小堆的碎片,Kestrel 采用了一种技术,它分配了 128 KB 的内存板块做为其内存池的一部分。而后,这些板块被进一步划分为 4 KB 的块,供 Kestrel 内部使用。板块必须大于 85 KB,以便在大对象堆上强制分配,以尽可能防止 GC 从新定位这个阵列。然而,随着新一代 GC 的引入,Pinned Object Heap(POH),在板块上分配块已经没有意义了。在预览版 3 中,咱们如今直接在 POH 上分配块[7],下降了管理本身的内存池所涉及的复杂性。这个变化应该能够更容易地执行将来的改进,好比让 Kestrel 使用的内存池更容易收减。

用于 WPF 和 Windows 窗体的 BlazorWebView 控件

对于 .NET 6,咱们增长了对使用 .NET MAUI 和 Blazor 构建跨平台混合桌面应用程序的支持。混合应用程序是利用 Web 技术实现其功能的本地应用程序。例如,一个混合应用程序可能会使用一个嵌入式的 Web 视图控件来渲染 Web UI。这意味着你可使用 HTML 和 CSS 等 Web 技术编写应用程序 UI,同时还可使用本地设备的功能。咱们将在即将发布的 .NET 6 预览版中引入对使用 .NET MAUI 和 Blazor 构建混合应用程序的支持。

在这个版本中,咱们为 WPF 和 Windows Forms 应用程序引入了BlazorWebView控件,该控件可将 Blazor 功能嵌入到基于 .NET 6 的现有 Windows 桌面应用程序中。使用 Blazor 和混合方式,你能够将你的 UI 与 WPF 和 Windows Forms 解耦。这是一种对现有桌面应用程序进行现代化改造的好方法,能够将其带到 .NET MAUI 上或在 Web 上使用。你可使用 Blazor 对现有的 Windows Forms 和 WPF 应用程序进行现代化改造。

要使用新的BlazorWebView控件,你首先须要确保你已经安装了 WebView2[8]

要将 Blazor 功能添加到现有的 Windows Forms 应用程序中,须要:

  • 更新 Windows Forms 应用程序,使其 Target 为 .NET 6。

  • 把应用程序项目文件中的 SDK 更新为 Microsoft.NET.Sdk.Razor。

  • 添加Microsoft.AspNetCore.Components.WebView.WindowsForms包引用。

  • 在项目中添加如下wwwroot/index.html文件,用实际的项目名称替换{PROJECT NAME}

    <!DOCTYPE html>
    <html>
      <head>
        <meta charset="utf-8" />
        <meta
          name="viewport"
          content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"
        />
        <title>Blazor app</title>
        <base href="/" />
        <link href="{PROJECT NAME}.styles.css" rel="stylesheet" />
        <link href="app.css" rel="stylesheet" />
      </head>
    
      <body>
        <div id="app"></div>
    
        <div id="blazor-error-ui">
          An unhandled error has occurred.
          <a href="" class="reload">Reload</a>
          <a class="dismiss">🗙</a>
        </div>
    
        <script src="_framework/blazor.webview.js"></script>
      </body>
    </html>
  • 在 wwwroot 文件夹中添加如下app.css文件,包含一些基本样式:

    html,
    body {
      font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
    }
    
    .valid.modified:not([type='checkbox']) {
      outline: 1px solid #26b050;
    }
    
    .invalid {
      outline: 1px solid red;
    }
    
    .validation-message {
      color: red;
    }
    
    #blazor-error-ui {
      background: lightyellow;
      bottom: 0;
      box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
      display: none;
      left: 0;
      padding: 0.6rem 1.25rem 0.7rem 1.25rem;
      position: fixed;
      width: 100%;
      z-index: 1000;
    }
    
    #blazor-error-ui .dismiss {
      cursor: pointer;
      position: absolute;
      right: 0.75rem;
      top: 0.5rem;
    }
  • 对于 wwwroot 文件夹中的全部文件,将Copy to Output Directory属性设置为Copy if newer

  • 在项目中添加一个 Blazor 根组件Counter.razor

    @using Microsoft.AspNetCore.Components.Web
    
    <h1>Counter</h1>
    
    <p>The current count is: @currentCount</p>
    <button @onclick="IncrementCount">Count</button>
    
    @code {
        int currentCount = 0;
    
        void IncrementCount()
        {
            currentCount++;
        }
    }
  • BlazorWebView控件添加到所需的表单中,以渲染 Blazor 根组件:

    var serviceCollection = new ServiceCollection();
    serviceCollection.AddBlazorWebView();
    var blazor = new BlazorWebView()
    {
        Dock = DockStyle.Fill,
        HostPage = "wwwroot/index.html",
        Services = serviceCollection.BuildServiceProvider(),
    };
    blazor.RootComponents.Add<Counter>("#app");
    Controls.Add(blazor);
  • 运行该应用程序,查看BlazorWebView的运行状况。

要将 Blazor 功能添加到现有的 WPF 应用程序中,请按照上面列出的 Windows 窗体应用程序的相同步骤进行操做。另外:

  • Microsoft.AspNetCore.Components.WebView.Wpf替换包引用。

  • 在 XAML 中添加BlazorWebView控件:

    <Window x:Class="WpfApp1.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:WpfApp1"
            xmlns:blazor="clr-namespace:Microsoft.AspNetCore.Components.WebView.Wpf;assembly=Microsoft.AspNetCore.Components.WebView.Wpf"
            mc:Ignorable="d"
            Title="MainWindow" Height="450" Width="800">
        <Grid>
            <blazor:BlazorWebView HostPage="wwwroot/index.html" Services="{StaticResource services}">
                <blazor:BlazorWebView.RootComponents>
                    <blazor:RootComponent Selector="#app" ComponentType="{x:Type local:Counter}" />
                </blazor:BlazorWebView.RootComponents>
            </blazor:BlazorWebView>
        </Grid>
    </Window>
  • 将服务提供者设置为静态资源:

    var serviceCollection = new ServiceCollection();
    serviceCollection.AddBlazorWebView();
    Resources.Add("services", serviceCollection.BuildServiceProvider());
  • 为了解决 WPF 运行时构建时找不到 Razor 组件类型的问题,在Counter.razor.cs中为组件添加一个空的局部类:

    public partial class Counter { }
  • 构建并运行基于 Blazor 的 WPF 应用:

提供反馈

咱们但愿你喜欢这个 .NET 6 预览版中的 ASP.NET Core 部分。咱们渴望听到你对这个版本的体验。请在 GitHub 上提交 Issue,让咱们知道你的想法。

谢谢你试用 ASP.NET Core!