共用方式為


從 ASP.NET Web Forms 移轉至 Blazor

提示

本內容節錄自《Blazor for ASP NET Web Forms Developers for Azure》電子書,可以從 .NET Docs 取得,也可以免費下載 PDF 離線閱讀。

Blazor-for-ASP-NET-Web-Forms-Developers 電子書封面縮圖。

將程式碼基底從 ASP.NET Web Forms 移轉至 Blazor 是需要規劃的耗時工作。 本章為流程概述。 確保應用程式遵守多層式架構能簡化轉換工作,在應用程式模型 (本案例為 Web Form) 中會與商務邏輯區隔。 這種分層區隔的邏輯可以看清需要移到 .NET Core 和 Blazor 的項目。

本範例使用可在 GitHub 取得的 eShop 應用程式。 eShop 這種目錄服務,可透過表單輸入和驗證提供 CRUD 功能。

為何應該將工作應用程式移轉至 Blazor? 很多時候不需要。 ASP.NET Web Forms 還會支援數年。 不過,Blazor 提供的許多功能只有移轉後的應用程式才支援。 這些功能包括:

  • 架構的效能改善,例如 Span<T>
  • 能以 WebAssembly 形式執行
  • Linux 和 macOS 的跨平台支援
  • 不影響其他應用程式的應用程式本機部署或共用架構部署

只要這些或其他新功能的吸引力夠大,移轉應用程式就有價值。 移轉可以採用不同的狀態,可以是完整的應用程式,或僅有需要變更的某些端點。 是否決定移轉最終取決於開發人員要解決的商務問題。

伺服器端裝載還是用戶端裝載

裝載模型一章所述,Blazor 應用程式有兩種不同的裝載方式:伺服器端和用戶端。 伺服器端模型使用 ASP.NET Core SignalR 連線管理 DOM 更新,同時在伺服器上執行任何實際程式碼。 用戶端模型則以 WebAssembly 形式在瀏覽器中執行,不需要任何伺服器連線。 可能影響特定應用程式最適裝載方式的差異如下:

  • 以 WebAssembly 形式執行不支援目前的所有功能 (例如執行緒)
  • 用戶端與伺服器的聊天通訊可能會導致伺服器端模式的延遲問題
  • 需另有用戶端裝載的服務才能存取資料庫以及內部或受保護的服務

本書撰寫時,伺服器端模型更近似 Web Form。 本章大部分著墨於伺服器端裝載模型,因為其已準備好投入生產環境。

建立新專案

這個初始移轉步驟是要建立新的專案。 此專案類型是以 .NET SDK 樣式的專案為基礎,但簡化了過去專案格式所用的許多重複使用內容。 如需詳細資訊,請參閱專案結構一章。

建立專案之後,請安裝以前專案使用的程式庫。 在過去的 Web Form 專案中,您可能曾使用 packages.config 檔案列出所需的 NuGet 套件。 在新的 SDK 樣式專案中,packages.config 已更換成專案檔中的 <PackageReference> 元素。 此方法的優點是所有相依性皆可以轉移方式安裝。 您只要列出關心的最上層相依性即可。

您現在使用的許多相依性皆可用於 .NET,包括 Entity Framework 6 和 log4net。 如果沒有可用的 .NET 或 .NET Standard 版本,通常可使用 .NET Framework 版本。 您需要的時間可能不一樣。 任何已使用但 .NET 不提供的 API 都會造成執行階段錯誤。 Visual Studio 會通知您這類套件的相關訊息。 在方案總管中,專案的 [參考] 節點上會顯示黃色圖示。

在 Blazor 式 eShop 專案中,您可以看到已安裝的套件。 過去,packages.config 檔案會列出專案使用的每個套件,以致檔案長度接近 50 行。 packages.config 的程式碼片段如下:

