Použití transakcíUsing Transactions

Transakce umožňují zpracovávat několik databázových operací atomovou způsobem.Transactions allow several database operations to be processed in an atomic manner. Pokud je transakce potvrzena, budou všechny operace v databázi úspěšně provedeny.If the transaction is committed, all of the operations are successfully applied to the database. Pokud se transakce vrátí zpět, v databázi se nepoužije žádná operace.If the transaction is rolled back, none of the operations are applied to the database.

Tip

Ukázku tohoto článku můžete zobrazit na GitHubu.You can view this article's sample on GitHub.

Výchozí chování transakceDefault transaction behavior

Ve výchozím nastavení platí, že pokud poskytovatel databáze podporuje transakce, jsou všechny změny v jednom volání SaveChanges() aplikovány v transakci.By default, if the database provider supports transactions, all changes in a single call to SaveChanges() are applied in a transaction. Pokud se kterákoli z změn nezdaří, transakce se vrátí zpět a v databázi se nepoužije žádná změna.If any of the changes fail, then the transaction is rolled back and none of the changes are applied to the database. To znamená, že SaveChanges() je zaručené buď kompletně, nebo ponechat databázi nezměněnou, pokud dojde k chybě.This means that SaveChanges() is guaranteed to either completely succeed, or leave the database unmodified if an error occurs.

U většiny aplikací je toto výchozí chování dostatečné.For most applications, this default behavior is sufficient. Transakce byste měli řídit jenom v případě, že požadavky vaší aplikace považují za nezbytné.You should only manually control transactions if your application requirements deem it necessary.

Řízení transakcíControlling transactions

K zahájení, potvrzení a vrácení transakcí můžete použít rozhraní DbContext.Database API.You can use the DbContext.Database API to begin, commit, and rollback transactions. Následující příklad ukazuje dvě operace SaveChanges() a dotaz LINQ prováděný v rámci jedné transakce.The following example shows two SaveChanges() operations and a LINQ query being executed in a single transaction.

Ne všichni poskytovatelé databáze podporují transakce.Not all database providers support transactions. Někteří zprostředkovatelé mohou vyvolat nebo no-op při volání rozhraní API pro transakce.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
        }
    }
}

Transakce křížového kontextu (pouze relační databáze)Cross-context transaction (relational databases only)

Můžete také sdílet transakce napříč několika instancemi kontextu.You can also share a transaction across multiple context instances. Tato funkce je k dispozici pouze při použití poskytovatele relační databáze, protože vyžaduje použití DbTransaction a DbConnection, která jsou specifická pro relační databáze.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.

Chcete-li sdílet transakci, kontexty musí sdílet DbConnection i DbTransaction.To share a transaction, the contexts must share both a DbConnection and a DbTransaction.

Umožnění externě dostupného připojeníAllow connection to be externally provided

Sdílení DbConnection vyžaduje možnost předat připojení do kontextu při jeho vytváření.Sharing a DbConnection requires the ability to pass a connection into a context when constructing it.

Nejjednodušší způsob, jak DbConnection být umožněn externě, je zastavit použití metody DbContext.OnConfiguring ke konfiguraci kontextu a externě vytvořeným DbContextOptions a jejich předání do konstruktoru kontextu.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.

Tip

DbContextOptionsBuilder je rozhraní API, které jste použili v DbContext.OnConfiguring ke konfiguraci kontextu, teď ho budete používat externě k vytvoření 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; }
}

Alternativou je dál používat DbContext.OnConfiguring, ale přijměte DbConnection, který se uloží a pak používá v 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);
    }
}

Sdílet připojení a transakciShare connection and transaction

Nyní můžete vytvořit více instancí kontextu, které sdílejí stejné připojení.You can now create multiple context instances that share the same connection. Pak použijte rozhraní DbContext.Database.UseTransaction(DbTransaction) API k zařazení kontextů do stejné transakce.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
        }
    }
}

Použití externích DbTransactions (jenom relační databáze)Using external DbTransactions (relational databases only)

Pokud pro přístup k relační databázi používáte více technologií pro přístup k datům, může být vhodné sdílet transakci mezi operacemi prováděnými těmito různými technologiemi.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.

Následující příklad ukazuje, jak provést operaci ADO.NET SqlClient a operaci Entity Framework Core ve stejné transakci.The following example, shows how to perform an ADO.NET SqlClient operation and an Entity Framework Core operation in the same transaction.

using (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
        }
    }
}

Použití System. TransactionsUsing System.Transactions

Poznámka

Tato funkce je v EF Core 2,1 novinkou.This feature is new in EF Core 2.1.

Je možné použít ambientní transakce, pokud potřebujete koordinovat větší rozsah.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 }))
{
    using (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
        }
    }
}

Je také možné zařadit do explicitní transakce.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.OpenConnection();
            context.Database.EnlistTransaction(transaction);

            // 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();
            context.Database.CloseConnection();
        }

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

Omezení pro System. TransactionsLimitations of System.Transactions

  1. EF Core spoléhá na to, že poskytovatelé databáze implementují podporu pro System. Transactions.EF Core relies on database providers to implement support for System.Transactions. I když je podpora poměrně častá mezi poskytovateli ADO.NET pro .NET Framework, rozhraní API se v poslední době přidalo do .NET Core, takže podpora není tak širší.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 as widespread. Pokud zprostředkovatel neimplementuje podporu pro System. Transactions, je možné, že volání těchto rozhraní API budou zcela ignorována.If a provider does not implement support for System.Transactions, it is possible that calls to these APIs will be completely ignored. SqlClient pro .NET Core ho podporuje od verze 2,1 a vyšší.SqlClient for .NET Core does support it from 2.1 onwards. SqlClient pro .NET Core 2,0 vyvolá výjimku, pokud se pokusíte funkci použít.SqlClient for .NET Core 2.0 will throw an exception if you attempt to use the feature.

    Důležité

    Doporučujeme, abyste před tím, než se spoléháte na správu transakcí, správně spolupracovali s vaším poskytovatelem.It is recommended that you test that the API behaves correctly with your provider before you rely on it for managing transactions. Pokud ne, doporučujeme, abyste se obrátili na údržbu poskytovatele databáze.You are encouraged to contact the maintainer of the database provider if it does not.

  2. Od verze 2,1 implementace System. Transactions v .NET Core nezahrnuje podporu distribuovaných transakcí, proto nemůžete použít TransactionScope ani CommittableTransaction k koordinaci transakcí napříč více správci prostředků.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 CommittableTransaction to coordinate transactions across multiple resource managers.