Usando transaçõesUsing Transactions

Transações permitem que várias operações de banco de dados a serem processadas de forma atômica.Transactions allow several database operations to be processed in an atomic manner. Se a transação for confirmada, todas as operações são aplicadas com êxito no banco de dados.If the transaction is committed, all of the operations are successfully applied to the database. Se a transação for revertida, nenhuma das operações são aplicadas ao banco de dados.If the transaction is rolled back, none of the operations are applied to the database.

Dica

Você pode exibir este artigo exemplo no GitHub.You can view this article's sample on GitHub.

Comportamento de transação padrãoDefault transaction behavior

Por padrão, se o provedor de banco de dados oferece suporte a transações, todas as alterações em uma única chamada para SaveChanges() são aplicados em uma transação.By default, if the database provider supports transactions, all changes in a single call to SaveChanges() are applied in a transaction. Se qualquer uma das alterações falhar, em seguida, a transação é revertida e nenhuma das alterações são aplicadas ao banco de dados.If any of the changes fail, then the transaction is rolled back and none of the changes are applied to the database. Isso significa que SaveChanges() é garantido completamente ser bem-sucedida, ou deixe o banco de dados não modificado se ocorrer um erro.This means that SaveChanges() is guaranteed to either completely succeed, or leave the database unmodified if an error occurs.

Para a maioria dos aplicativos, esse comportamento padrão é suficiente.For most applications, this default behavior is sufficient. Você deve controlar transações somente manualmente se seus requisitos de aplicativo julguem necessário.You should only manually control transactions if your application requirements deem it necessary.

Controle de transaçõesControlling transactions

Você pode usar o DbContext.Database API para iniciar, confirmar e reverter transações.You can use the DbContext.Database API to begin, commit, and rollback transactions. O exemplo a seguir mostra duas SaveChanges() operações e um LINQ de consulta que está sendo executada em uma única transação.The following example shows two SaveChanges() operations and a LINQ query being executed in a single transaction.

Nem todos os provedores de banco de dados oferecer suporte a transações.Not all database providers support transactions. Alguns provedores podem gerar ou não-operacional quando transações são chamadas de APIs.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
                }
            }
        }

Transação de contexto entre (bancos de dados relacionais somente)Cross-context transaction (relational databases only)

Você também pode compartilhar uma transação entre várias instâncias de contexto.You can also share a transaction across multiple context instances. Essa funcionalidade está disponível somente ao usar um provedor de banco de dados relacional, porque ele requer o uso de DbTransaction e DbConnection, que são específicos para bancos de dados relacionais.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.

Para compartilhar uma transação, os contextos devem compartilhar ambos um DbConnection e um DbTransaction.To share a transaction, the contexts must share both a DbConnection and a DbTransaction.

Permitir a conexão a ser fornecido externamenteAllow connection to be externally provided

Compartilhando um DbConnection requer a capacidade de transmitir uma conexão em um contexto ao construir a ele.Sharing a DbConnection requires the ability to pass a connection into a context when constructing it.

A maneira mais fácil para permitir DbConnection a ser fornecido externamente é parar de usar o DbContext.OnConfiguring método para configurar o contexto e criar externamente DbContextOptions e passá-los para o construtor de contexto.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.

Dica

DbContextOptionsBuilderé a API usada na DbContext.OnConfiguring para configurar o contexto, você agora vai usá-lo externamente para criar 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; }
    }

Uma alternativa é continuar usando DbContext.OnConfiguring, mas aceitam uma DbConnection que é salvo e, em seguida, usado em 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);
    }
}

Compartilhamento de conexão e de transaçãoShare connection and transaction

Agora você pode criar várias instâncias de contexto que compartilham a mesma conexão.You can now create multiple context instances that share the same connection. Em seguida, use o DbContext.Database.UseTransaction(DbTransaction) API para inscrever-se os dois contextos na mesma transação.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
                }
            }
        }

Usando DbTransactions externo (bancos de dados relacionais somente)Using external DbTransactions (relational databases only)

Se você estiver usando várias tecnologias de acesso a dados para acessar um banco de dados relacional, pode ser que você deseja compartilhar uma transação entre operações executadas por essas tecnologias diferentes.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.

O exemplo a seguir mostra como executar uma operação do SqlClient do ADO.NET e uma operação de Entity Framework Core na mesma transação.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
            }