<?xml version="1.0" encoding="utf-8"?>
<packages>
  ...
  <package id="Microsoft.ApplicationInsights.Agent.Intercept" version="2.4.0" targetFramework="net472" />
  <package id="Microsoft.ApplicationInsights.DependencyCollector" version="2.9.1" targetFramework="net472" />
  <package id="Microsoft.ApplicationInsights.PerfCounterCollector" version="2.9.1" targetFramework="net472" />
  <package id="Microsoft.ApplicationInsights.Web" version="2.9.1" targetFramework="net472" />
  <package id="Microsoft.ApplicationInsights.WindowsServer" version="2.9.1" targetFramework="net472" />
  <package id="Microsoft.ApplicationInsights.WindowsServer.TelemetryChannel" version="2.9.1" targetFramework="net472" />
  <package id="Microsoft.AspNet.FriendlyUrls" version="1.0.2" targetFramework="net472" />
  <package id="Microsoft.AspNet.FriendlyUrls.Core" version="1.0.2" targetFramework="net472" />
  <package id="Microsoft.AspNet.ScriptManager.MSAjax" version="5.0.0" targetFramework="net472" />
  <package id="Microsoft.AspNet.ScriptManager.WebForms" version="5.0.0" targetFramework="net472" />
  ...
  <package id="System.Memory" version="4.5.1" targetFramework="net472" />
  <package id="System.Numerics.Vectors" version="4.4.0" targetFramework="net472" />
  <package id="System.Runtime.CompilerServices.Unsafe" version="4.5.0" targetFramework="net472" />
  <package id="System.Threading.Channels" version="4.5.0" targetFramework="net472" />
  <package id="System.Threading.Tasks.Extensions" version="4.5.1" targetFramework="net472" />
  <package id="WebGrease" version="1.6.0" targetFramework="net472" />
</packages>

<packages> 元素包含所有必要的相依性。 因為您需要這些套件,所以很難識別哪些包含在內。 有些 <package> 元素只為滿足您需要的相依性需求而列出。

Blazor 專案會在專案檔的 <ItemGroup> 元素內列出您需要的相依性:

<ItemGroup>
    <PackageReference Include="Autofac" Version="4.9.3" />
    <PackageReference Include="EntityFramework" Version="6.4.4" />
    <PackageReference Include="log4net" Version="2.0.12" />
    <PackageReference Include="Microsoft.Extensions.Logging.Log4Net.AspNetCore" Version="2.2.12" />
</ItemGroup>

Windows 相容性套件是能讓 Web Form 開發人員生活更簡單的一種 NuGet 套件。 雖然 .NET 跨平台,但有些功能僅 Windows 提供。 安裝相容性套件即可取得 Windows 特定功能。 登錄、WMI 和目錄服務等等,皆屬於這類功能。 此套件新增約 20,000 個 API,並啟用許多您可能已熟悉的服務。 eShop 專案不需要相容性套件,但如果您的專案使用 Windows 特定功能,此類套件可減輕移轉工作。

啟用啟動流程

Web Form 的 Blazor 啟動流程已變更,且要遵循其他 ASP.NET Core 服務的類似設定。 裝載伺服器端時,Razor 元件會在一般 ASP.NET Core 應用程式中執行。 以 WebAssembly 形式裝載在瀏覽器中時,Razor 元件會使用類似的裝載模型。 差異在於這些元件是和所有後端處理序分開執行的服務。 不管什麼方式,啟動都類似。

Global.asax.cs 檔案是 Web Form 專案的預設啟動頁面。 在 eShop 專案中,此檔案會設定控制反轉 (IoC) 容器,並處理應用程式或要求的各種生命週期事件。 這些事件有些就是使用中介軟體處理 (例如 Application_BeginRequest)。 其他事件需要透過相依性插入 (DI) 覆寫特定服務。

例如 eShop 的 Global.asax.cs 檔案包含下列程式碼:

public class Global : HttpApplication, IContainerProviderAccessor
{
    private static readonly ILog _log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

    static IContainerProvider _containerProvider;
    IContainer container;

    public IContainerProvider ContainerProvider
    {
        get { return _containerProvider; }
    }

