使用交易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

根據預設,如果資料庫提供者支援交易,所有變更的單一呼叫中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.DatabaseAPI 來開始、 認可和回復交易。You can use the DbContext.Database API to begin, commit, and rollback transactions. 下列範例示範兩個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.

若要共用交易,內容必須共用兩者DbConnectionDbTransactionTo 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是您在中使用的 APIDbContext.OnConfiguring若要設定內容,您現在要建立外部使用DbContextOptionsDbContextOptionsBuilder 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.OnConfiguringAn 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 和 Entity Framework Core 作業。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
            }