[Entity Framework] トランザクションのスコープ制御(EF6:Code First)
Entity Framework のコンテキストにおいて、トランザクションは、既定では SaveChanges() を実行したときに暗黙的に使用されます。
要件によっては、トランザクションのスコープを明示的に制御したいケースも出てくるでしょう。
ここではトランザクションの明示的なスコープ制御を EF6 の Code First で行う方法をご紹介します。
※Entity Framework Core でのトランザクション制御については Microsoft Docs で詳しく解説されています。
※Model/Database First の場合は「トランザクションのスコープ制御(EF4.1~)」「トランザクションのスコープ制御(EF6)」をご覧ください。
複数回の SaveChanges をまたぐトランザクション
Database.BeginTransaction でトランザクションを開始し、その中で SaveChanges した変更をまとめて Commit します。
// コンテキスト
using (var context = new NorthwindContext())
{
// トランザクション開始
using (var transaction = context.Database.BeginTransaction())
{
// 1つめの SaveChanges()
var product = await context.Products.SingleAsync(p => p.ProductID == 1).ConfigureAwait(false);
product.ProductName = "New Product Name";
await context.SaveChangesAsync().ConfigureAwait(false);
// 2つめの SaveChanges()
var employee = await context.Employees.SingleAsync(e => e.EmployeeID == 1).ConfigureAwait(false);
employee.Title = "New Title";
await context.SaveChangesAsync().ConfigureAwait(false);
// まとめてコミット
transaction.Commit();
}
}
複数のコンテキストをまたぐトランザクション
あらかじめ接続を開いておいて BeginTransaction メソッドでトランザクションを開始し、その中で複数のコンテキストを操作、SaveChanges した後にまとめて Commit します。
Model/Database First と異なり、コンテキストのコンストラクタに渡す接続は EntityConnection を介しません。
コンテキストを Dispose しても接続が破棄されないよう、contextOwnsConnection 引数には false を指定します。
トランザクションは Database.UseTransaction メソッドでコンテキストに渡して共用します。
// 接続準備
using (var sqlConnection = new SqlConnection(NorthwindContext.GetConnectionString()))
{
// あらかじめ接続を開いておく。
sqlConnection.Open();
// トランザクション開始
using (var transaction = sqlConnection.BeginTransaction())
{
// 1つ目のコンテキストで保存
using (var context = new NorthwindContext(sqlConnection, false))
{
context.Database.UseTransaction(transaction);
var product = await context.Products.SingleAsync(p => p.ProductID == 1).ConfigureAwait(false);
product.ProductName = "New Product Name";
await context.SaveChangesAsync().ConfigureAwait(false);
}
// 2つ目のコンテキストで保存
using (var context = new NorthwindContext(sqlConnection, false))
{
context.Database.UseTransaction(transaction);
var employee = await context.Employees.SingleAsync(e => e.EmployeeID == 1).ConfigureAwait(false);
employee.Title = "New Title";
await context.SaveChangesAsync().ConfigureAwait(false);
}
// まとめてコミット
transaction.Commit();
}
}
// コンテキストクラス
public class NorthwindContext : DbContext
{
:
/// <summary>
/// コンストラクタ。
/// </summary>
/// <param name="existingConnection">コンテキストで使用する接続。</param>
/// <param name="contextOwnsConnection">false を指定すると、コンテキストが Dispose されたときに接続を Dispose しない。</param>
public NorthwindContext(DbConnection existingConnection, bool contextOwnsConnection)
: base(existingConnection, contextOwnsConnection)
{
}
/// <summary>
/// 接続文字列を取得する。
/// </summary>
/// <returns></returns>
public static string GetConnectionString()
{
using (var context = new NorthwindContext())
{
return context.Database.Connection.ConnectionString;
}
}
:
}
この記事が気に入ったらサポートをしてみませんか?