    protected void Application_Start(object sender, EventArgs e)
    {
        // Code that runs on app startup
        RouteConfig.RegisterRoutes(RouteTable.Routes);
        BundleConfig.RegisterBundles(BundleTable.Bundles);
        ConfigureContainer();
        ConfigDataBase();
    }

    /// <summary>
    /// Track the machine name and the start time for the session inside the current session
    /// </summary>
    protected void Session_Start(Object sender, EventArgs e)
    {
        HttpContext.Current.Session["MachineName"] = Environment.MachineName;
        HttpContext.Current.Session["SessionStartTime"] = DateTime.Now;
    }

    /// <summary>
    /// https://autofaccn.readthedocs.io/en/latest/integration/webforms.html
    /// </summary>
    private void ConfigureContainer()
    {
        var builder = new ContainerBuilder();
        var mockData = bool.Parse(ConfigurationManager.AppSettings["UseMockData"]);
        builder.RegisterModule(new ApplicationModule(mockData));
        container = builder.Build();
        _containerProvider = new ContainerProvider(container);
    }

    private void ConfigDataBase()
    {
        var mockData = bool.Parse(ConfigurationManager.AppSettings["UseMockData"]);

        if (!mockData)
        {
            Database.SetInitializer<CatalogDBContext>(container.Resolve<CatalogDBInitializer>());
        }
    }

    protected void Application_BeginRequest(object sender, EventArgs e)
    {
        //set the property to our new object
        LogicalThreadContext.Properties["activityid"] = new ActivityIdHelper();

        LogicalThreadContext.Properties["requestinfo"] = new WebRequestInfo();

        _log.Debug("Application_BeginRequest");
    }
}

上述檔案會變成伺服器端 Blazor 的 Program.cs 檔案:

using eShopOnBlazor.Models;
using eShopOnBlazor.Models.Infrastructure;
using eShopOnBlazor.Services;
using log4net;
using System.Data.Entity;
using eShopOnBlazor;

var builder = WebApplication.CreateBuilder(args);

// add services

builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor();

if (builder.Configuration.GetValue<bool>("UseMockData"))
{
    builder.Services.AddSingleton<ICatalogService, CatalogServiceMock>();
}
else
{
    builder.Services.AddScoped<ICatalogService, CatalogService>();
    builder.Services.AddScoped<IDatabaseInitializer<CatalogDBContext>, CatalogDBInitializer>();
    builder.Services.AddSingleton<CatalogItemHiLoGenerator>();
    builder.Services.AddScoped(_ => new CatalogDBContext(builder.Configuration.GetConnectionString("CatalogDBContext")));
}

var app = builder.Build();

new LoggerFactory().AddLog4Net("log4Net.xml");

if (app.Environment.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
}
else
{
    app.UseExceptionHandler("/Home/Error");
}

// Middleware for Application_BeginRequest
app.Use((ctx, next) =>
{
    LogicalThreadContext.Properties["activityid"] = new ActivityIdHelper(ctx);
    LogicalThreadContext.Properties["requestinfo"] = new WebRequestInfo(ctx);
    return next();
});

app.UseStaticFiles();

app.UseRouting();

app.UseEndpoints(endpoints =>
{
    endpoints.MapBlazorHub();
    endpoints.MapFallbackToPage("/_Host");
});

ConfigDataBase(app);

void ConfigDataBase(IApplicationBuilder app)
{
    using (var scope = app.ApplicationServices.CreateScope())
    {
        var initializer = scope.ServiceProvider.GetService<IDatabaseInitializer<CatalogDBContext>>();

        if (initializer != null)
        {
            Database.SetInitializer(initializer);
        }
    }
}

app.Run();

您可能會注意到 Web Form 有一項重大變更,即顯著的相依性插入 (DI)。 DI 曾是 ASP.NET Core 設計中的指導原則。 其幾乎支援自訂 ASP.NET Core 架構的所有層面。 甚至有一個可供多種情況使用的內建服務提供者。 如果需要更多自訂,還有許多社群專案可提供支援。 例如,您可以推進協力廠商 DI 程式庫的投資。

