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

精緻碼農發表於2021-04-10

原文:bit.ly/2Qb56NP
作者:Daniel Roth
譯者:精緻碼農-王亮

.NET 6 預覽版 3 現已推出,其中包括許多對新的 ASP.NET Core 改進。以下是本次預覽版的新內容:

  • 更小的 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]

如果你在 Windows 上使用 Visual Studio,我們建議安裝 Visual Studio 2019 16.10 的最新預覽版。如果你在 macOS 上,我們建議安裝 Visual Studio 2019 for Mac 8.10 的最新預覽版。

升級現有專案

要將現有的 ASP.NET Core 應用程式從 .NET 6 Preview 2 升級到 .NET 6 Preview 3:

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

檢視 ASP.NET Core for .NET 6 中的完整中斷性更改列表[2]

更小的指令碼檔案

得益於 Ben Adams 的社群貢獻,SignalR、MessagePack 和 Blazor Server 指令碼現在明顯變小了,下載體積減少,瀏覽器解析和編譯 JavaScript 的次數減少,啟動速度加快。

這項工作帶來的下載體積減少是非常驚人的:

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。

下面說下我們是如何減少體積的:

  • 更新 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!

相關文章