Verwenden von TransaktionenUsing Transactions

Mit Transaktionen können mehrere Datenbankvorgänge einzeln verarbeitet werden.Transactions allow several database operations to be processed in an atomic manner. Wenn die Transaktion committet wird, werden alle Vorgänge erfolgreich auf die Datenbank angewendet.If the transaction is committed, all of the operations are successfully applied to the database. Falls für die Transaktion ein Rollback ausgeführt wird, wird keiner der Vorgänge für die Datenbank übernommen.If the transaction is rolled back, none of the operations are applied to the database.

Tipp

Das in diesem Artikel verwendete Beispiel finden Sie auf GitHub.You can view this article's sample on GitHub.

StandardtransaktionsverhaltenDefault transaction behavior

Wenn der Datenbankanbieter Transaktionen unterstützt, werden standardmäßig alle Änderungen in einem einzigen Aufruf von SaveChanges() in einer Transaktion angewendet.By default, if the database provider supports transactions, all changes in a single call to SaveChanges() are applied in a transaction. Wenn bei einer der Änderungen ein Fehler auftritt, wird ein Rollback ausgeführt, und keine der Änderungen wird für die Datenbank übernommen.If any of the changes fail, then the transaction is rolled back and none of the changes are applied to the database. SaveChanges() wird also entweder vollständig ausgeführt oder überhaupt nicht, wenn ein Fehler auftritt. Die Datenbank bleibt in diesem Fall unverändert.This means that SaveChanges() is guaranteed to either completely succeed, or leave the database unmodified if an error occurs.

Bei den meisten Anwendungen ist dieses Standardverhalten ausreichend.For most applications, this default behavior is sufficient. Sie sollten Transaktionen nur manuell steuern, wenn die Anforderungen Ihrer Anwendung dies notwendig machen.You should only manually control transactions if your application requirements deem it necessary.

Steuern von TransaktionenControlling transactions

Mit der DbContext.Database-API können Sie Transaktionen beginnen, committen und ein Rollback dafür ausführen.You can use the DbContext.Database API to begin, commit, and rollback transactions. Im folgenden Beispiel werden zwei SaveChanges()-Vorgänge und eine LINQ-Abfrage dargestellt, die in einer einzelnen Transaktion ausgeführt wird.The following example shows two SaveChanges() operations and a LINQ query being executed in a single transaction.

Nicht alle Datenbankanbieter unterstützen Transaktionen.Not all database providers support transactions. In diesem Fall wird ein Fehler ausgelöst oder kein Vorgang gestartet, wenn Transaktions-APIs aufgerufen werden.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
        }
    }
}

Kontextübergreifende Transaktionen (nur relationale Datenbanken)Cross-context transaction (relational databases only)

Sie können Transaktionen auch für mehrere Kontextinstanzen freigeben.You can also share a transaction across multiple context instances. Diese Funktion ist nur bei Anbietern von relationalen Datenbanken verfügbar, da DbTransaction und DbConnection verwendet werden müssen, die charakteristisch für relationale Datenbanken sind.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.

Um eine Transaktion freigeben zu können, müssen die Kontexte DbConnection und DbTransaction verwenden.To share a transaction, the contexts must share both a DbConnection and a DbTransaction.

Zulassen von extern bereitgestellten VerbindungenAllow connection to be externally provided

Um DbConnection freigeben zu können, muss eine Verbindung an einen Kontext übergeben werden können, während dieser erstellt wird.Sharing a DbConnection requires the ability to pass a connection into a context when constructing it.

Die einfachste Möglichkeit, eine externe Bereitstellung von DbConnection zuzulassen, ist, den Kontext nicht mehr mit der DbContext.OnConfiguring-Methode zu konfigurieren, sondern extern DbContextOptions zu erstellen und es an den Kontextkonstruktor zu übergeben.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.

Tipp

DbContextOptionsBuilder ist die API, die Sie in DbContext.OnConfiguring zum Konfigurieren des Kontexts verwendet haben. Jetzt werden Sie es extern verwenden und DbContextOptions erstellen.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; }
}

Alternativ können Sie weiterhin DbContext.OnConfiguring verwenden und eine DbConnection akzeptieren, die gespeichert und dann in DbContext.OnConfiguring verwendet wird.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);
    }
}

Freigeben der Verbindung und der TransaktionShare connection and transaction

Sie können nun mehrere Kontextinstanzen erstellen, die die gleiche Verbindung verwenden.You can now create multiple context instances that share the same connection. Anschließend tragen Sie mit der DbContext.Database.UseTransaction(DbTransaction)-API beide Kontexte in derselben Transaktion ein.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
        }
    }
}

Verwenden von externen DbTransactions (nur relationale Datenbanken)Using external DbTransactions (relational databases only)

Wenn Sie mit verschiedenen Datenzugriffstechnologien auf eine relationale Datenbank zugreifen, sollten Sie eine gemeinsame Transaktion für die Vorgänge einrichten, die von diesen verschiedenen Technologien ausgeführt werden.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.

Im folgenden Beispiel wird gezeigt, wie ein ADO.NET SqlClient-Vorgang und ein Entity Framework Core-Vorgang in derselben Transaktion ausgeführt werden.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
        }
    }
}

Verwenden von System.TransactionsUsing System.Transactions

Hinweis

Dieses Feature ist neu in EF Core 2.1.This feature is new in EF Core 2.1.

Es ist möglich, Ambient-Transaktionen zu verwenden, wenn Sie einen größeren Bereich koordinieren müssen.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
        }
    }
}

Sie können auch eine Eintragung in einer expliziten Transaktion vornehmen.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
    }
}

Einschränkungen von System.TransactionsLimitations of System.Transactions

  1. In EF Core müssen die Datenbankanbieter die Unterstützung für System.Transactions implementieren.EF Core relies on database providers to implement support for System.Transactions. Unterstützung ist unter ADO.NET-Anbietern für .NET Framework zwar recht üblich, die API wurde .NET Core jedoch erst vor Kurzem hinzugefügt. Daher ist die Unterstützung noch nicht weit verbreitet.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. Wenn ein Anbieter keine Unterstützung für System.Transactions implementiert, ist es möglich, dass Aufrufe dieser APIs vollständig ignoriert werden.If a provider does not implement support for System.Transactions, it is possible that calls to these APIs will be completely ignored. SqlClient für .NET Core bietet Unterstützung ab Version 2.1.SqlClient for .NET Core does support it from 2.1 onwards. In SqlClient für .NET Core 2.0 wird eine Ausnahme ausgelöst, wenn Sie dieses Feature ausführen.SqlClient for .NET Core 2.0 will throw an exception of you attempt to use the feature.

    Wichtig

    Daher sollten Sie testen, ob die API ordnungsgemäß mit Ihrem Anbieter funktioniert, bevor Sie sie für die Verwaltung von Transaktionen einsetzen.It is recommended that you test that the API behaves correctly with your provider before you rely on it for managing transactions. Sollte die API nicht funktionieren, wenden Sie sich bitte an den Maintainer des Datenbankanbieters.You are encouraged to contact the maintainer of the database provider if it does not.

  2. Ab Version 2.1 enthält die System.Transactions-Implementierung in .NET Core keine Unterstützung für verteilte Transaktionen. Aus diesem Grund können Sie weder mit TransactionScope noch mit CommittableTransaction Transaktionen in mehreren Ressourcen-Managern koordinieren.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.