.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!