LoginSignup
1
5

More than 3 years have passed since last update.

[Entity Framework] トランザクションのスコープ制御(EF6:Code First)

Last updated at Posted at 2019-11-10

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;
        }
    }

    :
}
1
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
5