はじめに
今更ながら、Entity Framework の DB 接続の解放方法について、usingステートメントを使用した場合と ASP.NET Core 組み込みの DI コンテナを使った場合の違いについて調べました。
外部リソースの解放について
DB 接続など.NETの管理外の外部リソース1は、使い終わったら破棄する必要があります。
EF Coreでは DbContextを継承したクラスを使用してDB 接続を行います。
public class FooDbContext : DbContext {
public FooDbContext(DbContextOptions<FooDbContext> options) :base(options) { }
public DbSet<BarEntity> BarEntities { get; set; }
}
DB 接続を破棄するにはDbContext.Disposeメソッドを呼び出します。
確実にDisposeするには、
-
usingステートメントを使用する - フレームワーク
ASP.NET Coreの DI コンテナに任せる
方法があります。2
usingステートメントで外部リソースの破棄する
usingステートメントを使用するとブロック{}を抜けた際に自動でDisposeが呼ばれます。
途中でreturnしたり、例外がスローされても確実にDisposeされます。
アプリケーションでDbContextを使用する場合、後述のASP.NET Coreの DI コンテナに任せるので、usingはユニットテストなどで使用することが多いです。
using (var dbContext = new FooDbContext()) {
var bars = dbContext.BarEntities.ToLoist();
return bars;
} // <- ここで FooDbContext.Dispose() される
ASP.NET Coreの DI コンテナに破棄を任せる
ASP.NET Coreの DI コンテナを使用すると、DbContextのインスタンスの管理はフレームワークに任されます。
DI されたDbContextのインスタンスのライフタイムは利用側のスコープと同等で、利用側のスコープを抜けた時にDbContext.Disposeが呼ばれます。3
DI コンテナにDbContextを設定するにはStartupクラスでAddDbContextを使います。
public void ConfigureServices(IServiceCollection services) {
services.AddDbContext<FooDbContext>(options => {
options.UseSqlServer("接続文字列");
});
}
データベースコンテキストを利用するクラスのコンストラクタでDbContextを引数にとるとDbContextのインスタンスが DI コンテナによって注入されます。
以下のBarsControllerのインスタンスが破棄された時に自動でDbContextのDisposeが呼ばれ破棄されます。
public class BarsController {
readonly FooDbContext dbContext;
public BarsController(FooDbContext fooDbContext) {
dbContext = fooDbContext;
}
...
}
注意点
DI コンテナーで注入したデータベースコンテキストをusing(Dispose)してはいけません。
インスタンスが破棄されたにも関わらず、DI コンテナー側でDisposeが呼ばれて例外が発生します。