ASP.NET Core 在 .NET 6 Preview 2 中的更新
英文:devblogs.microsoft.com 譯文:cnblogs.com/ElderJames
譯者:ElderJames
前言
《.NET 6 Preview 2 發(fā)布》其中包括許多對(duì) ASP.NET Core 的新改進(jìn)。
以下是本次預(yù)覽版的新內(nèi)容:
Razor 編譯器更新為使用 Source Generators Blazor 支持自定義事件參數(shù) 增加 MVC 視圖和 Razor 頁(yè)面的 CSS 隔離 Blazor 支持從祖先組件中推斷組件的泛型類(lèi)型 Blazor 應(yīng)用程序支持保留預(yù)渲染時(shí)的狀態(tài) SignalR – 支持 Nullable 標(biāo)注
開(kāi)始使用
想開(kāi)始在 .NET 6 Preview 2 中使用 ASP.NET Core,請(qǐng)先安裝 .NET 6 SDK。
如果正在 Windows 上使用 Visual Studio,我們建議安裝 Visual Studio 2019 16.10 的最新預(yù)覽版。如果您在 macOS 上,我們建議安裝 Visual Studio 2019 for Mac 8.10 的最新預(yù)覽版。
升級(jí)現(xiàn)有項(xiàng)目
要將現(xiàn)有的 ASP.NET Core 應(yīng)用程序從 .NET 6 Preview 1升級(jí)到.NET 6 Preview 2,您需要:
更新所有 Microsoft.AspNetCore.* 包的引用到 6.0.0-preview.2.*. 更新所有 Microsoft.Extensions.* 包的引用到 6.0.0-preview.2.*.
再查看完整的 ASP.NET Core 在 .NET 6 中的破壞性改動(dòng)列表。
Razor 編譯器更新為使用 Source Generators
我們?cè)谶@個(gè)預(yù)覽版中更新了 Razor 編譯器,使用 C# Source Generators來(lái)實(shí)現(xiàn)。Source Generators 在編譯過(guò)程中運(yùn)行,并能檢查正在編譯的內(nèi)容,以生成額外的文件,與項(xiàng)目的其余部分一起編譯。我們使用 Source Generators 簡(jiǎn)化了 Razor 編譯器,并顯著加快了構(gòu)建的時(shí)間。
下圖顯示了使用新的 Razor 編譯器構(gòu)建默認(rèn)的 Blazor Server 和 MVC 模板時(shí)的構(gòu)建時(shí)間改進(jìn)。

Blazor 支持自定義事件參數(shù)
Blazor 對(duì)自定義事件的支持現(xiàn)已擴(kuò)展到支持自定義事件參數(shù)。這允許通過(guò)自定義事件向 .NET 事件處理程序傳遞任意數(shù)據(jù)。
例如,您可能希望在用戶(hù)粘貼文本的同時(shí)接收剪貼板粘貼事件。要做到這一點(diǎn),首先要為您的事件聲明一個(gè)自定義的名稱(chēng),以及一個(gè) .NET ,該類(lèi)將持有該事件的事件參數(shù),通過(guò)添加以下類(lèi)到您的項(xiàng)目中:
[EventHandler("oncustompaste", typeof(CustomPasteEventArgs), enableStopPropagation: true, enablePreventDefault: true)]
public static class EventHandlers
{
// 這個(gè)靜態(tài)類(lèi)不需要包含任何成員。
// 它只是一個(gè)讓我們可以把 [EventHandler] 屬性放在 Razor 編譯器上配置事件類(lèi)型的地方。
// 這樣將影響編譯器的輸出以及編輯器中的代碼完成。
}
public class CustomPasteEventArgs : EventArgs
{
// 這些屬性的數(shù)據(jù)將由自定義的 JavaScript 邏輯提供。
public DateTime EventTimestamp { get; set; }
public string PastedData { get; set; }
}
一旦這些都到位了,您就會(huì)在您的 Razor 組件中得到一個(gè)叫做 @oncustompaste 的新事件的智能提醒。例如,在 Index.razor 中,您可以按以下方式使用它:
@page "/"
<p>Try pasting into the following text box:</p>
<input @oncustompaste="HandleCustomPaste" />
<p>@message</p>
@code {
string message;
void HandleCustomPaste(CustomPasteEventArgs eventArgs)
{
message = $"At {eventArgs.EventTimestamp.ToShortTimeString()}, you pasted: {eventArgs.PastedData}";
}
}
然而,如果您現(xiàn)在實(shí)際運(yùn)行代碼,事件將不會(huì)觸發(fā)。因?yàn)檫€剩下一個(gè)步驟,就是添加一些 JavaScript 代碼,實(shí)際為您的新 EventArgs 子類(lèi)提供數(shù)據(jù)。在您的 index.html 或 _Host.cshtml 文件中,添加以下內(nèi)容:
<!-- 您需要將這段代碼直接添加到 blazor.server.js 或 blazor.webassembly.js 的 <script> 標(biāo)簽后面。-->
<script>
Blazor.registerCustomEventType('custompaste', {
browserEventName: 'paste',
createEventArgs: event => {
// 這個(gè)例子只處理粘貼文本,但你可以使用任意的 JavaScript API
// 來(lái)處理用戶(hù)粘貼其他類(lèi)型的數(shù)據(jù),如圖片。
return {
eventTimestamp: new Date(),
pastedData: event.clipboardData.getData('text')
};
}
});
</script>
這是告訴瀏覽器,每當(dāng)本地粘貼事件發(fā)生時(shí),它也應(yīng)該引發(fā)一個(gè)自定義粘貼事件,并使用您的自定義邏輯提供事件參數(shù)數(shù)據(jù)。請(qǐng)注意,事件名稱(chēng)的約定在 .NET(事件名稱(chēng)前綴為on)和 JavaScript(事件名稱(chēng)沒(méi)有任何前綴)之間有所不同。
增加 MVC 視圖和 Razor 頁(yè)面的 CSS 隔離
現(xiàn)在,MVC視圖 和 Razor 頁(yè)面跟 Blazor 組件一樣支持 CSS 隔離。想要添加一個(gè)視圖或頁(yè)面特定的 CSS 文件,只需添加一個(gè)與 .cshtml文件名稱(chēng)相匹配的 .cshtml.css 文件即可。
Index.cshtml.css
h1 {
color: red;
}
在你的布局中添加一個(gè)鏈接到 {項(xiàng)目名}.styles.css 來(lái)引用捆綁的樣式。
<link rel="stylesheet" href="MyWebApp.styles.css" />
然后,這些樣式將只應(yīng)用于各自的視圖和頁(yè)面。

