ASP.NET Core Blazor と Entity Framework 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 サービスの場合は、マネージド ID を使用することをお勧めします。 マネージド ID を使用すると、アプリ コードに資格情報を保存せずに、Azure サービスに対して安全に認証を行うことができます。 詳細については、次のリソースを参照してください。
- Azure リソースのマネージド ID とは (Microsoft Entra のドキュメント)
- Azure サービスのドキュメント
Blazorムービー データベース アプリのチュートリアルを作成する
データベース操作に
データベース アクセス
EF Core では、DbContextし、"作業単位" として機能する手段として を利用しています。 EF Core には、コンテキストをAddDbContext サービスとして登録する ASP.NET Core アプリの 拡張機能が用意されています。 サーバー側の Blazor アプリでは、インスタンスがユーザーの回線内のコンポーネント全体で共有されるため、スコープ サービスの登録が問題になる可能性があります。 DbContext はスレッド セーフではなく、同時に使用するように設計されていません。 次の理由により、既存の有効期間は不適切です。
- [Singleton](シングルトン) の場合、アプリのすべてのユーザーで状態が共有され、不適切な同時使用につながります。
- [範囲指定] (既定値) の場合、同じユーザーのコンポーネント間で同様の問題が発生します。
- [一時的] の場合、要求ごとに新しいインスタンスが生成されます。ただし、コンポーネントの有効期間が長くなる可能性があるため、意図したよりも時間のかかるコンテキストになります。
以下の推奨事項は、サーバー側の EF Core アプリで Blazor を使用する上で一貫したアプローチを提供するように設計されています。
操作ごとに 1 つのコンテキストを使用することを検討してください。 コンテキストは、高速でオーバーヘッドの少ないインスタンス化を目的として設計されています。
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
を使用して新しいインスタンスを作成することです。 ただし、追加の依存関係を解決する必要がある次のようなシナリオがあります。
DbContextOptions
を使用してコンテキストを構成する。- DbContext ごとに接続文字列を使用する (たとえば、ASP.NET Core の Identity モデルを使用する場合など)。 詳細については、マルチテナントに関するページ (EF Core ドキュメント) を参照してください。
警告
アプリ シークレット、接続文字列、資格情報、パスワード、個人識別番号 (PIN)、プライベート C#/.NET コード、秘密キー/トークンをクライアント側コードに格納しないでください。これは安全ではありません。 テスト/ステージング環境と運用環境では、サーバー側の Blazor コードと Web API は、プロジェクト コードまたは構成ファイル内で資格情報を維持しないように、セキュリティで保護された認証フローを使用する必要があります。 ローカル開発テスト以外では、環境変数が最も安全なアプローチではないため、環境変数を使用して機密データを格納しないようにすることをお勧めします。 ローカル開発テストでは、機密データをセキュリティで保護するために、 Secret Manager ツール をお勧めします。 詳細については、「 機密データと資格情報を安全に管理するを参照してください。
依存関係を持つ新しい 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);
}
}
前のファクトリは、次のようになっています。
- すべての依存関係は、サービス プロバイダーを介して ActivatorUtilities.CreateInstance によって満たされています。
- IDbContextFactory<TContext> は EF Core ASP.NET Core 5.0 以降で使用できるため、上記のインターフェイスは ASP.NET Core 3.x にのみ必要です。
次の例では、SQLite
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"));
ファクトリがコンポーネントに挿入され、新しい DbContext インスタンスが作成されます。
データベース コンテキストのスコープをコンポーネント メソッドに設定する
ファクトリはコンポーネントに挿入されます。
@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
その他のリソース
ASP.NET Core