共用方式為


搭配 Entity Framework Core (Blazor) 的 ASP.NET Core EF Core

注意

這不是這篇文章的最新版本。 如需目前的版本,請參閱 本文的 .NET 9 版本。

警告

不再支援此版本的 ASP.NET Core。 如需詳細資訊,請參閱 .NET 和 .NET Core 支持原則。 如需目前的版本,請參閱 本文的 .NET 9 版本。

重要

這些發行前產品的相關資訊在產品正式發行前可能會有大幅修改。 Microsoft 對此處提供的資訊,不做任何明確或隱含的瑕疵擔保。

如需目前的版本,請參閱 本文的 .NET 9 版本。

本文說明如何在伺服器端 EF Core 應用程式中使用

伺服器端 Blazor 是具狀態的應用程式架構。 應用程式會維持伺服器的持續連線,且使用者的狀態會以線路形式保留在伺服器記憶體中。 使用者狀態的其中一個範例,是在線路範圍內的相依性插入 (DI) 服務執行個體中保存的資料。 Blazor 提供的唯一應用程式模型需要使用 Entity Framework Core 的特殊方法。

注意

本文說明伺服器端 EF Core 應用程式中的 Blazor。 Blazor WebAssembly 應用程式會在 WebAssembly 沙箱中執行,以防止大多數直接的資料庫連接。 在 EF Core 中執行 Blazor WebAssembly 超出本文的範圍。

本指南適用於在 Blazor Web App 中採用互動式伺服器端轉譯 (互動式 SSR) 的元件。

本指導適用於託管 Server 解決方案或 Blazor WebAssembly 應用程式的 Blazor Server 專案。

生產應用程式所需的安全驗證流程

本文與使用不需要使用者驗證的本機資料庫有關。 實際執行應用程式應該使用可用的最安全驗證流程。 如需已部署測試和生產 Blazor 應用程式驗證的詳細資訊,請參閱 Blazor安全性和 Identity 節點中的文章。

針對 Microsoft Azure 服務,我們建議使用 受控識別。 受控識別可以以安全的方式向 Azure 服務進行驗證,而無需在應用程式程式碼中儲存認證。 如需詳細資訊,請參閱以下資源:

建置 Blazor 電影資料庫應用程式教學課程

如需建置使用資料庫作業 EF Core 之應用程式的教學課程體驗,請參閱 建置 Blazor 電影資料庫應用程式 (概觀)。 本教學課程說明如何建立 Blazor Web App ,以在電影資料庫中顯示和管理電影。

資料庫存取

EF Core 會依賴 DbContext 作為設定資料庫存取的方法,並作為工作單位。 EF Core 會針對將內容註冊為AddDbContext服務的 ASP.NET Core 應用程式,提供 延伸模組。 在伺服器端 Blazor 應用程式中,範圍服務註冊可能會造成問題,因為執行個體會在使用者線路內的元件之間共用。 DbContext 不是安全的執行緒,而且不是針對同時使用而設計。 現有的存留期不合適,原因如下:

  • Singleton 會共用應用程式所有使用者的狀態,並造成不合適的同時使用。
  • 範圍 (預設值) 會在相同使用者元件之間造成類似的問題。
  • 暫時性會造成每個要求產生新的執行個體;但是,由於元件可以長期存在,因此會產生比預期留存更久的內容。

下列建議旨在提供在伺服器端 EF Core 應用程式中使用 Blazor 的一致方法。

  • 請考慮針對每個作業使用一個內容。 內容是專為快速、低負荷具現化而設計:

    using var context = new ProductsDatabaseContext();
    
    return await context.Products.ToListAsync();
    
  • 使用旗標來防止多個同時作業:

    if (Loading)
    {
        return;
    }
    
    try
    {
        Loading = true;
    
        ...
    }
    finally
    {
        Loading = false;
    }
    

    將資料庫作業放在 try 區塊的 Loading = true; 行之後。

    執行緒安全性不是問題,因此載入邏輯不需要鎖定資料庫記錄。 載入邏輯是用來停用 UI 控制項,讓使用者在擷取資料時不會意外選取按鈕或更新欄位。

  • 如果有多個執行緒可能存取相同的程式碼區塊,插入處理站,並為每個作業建立新的執行個體。 否則,插入和使用內容通常就已足夠。

  • 針對利用 EF Core 的變更追蹤並行控制的較長存留期作業,將內容範圍限制在元件的存留期內

