トランザクションの使用Using Transactions

トランザクションはアトミックな方法で処理される複数のデータベース操作を許可します。Transactions allow several database operations to be processed in an atomic manner. トランザクションがコミットされた場合は、すべての操作が正常にデータベースに適用します。If the transaction is committed, all of the operations are successfully applied to the database. トランザクションがロールバックされた場合は、データベースにどの操作も適用されます。If the transaction is rolled back, none of the operations are applied to the database.

ヒント

この記事のサンプルは GitHub で確認できます。You can view this article's sample on GitHub.

既定のトランザクション動作Default transaction behavior

既定では、データベース プロバイダーが、トランザクションをサポートしている場合のすべての変更を 1 回の呼び出しでSaveChanges()トランザクションに適用されます。By default, if the database provider supports transactions, all changes in a single call to SaveChanges() are applied in a transaction. 変更が失敗した場合、トランザクションがロールバックされ、どの変更は、データベースに適用します。If any of the changes fail, then the transaction is rolled back and none of the changes are applied to the database. つまり、SaveChanges()完全に成功、または、データベース エラーが発生した場合は、未変更の状態のままにすることが保証されます。This means that SaveChanges() is guaranteed to either completely succeed, or leave the database unmodified if an error occurs.

ほとんどのアプリケーションでこの既定の動作で十分です。For most applications, this default behavior is sufficient. アプリケーションの要件とみなす必要な場合に手動でのみ、トランザクションを制御する必要があります。You should only manually control transactions if your application requirements deem it necessary.

トランザクションを制御します。Controlling transactions

使用することができます、DbContext.Databaseはじめに、コミット、API とのトランザクションをロールバックします。You can use the DbContext.Database API to begin, commit, and rollback transactions. 次の例は 2 つSaveChanges()操作と LINQ クエリを単一のトランザクションで実行されています。The following example shows two SaveChanges() operations and a LINQ query being executed in a single transaction.

すべてのデータベース プロバイダーは、トランザクションをサポートします。Not all database providers support transactions. スローする可能性が一部のプロバイダーまたはトランザクションの Api が呼び出されたときに行いません。Some providers may throw or no-op when transaction APIs are called.

        using (var context = new BloggingContext())
        {
            using (var transaction = context.Database.BeginTransaction())
            {
                try
                {
                    context.Blogs.Add(new Blog { Url = "http://blogs.msdn.com/dotnet" });
                    context.SaveChanges();

                    context.Blogs.Add(new Blog { Url = "http://blogs.msdn.com/visualstudio" });
                    context.SaveChanges();

                    var blogs = context.Blogs
                        .OrderBy(b => b.Url)
                        .ToList();

                    // Commit transaction if all commands succeed, transaction will auto-rollback
                    // when disposed if either commands fails
                    transaction.Commit();
                }
                catch (Exception)
                {
                    // TODO: Handle failure
                }
            }
        }

クロス コンテキスト トランザクション (リレーショナル データベースのみ)Cross-context transaction (relational databases only)

複数のコンテキストのインスタンス間でトランザクションを共有することもできます。You can also share a transaction across multiple context instances. この機能は、の使用が必要とするために、リレーショナル データベース プロバイダーを使用する場合にのみ使用可能なDbTransactionDbConnection、これは、リレーショナル データベースに固有です。This functionality is only available when using a relational database provider because it requires the use of DbTransaction and DbConnection, which are specific to relational databases.

トランザクションを共有するコンテキストを共有両方、DbConnectionDbTransactionです。To share a transaction, the contexts must share both a DbConnection and a DbTransaction.

提供される外部接続を許可します。Allow connection to be externally provided

共有、DbConnectionを構築するときに、コンテキストに接続を渡すことが必要です。Sharing a DbConnection requires the ability to pass a connection into a context when constructing it.

許可する最も簡単な方法DbConnectionを外部的に提供するには、使用を停止するが、DbContext.OnConfiguringメソッド コンテキストを構成し、外部で作成するDbContextOptionsコンテキスト コンス トラクターに渡すとします。The easiest way to allow DbConnection to be externally provided, is to stop using the DbContext.OnConfiguring method to configure the context and externally create DbContextOptions and pass them to the context constructor.

