はじめに
今更ながら、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
が呼ばれて例外が発生します。