トランザクションの使用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
            }