原始的 eShop 應用程式中有一些管理工作階段的組態。 因為伺服器端 Blazor 使用 ASP.NET Core SignalR 通訊,所以可能會因為連線與 HTTP 內容無關而不支援工作階段狀態。 使用工作階段狀態的應用程式需要重新架構後,才能當成 Blazor 應用程式執行。

如需應用程式啟動的詳細資訊,請參閱應用程式啟動

將 HTTP 模組和處理常式移轉至中介軟體

Web Form 經常使用 HTTP 模組和處理常式來控制 HTTP 要求管線。 實作 IHttpModuleIHttpHandler 的類別可予以註冊,亦可處理內送要求。 Web Form 在 web.config 檔案中設定模組和處理常式。 Web Form 也非常依賴應用程式生命週期事件處理。 ASP.NET Core 改用中介軟體。 中介軟體會使用 Startup 類別的 Configure 方法註冊。 註冊順序決定中介軟體的執行順序。

啟用啟動流程一節中,生命週期事件被 Web Form 引發為 Application_BeginRequest 方法。 ASP.NET Core 不提供此事件。 達成此行為的方法是實作中介軟體,如 Startup.cs 檔案範例所示。 此中介軟體會執行相同的邏輯,然後將控制權傳輸至中介軟體管線中的下一個處理常式。

如需移轉模組和處理常式的詳細資訊,請參閱將 HTTP 處理常式和模組移轉至 ASP.NET Core 中介軟體

移轉靜態檔案

中介軟體必須公開檔案,才能儲存靜態檔案 (例如 HTML、CSS、映像和 JavaScript)。 呼叫 UseStaticFiles 方法可讓您從 Web 根路徑提供靜態檔案。 預設 Web 根目錄是 wwwroot,但可自訂。 如 Program.cs 檔案中的內容:

...

app.UseStaticFiles();

...

eShop 專案會啟用基本的靜態檔案存取。 有許多自訂可用於靜態檔案存取。 如需啟用預設檔案或檔案瀏覽器的相關資訊,請參閱 ASP.NET Core 中的靜態檔案

移轉執行階段統合和縮制設定

統合和縮制是效能最佳化技術,可減少伺服器要求的數目和大小,以擷取特定檔案類型。 JavaScript 和 CSS 通常會先經過某種形式的統合或縮制,再傳送到用戶端。 ASP.NET Web Forms 會在執行階段處理這些最佳化。 最佳化慣例定義在 App_Start/BundleConfig.cs 檔案中。 ASP.NET Core 採用更接近宣告式的方法。 檔案會列出要縮製的檔案及特定的縮制設定。

如需統合和縮制的詳細資訊,請參閱組合與縮製 ASP.NET Core 中的靜態資產

移轉 ASPX 頁面

Web Form 應用程式頁面是副檔名為 .aspx 的檔案。 Web Form 頁面通常可對應至 Blazor 中的元件。 Razor 元件是在副檔名為 .razor 的檔案中撰寫。 eShop 專案會有五個頁面轉換成 Razor 頁面。

例如,詳細資料檢視包含 Web Form 專案中的三個檔案:Details.aspxDetails.aspx.csDetails.aspx.designer.cs。 轉換成 Blazor 時,程式碼後置和標記會合併為 Details.razor。 Razor 編譯 (相當於 .designer.cs 檔案中的內容) 儲存在 obj 目錄中,而且預設不顯示在方案總管中。 Web Form 頁面包含下列標記:

<%@ Page Title="Details" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="Details.aspx.cs" Inherits="eShopLegacyWebForms.Catalog.Details" %>