ヒント

DbContextOptionsBuilder 使用される API は、DbContext.OnConfiguringコンテキストを構成するようになりましたしようとする外部で使用して作成するDbContextOptionsです。DbContextOptionsBuilder is the API you used in DbContext.OnConfiguring to configure the context, you are now going to use it externally to create DbContextOptions.

public class BloggingContext : DbContext
{
    public BloggingContext(DbContextOptions<BloggingContext> options)
        : base(options)
    { }

    public DbSet<Blog> Blogs { get; set; }
}

使用を継続するという方法もDbContext.OnConfiguringは受け付け、DbConnectionが保存されで使用してDbContext.OnConfiguringです。An alternative is to keep using DbContext.OnConfiguring, but accept a DbConnection that is saved and then used in DbContext.OnConfiguring.

public class BloggingContext : DbContext
{
    private DbConnection _connection;

    public BloggingContext(DbConnection connection)
    {
      _connection = connection;
    }

    public DbSet<Blog> Blogs { get; set; }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseSqlServer(_connection);
    }
}

共有接続とトランザクションShare connection and transaction

同じ接続を共有する複数のコンテキストのインスタンスを作成できます。You can now create multiple context instances that share the same connection. 使用して、 DbContext.Database.UseTransaction(DbTransaction) API を両方のコンテキストで、同じトランザクションに参加します。Then use the DbContext.Database.UseTransaction(DbTransaction) API to enlist both contexts in the same transaction.

var options = new DbContextOptionsBuilder<BloggingContext>()
    .UseSqlServer(new SqlConnection(connectionString))
    .Options;

using (var context1 = new BloggingContext(options))
{
    using (var transaction = context1.Database.BeginTransaction())
    {
        try
        {
            context1.Blogs.Add(new Blog { Url = "http://blogs.msdn.com/dotnet" });
            context1.SaveChanges();

            using (var context2 = new BloggingContext(options))
            {
                context2.Database.UseTransaction(transaction.GetDbTransaction());

                var blogs = context2.Blogs
                    .OrderBy(b => b.Url)
                    .ToList();
            }

            // Commit transaction if all commands succeed, transaction will auto-rollback
            // when disposed if either commands fails
            transaction.Commit();
        }
        catch (Exception)
        {
            // TODO: Handle failure
        }
    }
}

外部 DbTransactions (リレーショナル データベースのみ) を使用します。Using external DbTransactions (relational databases only)

リレーショナル データベースへのアクセスを複数のデータ アクセス テクノロジを使用している場合は、これらのさまざまなテクノロジによって実行される操作の間でトランザクションを共有します。If you are using multiple data access technologies to access a relational database, you may want to share a transaction between operations performed by these different technologies.

次の例では、同じトランザクションで ADO.NET SqlClient 操作とエンティティ フレームワークの主要な操作を実行する方法を示します。The following example, shows how to perform an ADO.NET SqlClient operation and an Entity Framework Core operation in the same transaction.

var connection = new SqlConnection(connectionString);
connection.Open();

using (var transaction = connection.BeginTransaction())
{
    try
    {
        // Run raw ADO.NET command in the transaction
        var command = connection.CreateCommand();
        command.Transaction = transaction;
        command.CommandText = "DELETE FROM dbo.Blogs";
        command.ExecuteNonQuery();

        // Run an EF Core command in the transaction
        var options = new DbContextOptionsBuilder<BloggingContext>()
            .UseSqlServer(connection)
            .Options;

        using (var context = new BloggingContext(options))
        {
            context.Database.UseTransaction(transaction);
            context.Blogs.Add(new Blog { Url = "http://blogs.msdn.com/dotnet" });
            context.SaveChanges();
        }

        // Commit transaction if all commands succeed, transaction will auto-rollback
        // when disposed if either commands fails
        transaction.Commit();
    }
    catch (System.Exception)
    {
        // TODO: Handle failure
    }
}

System.Transactions の使用Using System.Transactions

