はじめに
MicrosoftLearnでBlazorを学習していると、題材としてDbContext
というものが出てきて、今回おそらく初めて触りました。
使い方の注意点があったので、それをまとめます。
私は普段、C#でWindowsのデスクトップアプリを開発しています。
Webの技術を体系的に学び、しっかり活用した経験がなく用語の理解も怪しいので、超初歩的な内容になってます。
DbContextについて
DbContext
は、Entity Framework Core (EF Core) の主要なクラスで、データベースとの接続、クエリの実行、データの追加・更新・削除など、いい感じにデータベースを扱えるようにしてくれるものみたいです。
大量に機能があったので、詳しくはこちら。
DbContextクラスを継承したクラスを定義することによって、アプリケーションのデータベースに特化した設定や操作を定義できます。
DbContextの注意点
DbContext
は、同時に使用できるよう設計されていません。特に、サーバーアプリケーションのようにスコープが長く続く場合には、DIコンテナから直接DbContextを取得して使用するのは不適切です。
サーバーアプリではスコープが長く続くとは?
Blazorには、WebAssembly(クライアント側)とServer(サーバー側)の2つのホスティングモデルがあります。(こちらの記事にまとめています。)
Blazor Server では、ユーザーのUI操作がサーバー上で処理され、結果がリアルタイムでクライアントに反映されます。各ユーザーのセッションがサーバー上で維持されるため、スコープが長く続きやすいです。
(セッション ≒ スコープだと思ってます。サーバー側とブラウザ側の間の通信が生きている間のことを指しています。リロードしたり、別タブ、別ブラウザなど、通信の単位が異なると、別スコープ(セッション)になる認識です。)
たとえば、ユーザーがブラウザを閉じない限り、1つのセッションが数十分、数時間続くこともあります。この長時間のセッションの間に、同じDbContextインスタンスが再利用されると、複数のリクエストが同時にアクセスする可能性があり、競合やデータの不整合が発生するリスクが高まります。
1操作ごとにDbContextの生成と破棄をするのが、望ましいとされているそうです。
DbContextFactoryの利用
この問題を解決するためにDbContextFactoryを利用するのがよいです。
DbContextFactoryを使用すると、必要なときに新しいDbContextインスタンスを生成し、使用後すぐに破棄することができます。
これにより、スコープが長く続く環境でも安全にデータベースアクセスを行えます。
以下のように使います。
DbContextを継承したクラスを指定して、DbContextFactoryをDI登録します。
// Program.cs
builder.Services.AddDbContextFactory<MyDbContext>();
利用したい箇所にFactory
を注入します。@inject
で可能です。
DbContext
の利用箇所でFactory
から生成します。
使用後は必ず破棄するよう、usingで囲います。
// XXXConponent.razor
@inject IDbContextFactory<MyDbContext> DbContextFactory
@code {
public async Task LoadDataAsync() {
using var context = DbContextFactory.CreateDbContext();
var data = await context.MyEntities.ToListAsync();
// データ処理
}
}
まとめ
使い方の注意点はわかりましたが、DbContextを理解して使いこなすのが大変そうですね、、、。
参考