<asp:Content ID="Details" ContentPlaceHolderID="MainContent" runat="server">
    <h2 class="esh-body-title">Details</h2>

    <div class="container">
        <div class="row">
            <asp:Image runat="server" CssClass="col-md-6 esh-picture" ImageUrl='<%#"/Pics/" + product.PictureFileName%>' />
            <dl class="col-md-6 dl-horizontal">
                <dt>Name
                </dt>

                <dd>
                    <asp:Label runat="server" Text='<%#product.Name%>' />
                </dd>

                <dt>Description
                </dt>

                <dd>
                    <asp:Label runat="server" Text='<%#product.Description%>' />
                </dd>

                <dt>Brand
                </dt>

                <dd>
                    <asp:Label runat="server" Text='<%#product.CatalogBrand.Brand%>' />
                </dd>

                <dt>Type
                </dt>

                <dd>
                    <asp:Label runat="server" Text='<%#product.CatalogType.Type%>' />
                </dd>
                <dt>Price
                </dt>

                <dd>
                    <asp:Label CssClass="esh-price" runat="server" Text='<%#product.Price%>' />
                </dd>

                <dt>Picture name
                </dt>

                <dd>
                    <asp:Label runat="server" Text='<%#product.PictureFileName%>' />
                </dd>

                <dt>Stock
                </dt>

                <dd>
                    <asp:Label runat="server" Text='<%#product.AvailableStock%>' />
                </dd>

                <dt>Restock
                </dt>

                <dd>
                    <asp:Label runat="server" Text='<%#product.RestockThreshold%>' />
                </dd>

                <dt>Max stock
                </dt>

                <dd>
                    <asp:Label runat="server" Text='<%#product.MaxStockThreshold%>' />
                </dd>

            </dl>
        </div>

        <div class="form-actions no-color esh-link-list">
            <a runat="server" href='<%# GetRouteUrl("EditProductRoute", new {id =product.Id}) %>' class="esh-link-item">Edit
            </a>
            |
            <a runat="server" href="~" class="esh-link-item">Back to list
            </a>
        </div>

    </div>
</asp:Content>

上述標記的程式碼後置包含下列程式碼:

using eShopLegacyWebForms.Models;
using eShopLegacyWebForms.Services;
using log4net;
using System;
using System.Web.UI;

namespace eShopLegacyWebForms.Catalog
{
    public partial class Details : System.Web.UI.Page
    {
        private static readonly ILog _log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

        protected CatalogItem product;

        public ICatalogService CatalogService { get; set; }

        protected void Page_Load(object sender, EventArgs e)
        {
            var productId = Convert.ToInt32(Page.RouteData.Values["id"]);
            _log.Info($"Now loading... /Catalog/Details.aspx?id={productId}");
            product = CatalogService.FindCatalogItem(productId);

            this.DataBind();
        }
    }
}

轉換成 Blazor 時,Web Form 頁面會轉譯為下列程式碼:

@page "/Catalog/Details/{id:int}"
@inject ICatalogService CatalogService
@inject ILogger<Details> Logger

<h2 class="esh-body-title">Details</h2>

<div class="container">
    <div class="row">
        <img class="col-md-6 esh-picture" src="@($"/Pics/{_item.PictureFileName}")">

        <dl class="col-md-6 dl-horizontal">
            <dt>
                Name
            </dt>

            <dd>
                @_item.Name
            </dd>

            <dt>
                Description
            </dt>

            <dd>
                @_item.Description
            </dd>

            <dt>
                Brand
            </dt>

            <dd>
                @_item.CatalogBrand.Brand
            </dd>

            <dt>
                Type
            </dt>

            <dd>
                @_item.CatalogType.Type
            </dd>
            <dt>
                Price
            </dt>

            <dd>
                @_item.Price
            </dd>

            <dt>
                Picture name
            </dt>

            <dd>
                @_item.PictureFileName
            </dd>

            <dt>
                Stock
            </dt>

            <dd>
                @_item.AvailableStock
            </dd>

            <dt>
                Restock
            </dt>

            <dd>
                @_item.RestockThreshold
            </dd>

            <dt>
                Max stock
            </dt>

            <dd>
                @_item.MaxStockThreshold
            </dd>

        </dl>
    </div>

    <div class="form-actions no-color esh-link-list">
        <a href="@($"/Catalog/Edit/{_item.Id}")" class="esh-link-item">
            Edit
        </a>
        |
        <a href="/" class="esh-link-item">
            Back to list
        </a>
    </div>

