Help us understand the problem. What is going on with this article?

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

Entity Framework のコンテキストにおいて、トランザクションは、既定では SaveChanges() を実行したときに暗黙的に使用されます。
要件によっては、トランザクションのスコープを明示的に制御したいケースも出てくるでしょう。

EF6 ではトランザクション操作のために DbContext.Database.BeginTransaction/UseTransaction メソッドが導入され、TransactionScope よりも推奨されるようになりました。(Microsoft Docs 解説
ここでは、トランザクションの明示的なスコープ制御を EF6 の Model/Database First で行う例を示します。

※EF4.1 以降で TransactionScope クラスや DbTransaction.BeginTransaction メソッドを使用する例については、「トランザクションのスコープ制御(EF4.1~:Model/Database First)」をご覧ください。
※Code First での制御については「トランザクションのスコープ制御(EF6:Code First)」をご覧ください。

複数回の SaveChanges をまたぐトランザクション

Database.BeginTransaction でトランザクションを開始し、その中で SaveChanges した変更をまとめて Commit します。

// コンテキスト
using (var context = new NorthwindEntities())
{
    // トランザクション開始
    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 します。

Database/Model First では、コンテキストのコンストラクタには EntityConnection を渡す必要があります。
コンテキストを Dispose しても接続が破棄されないよう、contextOwnsConnection 引数には false を指定します。

トランザクションは Database.UseTransaction メソッドでコンテキストに渡して共用します。

// 接続準備
var workspace = NorthwindEntities.GetMetadataWorkspace();
using (var entityConnection1 = new EntityConnection("name=NorthwindEntities"))
using (var sqlConnection = entityConnection1.StoreConnection)
using (var entityConnection2 = new EntityConnection(workspace, sqlConnection, false))
{
    // あらかじめ接続を開いておく。
    sqlConnection.Open();

    // トランザクション開始
    using (var transaction = sqlConnection.BeginTransaction())
    {
        // 1つ目のコンテキストを操作する。
        using (var context = new NorthwindEntities(entityConnection1, 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);
        }

        // 別の EntityConnection を使って2つ目のコンテキストを操作する。
        // ※同じ EntityConnection を使用すると InvalidOperationException が発生する。
        using (var context = new NorthwindEntities(entityConnection2, 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 partial class NorthwindEntities : DbContext
{
    /// <summary>
    /// コンストラクタ。
    /// </summary>
    /// <param name="existingConnection">コンテキストで使用する接続。</param>
    /// <param name="contextOwnsConnection">false を指定すると、コンテキストが Dispose されたときに接続を Dispose しない。</param>
    public NorthwindEntities(DbConnection existingConnection, bool contextOwnsConnection)
        : base(existingConnection, contextOwnsConnection)
    {
    }

    /// <summary>
    /// メタデータワークスペースを取得する。
    /// </summary>
    /// <returns></returns>
    public static MetadataWorkspace GetMetadataWorkspace()
    {
        using (var context = new NorthwindEntities())
        {
            var objectContext = ((IObjectContextAdapter)context).ObjectContext;
            return objectContext.MetadataWorkspace;
        }
    }
}

それぞれのコンテキストには別々の EntityConnection を渡す必要があります。
2つ目以降の EntityConnection を作成するときに必要となる MetadataWorkspace オブジェクトは、コンテキストが実装する IObjectContextAdapter インターフェイスの ObjectContext プロパティから取得できます。

CodeOne
【品質と生産性にこだわるシステム開発】 .NET(C#/VB.NET)専門・リモート開発歴10年。即日・15分から頼める常駐しないエンジニア。確かな技術で開発チームを手堅くサポートします。
https://codeone.jp/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away