注意

この機能は、EF コア 2.1 の新機能です。This feature is new in EF Core 2.1.

大きいスコープの間で調整する必要がある場合は、アンビエント トランザクションを使用して行うことができます。It is possible to use ambient transactions if you need to coordinate across a larger scope.

using (var scope = new TransactionScope(
    TransactionScopeOption.Required, 
    new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted }))
{
    var connection = new SqlConnection(connectionString);
    connection.Open();

    try
    {
        // Run raw ADO.NET command in the transaction
        var command = connection.CreateCommand();
        command.CommandText = "DELETE FROM dbo.Blogs";
        command.ExecuteNonQuery();

        // Run an EF Core command in the transaction
        var options = new DbContextOptionsBuilder<BloggingContext>()
            .UseSqlServer(connection)
            .Options;

        using (var context = new BloggingContext(options))
        {
            context.Blogs.Add(new Blog { Url = "http://blogs.msdn.com/dotnet" });
            context.SaveChanges();
        }

        // Commit transaction if all commands succeed, transaction will auto-rollback
        // when disposed if either commands fails
        scope.Complete();
    }
    catch (System.Exception)
    {
        // TODO: Handle failure
    }
}

明示的なトランザクションに参加することもできます。It is also possible to enlist in an explicit transaction.

using (var transaction = new CommittableTransaction(
    new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted }))
{
    var connection = new SqlConnection(connectionString);

    try
    {
        var options = new DbContextOptionsBuilder<BloggingContext>()
            .UseSqlServer(connection)
            .Options;

        using (var context = new BloggingContext(options))
        {
            context.Database.EnlistTransaction(transaction);
            context.Database.OpenConnection();

            // Run raw ADO.NET command in the transaction
            var command = connection.CreateCommand();
            command.CommandText = "DELETE FROM dbo.Blogs";
            command.ExecuteNonQuery();

            // Run an EF Core command in the transaction
            context.Blogs.Add(new Blog { Url = "http://blogs.msdn.com/dotnet" });
            context.SaveChanges();
        }

        // Commit transaction if all commands succeed, transaction will auto-rollback
        // when disposed if either commands fails
        transaction.Commit();
    }
    catch (System.Exception)
    {
        // TODO: Handle failure
    }
}

System.Transactions の制限事項Limitations of System.Transactions

  1. EF コアは、System.Transactions についてサポートを実装するデータベースのプロバイダーに依存します。EF Core relies on database providers to implement support for System.Transactions. サポートは非常に共通の ADO.NET プロバイダーの .NET Framework、API を .NET Core 最近追加されただけし、したがってサポートはできません、広範囲にわたる。Although support is quite common among ADO.NET providers for .NET Framework, the API has only been recently added to .NET Core and hence support is not be as widespread. 場合は、プロバイダーは、System.Transactions についてサポートを実装していない、可能であればこれらの Api への呼び出しを完全に無視することです。If a provider does not implement support for System.Transactions, it is possible that calls to these APIs will be completely ignored. .NET Core の SqlClient では、2.1 以降からサポートことです。SqlClient for .NET Core does support it from 2.1 onwards. .NET Core 2.0 SqlClient は例外をスローのしようとする機能を使用します。SqlClient for .NET Core 2.0 will throw an exception of you attempt to use the feature.

    重要

    API が正常に動作プロバイダーでトランザクションを管理するために依存する前にテストすることをお勧めします。It is recommended that you test that the API behaves correctly with your provider before you rely on it for managing transactions. そうでないデータベース プロバイダーの保守管理者に連絡することをお勧めしています。You are encouraged to contact the maintainer of the database provider if it does not.

  2. バージョン 2.1 では、.NET Core での System.Transactions の実装は含まれません分散トランザクションのサポート、したがって使用することはできませんTransactionScopeまたはCommitableTransaction複数のリソース マネージャー間でトランザクションを調整します。As of version 2.1, the System.Transactions implementation in .NET Core does not include support for distributed transactions, therefore you cannot use TransactionScope or CommitableTransaction to coordinate transactions across multiple resource managers.