Używanie transakcjiUsing Transactions

Transakcje zezwala na kilka operacji bazy danych mają być przetwarzane w sposób atomic.Transactions allow several database operations to be processed in an atomic manner. Transakcja została zatwierdzona, wszystkie operacje są pomyślnie zastosowana do bazy danych.If the transaction is committed, all of the operations are successfully applied to the database. Jeśli transakcja zostanie wycofana, żadna z operacji są stosowane do bazy danych.If the transaction is rolled back, none of the operations are applied to the database.

Porada

Można wyświetlić w tym artykule próbki w witrynie GitHub.You can view this article's sample on GitHub.

Domyślne zachowanie transakcjiDefault transaction behavior

Domyślnie, jeśli dostawca bazy danych obsługuje transakcje, wszystkie zmiany w jednym wywołaniu SaveChanges() są stosowane w ramach transakcji.By default, if the database provider supports transactions, all changes in a single call to SaveChanges() are applied in a transaction. W przypadku awarii zmian, transakcja zostanie wycofana i zmiany są stosowane do bazy danych.If any of the changes fail, then the transaction is rolled back and none of the changes are applied to the database. Oznacza to, że SaveChanges() jest gwarantowana całkowicie powiedzie się, lub pozostaw bazę danych, nie mają być modyfikowane w przypadku wystąpienia błędu.This means that SaveChanges() is guaranteed to either completely succeed, or leave the database unmodified if an error occurs.

W przypadku większości aplikacji to domyślne zachowanie jest wystarczająca.For most applications, this default behavior is sufficient. Transakcje powinien kontroli tylko ręcznie, jeśli wymagania aplikacji uznać za konieczne.You should only manually control transactions if your application requirements deem it necessary.

Kontrolowanie transakcjiControlling transactions

Można użyć DbContext.Database interfejsu API, aby rozpocząć, zatwierdzenia i wycofywania transakcji.You can use the DbContext.Database API to begin, commit, and rollback transactions. Poniższy przykład przedstawia dwa SaveChanges() operacje i LINQ zapytania wykonywane w ramach jednej transakcji.The following example shows two SaveChanges() operations and a LINQ query being executed in a single transaction.

Nie wszyscy dostawcy bazy danych obsługuje transakcji.Not all database providers support transactions. Niektórzy dostawcy może zgłaszać lub pusta podczas transakcji są nazywane interfejsów 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
        }
    }
}

Między kontekstu transakcji (relacyjnych baz danych tylko)Cross-context transaction (relational databases only)

Możesz również udostępniać w wielu wystąpieniach kontekstu transakcji.You can also share a transaction across multiple context instances. Ta funkcja jest dostępna tylko w przypadku korzystania z dostawcy relacyjnej bazy danych, ponieważ wymaga użycia DbTransaction i DbConnection, które są specyficzne dla relacyjnych baz danych.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.

Aby udostępnić transakcji, kontekstów musi udostępniać zarówno DbConnection i DbTransaction.To share a transaction, the contexts must share both a DbConnection and a DbTransaction.

Zezwalaj na połączenia należy podać zewnętrznieAllow connection to be externally provided

Udostępnianie DbConnection wymaga możliwości przekazania połączenie w kontekście podczas jego tworzenia.Sharing a DbConnection requires the ability to pass a connection into a context when constructing it.

Najprostszym sposobem, aby umożliwić DbConnection zewnętrznie zostać podany, jest zatrzymanie przy użyciu DbContext.OnConfiguring metody zewnętrznie utworzony i skonfigurowany kontekst DbContextOptions i przekazać je do konstruktora kontekstu.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.

Porada

DbContextOptionsBuilder jest używany w API DbContext.OnConfiguring można skonfigurować kontekstu, teraz będzie go zewnętrznie użyć do utworzenia 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; }
}

Alternatywą jest nadal używać DbContext.OnConfiguring, ale akceptują DbConnection które jest zapisywane i następnie używane w 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);
    }
}

Połączenie udziału i transakcjiShare connection and transaction

Teraz możesz utworzyć wiele wystąpień kontekstu, które współużytkują to samo połączenie.You can now create multiple context instances that share the same connection. Następnie użyj DbContext.Database.UseTransaction(DbTransaction) interfejsu API można zarejestrować zarówno kontekstów w tej samej transakcji.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
        }
    }
}

Za pomocą zewnętrznego DbTransactions (relacyjnych baz danych tylko)Using external DbTransactions (relational databases only)

Korzystając z wielu technologii dostępu do danych relacyjnej bazy danych, można udostępniać transakcji między operacje wykonywane przez te różnych technologii.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.

Poniższy przykład przedstawia sposób wykonywania operacji ADO.NET SqlClient i Entity Framework podstawowej funkcjonalności w tej samej transakcji.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
    }
}

Przy użyciu System.TransactionsUsing System.Transactions

Uwaga

Ta funkcja jest nowa w programie EF Core 2.1.This feature is new in EF Core 2.1.

Istnieje możliwość użycia otoczenia transakcji, aby koordynować w większego zakresu.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
    }
}

Istnieje również możliwość można zarejestrować w jawnych transakcji.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
    }
}

Ograniczenia obszaru nazw System.TransactionsLimitations of System.Transactions

  1. Podstawowe EF zależy od dostawcy bazy danych, obsługa System.Transactions.EF Core relies on database providers to implement support for System.Transactions. Mimo że pomocy technicznej jest dość często wśród dostawców ADO.NET dla programu .NET Framework, interfejsu API tylko został ostatnio dodany do platformy .NET Core i dlatego nie jest tak szerokie pomocy technicznej.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. Jeśli dostawca nie implementuje obsługę System.Transactions, istnieje możliwość, że wywołań do tych interfejsów API będą ignorowane całkowicie.If a provider does not implement support for System.Transactions, it is possible that calls to these APIs will be completely ignored. Klient SQL dla platformy .NET Core obsługuje z 2.1 lub nowszej.SqlClient for .NET Core does support it from 2.1 onwards. SqlClient programu .NET Core 2.0 spowoduje zgłoszenie wyjątku z próby użycia funkcji.SqlClient for .NET Core 2.0 will throw an exception of you attempt to use the feature.

    Ważne

    Zaleca się przetestowanie czy interfejsu API poprawne działanie u dostawcy przed polegać na niej zarządzania transakcji.It is recommended that you test that the API behaves correctly with your provider before you rely on it for managing transactions. Zachęcamy do kontaktowania się z Element utrzymujący dostawcy bazy danych, jeśli jej nie ma.You are encouraged to contact the maintainer of the database provider if it does not.

  2. Począwszy od wersji 2.1 implementacja System.Transactions w .NET Core nie ma obsługi transakcji rozproszonych, dlatego nie można używać TransactionScope lub CommitableTransaction aby koordynować transakcje w wielu menedżerów zasobów.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.