Blazor 支持從祖先組件中推斷組件的泛型類(lèi)型
在使用 Blazor 的泛型組件(如 Grid<TItem> 或 ListView<TItem>)時(shí),Blazor 通??梢愿鶕?jù)傳遞給組件的參數(shù)來(lái)推斷泛型類(lèi)型參數(shù),因此您不必明確指定它們。但在更復(fù)雜的組件中,您可能會(huì)有多個(gè)泛型組件一起使用,而這些組件的類(lèi)型參數(shù)是要匹配的,比如 Grid<TItem> 和 Column<TItem> 。在這些復(fù)合場(chǎng)景中,泛型類(lèi)型參數(shù)通常需要顯式指定,就像這樣:
<Grid Items="@people">
<Column TItem="Person" Name="Full name">@context.FirstName @context.LastName</Column>
<Column TItem="Person" Name="E-mail address">@context.Email</Column>
</Grid>
但你真正想做的是:
<Grid Items="@people">
<Column Name="Full name">@context.FirstName @context.LastName</Column>
<Column Name="E-mail address">@context.Email</Column>
</Grid>
有必要在每個(gè) <Column> 上重新指定 TItem,因?yàn)槊總€(gè) <Column> 都被視為一個(gè)獨(dú)立的組件,沒(méi)有其他方法知道它應(yīng)該與什么類(lèi)型的數(shù)據(jù)一起工作。
在 .NET 6 Preview 2 中,Blazor 可以從祖先組件中推斷出泛型類(lèi)型參數(shù)。祖先組件必須選擇性地加入這種行為,使用 [CascadingTypeParameter] 特性(attribute)來(lái)通過(guò)名稱(chēng)將類(lèi)型參數(shù)級(jí)聯(lián)到后代。這個(gè)特性(attribute)允許泛型類(lèi)型推斷出,對(duì)于有同名類(lèi)型參數(shù)的子代,可以自動(dòng)使用指定的類(lèi)型參數(shù)。
例如,你可以定義像這樣的 Grid 和 Column 組件:
Grid.razor
@typeparam TItem
@attribute [CascadingTypeParameter(nameof(TItem))]
...
@code {
[Parameter] public IEnumerable<TItem> Items { get; set; }
[Parameter] public RenderFragment ChildContent { get; set; }
}
Column.razor
@typeparam TItem
...
@code {
[Parameter] public string Title { get; set; }
}
然后你可以像這樣使用 Grid 和 Column 組件:
<Grid Items="@GetItems()">
<Column Title="Product name" />
<Column Title="Num sales" />
</Grid>
@code {
IEnumerable<SaleRecord> GetItems() { ... }
}
注意:Visual Studio Code 中的 Razor 支持尚未更新以支持該功能,因此即使項(xiàng)目正確構(gòu)建,你也可能遇到誤判的錯(cuò)誤提醒。這將在即將發(fā)布的工具版本中解決。
Blazor 應(yīng)用程序支持保留預(yù)渲染時(shí)的狀態(tài)
Blazor 應(yīng)用程序可從服務(wù)端進(jìn)行預(yù)渲染,以加快應(yīng)用程序可感知的加載時(shí)間。當(dāng)應(yīng)用程序在后臺(tái)進(jìn)行交互性設(shè)置時(shí),可立即渲染預(yù)渲染的 HTML。但是,預(yù)渲染期間使用的任何狀態(tài)都會(huì)丟失,必須在應(yīng)用程序完全加載時(shí)重新創(chuàng)建。如果異步設(shè)置任何狀態(tài),那么當(dāng)預(yù)渲染的 UI 被臨時(shí)占位符替換,然后再次完全渲染時(shí),UI可能會(huì)閃爍。
為了解決這個(gè)問(wèn)題,我們?cè)黾恿诵碌?<preserve-component-state /> tag helper,它可以支持把狀態(tài)持久化到預(yù)渲染頁(yè)面。在你的應(yīng)用程序中,你可以使用新的 ComponentApplicationState 服務(wù)來(lái)決定你要持久化的狀態(tài)。當(dāng)狀態(tài)即將被持久化到預(yù)渲染頁(yè)面中時(shí),ComponentApplicationState.OnPersisting 事件會(huì)被觸發(fā)。然后你就可以在初始化你的組件時(shí)恢復(fù)(retrieved)任何已被持久化的狀態(tài)。
下面的示例展示了如何在預(yù)渲染期間持久化默認(rèn)的 FetchData 組件中的天氣預(yù)報(bào),然后在 Blazor 服務(wù)端應(yīng)用程序中恢復(fù)狀態(tài)以初始化該組件。
_Host.cshtml
<body>
<component type="typeof(App)" render-mode="ServerPrerendered" />
...
@* 當(dāng)所有組件調(diào)用后持久化組件狀態(tài) *@
<persist-component-state />
</body>
FetchData.razor
@page "/fetchdata"
@implements IDisposable
@inject ComponentApplicationState ApplicationState
...
@code {
protected override async Task OnInitializedAsync()
{
ApplicationState.OnPersisting += PersistForecasts;
if (!ApplicationState.TryRedeemPersistedState("fetchdata", out var data))
{
forecasts = await ForecastService.GetForecastAsync(DateTime.Now);
}
else
{
var options = new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
PropertyNameCaseInsensitive = true,
};
forecasts = JsonSerializer.Deserialize<WeatherForecast[]>(data, options);
}
}
private Task PersistForecasts()
{
ApplicationState.PersistAsJson("fetchdata", forecasts);
return Task.CompletedTask;
}
void IDisposable.Dispose()
{
ApplicationState.OnPersisting -= PersistForecasts;
}
}
注意:這個(gè)預(yù)覽版中提供了一個(gè)
TryRedeemFromJson<T>輔助方法,但有一個(gè)已知的問(wèn)題,將會(huì)在未來(lái)的更新中解決。為了解決這個(gè)問(wèn)題,請(qǐng)先使用TryRedeemPersistedState和手動(dòng)JSON反序列化,如上例所示。
通過(guò)使用與預(yù)渲染時(shí)相同的狀態(tài)來(lái)初始化你的組件,任何昂貴的初始化步驟都只需要執(zhí)行一次。新渲染的 UI 也與預(yù)渲染的 UI 相匹配,因此不會(huì)發(fā)生閃爍。
SignalR - Nullable 注解
ASP.NET Cpre SignalR 客戶(hù)端包中已經(jīng)啟用 Nullability。這意味著,當(dāng)您 啟用 Nullability時(shí),C# 編譯器將根據(jù)您在 SignalR API 中對(duì)空值的處理提供適當(dāng)?shù)姆答?。?.NET 5 中,SignalR 服務(wù)器已經(jīng)更新了 Nullability,但在 .NET 6 中做了一些修改。
原文:https://devblogs.microsoft.com/aspnet/asp-net-core-updates-in-net-6-preview-2/?WT.mc_id=DT-MVP-5003987


技巧:微信可以設(shè)置雪花昵稱(chēng)了!

強(qiáng)烈推薦:超全C#幫助類(lèi),提升效率就靠它
點(diǎn)贊和在看就是最大的支持??