新的 DbContext 執行個體

建立新 DbContext 執行個體的最快方式是使用 new 來建立新的執行個體。 不過,在某些情況下,需要解決其他相依性:

警告

請勿在用戶端程式代碼中儲存應用程式密碼、連接字串、認證、密碼、個人標識碼 (PIN)、私人 C#/.NET 程式代碼或私鑰/令牌,這一律不安全。 在測試/預備和生產環境中,伺服器端 Blazor 程序代碼和 Web API 應該使用安全驗證流程,以避免在專案程式代碼或組態檔內維護認證。 在本機開發測試之外,建議您避免使用環境變數來儲存敏感數據,因為環境變數不是最安全的方法。 針對本機開發測試, 建議使用秘密管理員工具 來保護敏感數據。 如需詳細資訊,請參閱 安全地維護敏感數據和認證

使用相依性建立新 DbContext 的建議方法是使用處理站。 EF Core 5.0 或更新版本提供內建處理站來建立新內容。

在 5.0 之前的 .NET 版本中,請使用下列 DbContextFactory

using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;

namespace BlazorServerDbContextExample.Data
{
    public class DbContextFactory<TContext> 
        : IDbContextFactory<TContext> where TContext : DbContext
    {
        private readonly IServiceProvider provider;

        public DbContextFactory(IServiceProvider provider)
        {
            this.provider = provider ?? throw new ArgumentNullException(
                $"{nameof(provider)}: You must configure an instance of " +
                "IServiceProvider");
        }

        public TContext CreateDbContext() => 
            ActivatorUtilities.CreateInstance<TContext>(provider);
    }
}

在上述處理站中:

下列範例會設定 SQLite,並在管理聯繫人的應用程式中啟用數據記錄。 程式碼會使用擴充方法 (AddDbContextFactory) 來設定適用於 DI 的資料庫處理站,並提供預設選項:

builder.Services.AddDbContextFactory<ContactContext>(opt =>
    opt.UseSqlite($"Data Source={nameof(ContactContext.ContactsDb)}.db"));
services.AddDbContextFactory<ContactContext>(opt =>
    opt.UseSqlite($"Data Source={nameof(ContactContext.ContactsDb)}.db"));

Factory 會插入元件,以建立新的 DbContext 實例。

將資料庫上下文限制到元件方法

Factory 會插入元件:

@inject IDbContextFactory<ContactContext> DbFactory

使用工廠(DbFactory)為方法創建 DbContext

private async Task DeleteContactAsync()
{
    using var context = DbFactory.CreateDbContext();

    if (context.Contacts is not null)
    {
        var contact = await context.Contacts.FirstAsync(...);

        if (contact is not null)
        {
            context.Contacts?.Remove(contact);
            await context.SaveChangesAsync();
        }
    }
}

將資料庫內容的範圍設定為元件的存留期

您可能想要建立存在於元件存留期的 DbContext。 這可讓您將其作為工作單位,並利用內建功能,例如變更追蹤和並行解析。

實作 IDisposable 並將處理站插入元件:

@implements IDisposable
@inject IDbContextFactory<ContactContext> DbFactory

建立 DbContext的屬性:

private ContactContext? Context { get; set; }

OnInitializedAsync 被覆蓋以生成 DbContext

protected override async Task OnInitializedAsync()
{
    Context = DbFactory.CreateDbContext();
}

當元件被處置時,也會處置 DbContext

public void Dispose() => Context?.Dispose();

啟用敏感資料記錄

EnableSensitiveDataLogging 在例外狀況訊息和架構記錄中包含應用程式資料。 記錄的資料可以包含指派給實體執行個體屬性的值,以及傳送至資料庫的命令的參數值。 使用 EnableSensitiveDataLogging 記錄資料是安全性風險,因為可能會在記錄對資料庫執行的 SQL 陳述式時,暴露密碼和其他個人識別資訊 (PII)

我們建議只針對開發和測試啟用 EnableSensitiveDataLogging

#if DEBUG
    services.AddDbContextFactory<ContactContext>(opt =>
        opt.UseSqlite($"Data Source={nameof(ContactContext.ContactsDb)}.db")
        .EnableSensitiveDataLogging());
#else
    services.AddDbContextFactory<ContactContext>(opt =>
        opt.UseSqlite($"Data Source={nameof(ContactContext.ContactsDb)}.db"));
#endif

其他資源