</div>

@code {
    private CatalogItem _item;

    [Parameter]
    public int Id { get; set; }

    protected override void OnInitialized()
    {
        Logger.LogInformation("Now loading... /Catalog/Details/{Id}", Id);

        _item = CatalogService.FindCatalogItem(Id);
    }
}

請注意,程式碼和標記位於同一檔案中。 使用 @inject 屬性可將所有必要的服務變成可存取。 藉由 @page 指示詞,您可在 Catalog/Details/{id} 路由存取此頁面。 路由的 {id} 預留位置值已限制為整數。 如路由一節所述,Razor 元件會明確指出其路由和包含的任何參數,這是和 Web Form 的差異。 許多 Web Form 控制項可能沒有確切的 Blazor 對應項。 通常有對等的 HTML 程式碼片段會提供同樣的用途。 例如,<asp:Label /> 控制項可為 HTML <label> 元素所取代。

Blazor 中的模型驗證

如果您的 Web Form 程式碼包含驗證,您可以傳輸大部分內容,幾乎不需要變更。 在 Blazor 中執行的優點是可以執行相同的驗證邏輯,卻不需要自訂 JavaScript。 資料註解讓模型驗證變得更容易。

例如,Create.aspx 頁面有包含驗證的資料輸入表單。 範例程式碼片段看起來會像這樣:

<div class="form-group">
    <label class="control-label col-md-2">Name</label>
    <div class="col-md-3">
        <asp:TextBox ID="Name" runat="server" CssClass="form-control"></asp:TextBox>
        <asp:RequiredFieldValidator runat="server" ControlToValidate="Name" Display="Dynamic"
            CssClass="field-validation-valid text-danger" ErrorMessage="The Name field is required." />
    </div>
</div>

Blazor 會在 Create.razor 檔案中提供對等標記:

<EditForm Model="_item" OnValidSubmit="@...">
    <DataAnnotationsValidator />

    <div class="form-group">
        <label class="control-label col-md-2">Name</label>
        <div class="col-md-3">
            <InputText class="form-control" @bind-Value="_item.Name" />
            <ValidationMessage For="(() => _item.Name)" />
        </div>
    </div>

    ...
</EditForm>

EditForm 內容包含驗證支援,而且可為輸入所圍繞。 資料註解是新增驗證的常見方式。 您可以透過 DataAnnotationsValidator 元件新增這類驗證支援。 如需此機制的詳細資訊,請參閱 ASP.NET Core Blazor 表單和驗證

移轉組態

在 Web Form 專案中,組態資料最常儲存在 web.config 檔案中。 組態資料可使用 ConfigurationManager 存取。 剖析物件通常需要有服務。 .NET Framework 4.7.2 已透過 ConfigurationBuilders 將可組合性新增至組態。 這些建立器可讓開發人員為要在執行階段撰寫的組態新增各種來源,以擷取必要的值。

ASP.NET Core 引入了彈性組態系統,可讓您定義應用程式與部署所使用的組態來源。 可能用在 Web Form 應用程式中的 ConfigurationBuilder 基礎結構,是在 ASP.NET Core 組態系統使用此概念後所建立的模型。

下列程式碼片段示範 Web Form eShop 專案如何使用 web.config 儲存組態值:

<configuration>
  <configSections>
    <section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
  </configSections>
  <connectionStrings>
    <add name="CatalogDBContext" connectionString="Data Source=(localdb)\MSSQLLocalDB; Initial Catalog=Microsoft.eShopOnContainers.Services.CatalogDb; Integrated Security=True; MultipleActiveResultSets=True;" providerName="System.Data.SqlClient" />
  </connectionStrings>
  <appSettings>
    <add key="UseMockData" value="true" />
    <add key="UseCustomizationData" value="false" />
  </appSettings>
</configuration>

這對要儲存在 web.config 中的祕密很常見,例如資料庫連接字串。祕密難免會保存在不安全的位置,例如原始檔控制。 使用 ASP.NET Core 上的 Blazor,上述 XML 型組態會更換為下列 JSON:

{
  "ConnectionStrings": {
    "CatalogDBContext": "Data Source=(localdb)\\MSSQLLocalDB; Initial Catalog=Microsoft.eShopOnContainers.Services.CatalogDb; Integrated Security=True; MultipleActiveResultSets=True;"
  },
  "UseMockData": true,
  "UseCustomizationData": false
}

JSON 是預設的組態格式,但 ASP.NET Core 支援許多其他格式,包括 XML。 另外還有數種社群支援的格式。

您可以從 Program.cs 中的建立器存取組態值:

if (builder.Configuration.GetValue<bool>("UseMockData"))
{
    builder.Services.AddSingleton<ICatalogService, CatalogServiceMock>();
}
else
{
    builder.Services.AddScoped<ICatalogService, CatalogService>();
    builder.Services.AddScoped<IDatabaseInitializer<CatalogDBContext>, CatalogDBInitializer>();
    builder.Services.AddSingleton<CatalogItemHiLoGenerator>();
    builder.Services.AddScoped(_ => new CatalogDBContext(builder.Configuration.GetConnectionString("CatalogDBContext")));
}

根據預設,環境變數、JSON 檔案 (appsettings.jsonappsettings.{Environment}.json) 和命令列選項都會註冊為組態物件中的有效組態來源。 您可以透過 Configuration[key] 存取組態來源。 更進階的技術是使用選項模式將組態資料繫結至物件。 如需組態和選項模式的詳細資訊,請分別參閱 ASP.NET Core 的組態ASP.NET Core 的選項模式

移轉資料存取

資料存取對任何應用程式都很重要。 eShop 專案會將目錄資訊儲存在資料庫中,並使用 Entity Framework (EF) 6 擷取資料。 因為 .NET 5 支援 EF 6,所以專案可以繼續使用。

eShop 需要有下列 EF 相關變更:

  • 在 .NET Framework 中,DbContext 物件接受格式為 name=ConnectionString 的字串,並使用 ConfigurationManager.AppSettings[ConnectionString] 的連接字串連線。 .NET Core 不支援此功能。 您必須提供連接字串。
  • 資料庫是以同步方式存取。 雖然運作正常,但可擴縮性會受到影響。 此邏輯應移至非同步模式。

雖然資料集繫結沒有相同的原生支援,但 Blazor 會在 Razor 頁面中提供其 C# 支援的彈性和功能。 例如,您可以執行計算並顯示結果。 如需 Blazor 資料模式的詳細資訊,請參閱資料存取一章。

架構變更

最後,移轉至 Blazor 時,還需要考慮一些重要的架構差異。 其中許多變更都適用於以 .NET Core 或 ASP.NET Core 為基礎的所有專案。

因為 Blazor 建置在 .NET Core 上,所以會有些考量以確保 .NET Core 支援。 有些重大變更包括移除下列功能:

  • 多個 AppDomain
  • 遠端
  • 程式碼存取安全性 (CAS)
  • 安全性透明度

如需相關技術的詳細資訊,以識別支援在 .NET Core 中執行作業所需之變更,請參閱將程式碼從 .NET Framework 移植到 .NET Core

ASP.NET Core 是重新詮釋的 ASP.NET,有些變更可能一開始並不明顯。 主要變更如下:

  • 沒有同步處理內容,這表示沒有 HttpContext.CurrentThread.CurrentPrincipal 或其他靜態存取子
  • 無陰影複製
  • 無要求佇列

ASP.NET Core 中有許多非同步作業,可讓您更輕鬆卸載 I/O 繫結工作。 請絕對不要使用 Task.Wait()Task.GetResult() 執行封鎖,這會快速耗盡執行緒集區資源。

移轉結論

此時,您已見過許多範例,知道將 Web Form 專案移至 Blazor 所需的一切。 有關完整範例,請參閱 eShopOnBlazor 專案。