Arbeiten mit TransaktionenWorking with Transactions

Hinweis

Nur EF6 und höher: Die Features, APIs usw., die auf dieser Seite erläutert werden, wurden in Entity Framework 6 eingeführt.EF6 Onwards Only - The features, APIs, etc. discussed in this page were introduced in Entity Framework 6. Wenn Sie eine frühere Version verwenden, gelten manche Informationen nicht.If you are using an earlier version, some or all of the information does not apply.

Dieses Dokument beschreibt die Verwendung von Transaktionen in EF6 einschließlich der Verbesserungen, die wir seit EF5 hinzugefügt haben, um die Arbeit mit Transaktionen zu vereinfachen.This document will describe using transactions in EF6 including the enhancements we have added since EF5 to make working with transactions easy.

Funktionsweise von EF standardmäßigWhat EF does by default

Wenn Sie in allen Versionen von Entity Framework SaveChanges () ausführen, um die Datenbank einzufügen, zu aktualisieren oder zu löschen, packt das Framework diesen Vorgang in eine Transaktion.In all versions of Entity Framework, whenever you execute SaveChanges() to insert, update or delete on the database the framework will wrap that operation in a transaction. Diese Transaktion dauert nur lange genug, um den Vorgang auszuführen und dann abgeschlossen zu werden.This transaction lasts only long enough to execute the operation and then completes. Wenn Sie einen solchen Vorgang ausführen, wird eine neue Transaktion gestartet.When you execute another such operation a new transaction is started.

Beginnend mit "EF6 Database.Executesqlcommand () " wird der Befehl in einer Transaktion umschlossen, wenn noch keiner vorhanden ist.Starting with EF6 Database.ExecuteSqlCommand() by default will wrap the command in a transaction if one was not already present. Es gibt über Ladungen dieser Methode, die es Ihnen ermöglichen, dieses Verhalten zu überschreiben, wenn Sie möchten.There are overloads of this method that allow you to override this behavior if you wish. Außerdem wird in EF6 die Ausführung gespeicherter Prozeduren, die im Modell durch APIs wie ObjectContext.Executefunction () enthalten sind, identisch (mit der Ausnahme, dass das Standardverhalten im Moment nicht überschrieben werden kann).Also in EF6 execution of stored procedures included in the model through APIs such as ObjectContext.ExecuteFunction() does the same (except that the default behavior cannot at the moment be overridden).

In beiden Fällen ist die Isolationsstufe der Transaktion unabhängig von der Isolationsstufe, die der Datenbankanbieter als Standardeinstellung ansieht.In either case, the isolation level of the transaction is whatever isolation level the database provider considers its default setting. Standardmäßig wird beispielsweise auf SQL Server für die ein Lese Commit ausgeführt wird.By default, for instance, on SQL Server this is READ COMMITTED.

Entity Framework packt keine Abfragen in einer Transaktion.Entity Framework does not wrap queries in a transaction.

Diese Standardfunktionalität eignet sich für viele Benutzer, und wenn dies der Fall ist, ist es nicht erforderlich, in EF6 etwas anderes zu tun. Schreiben Sie den Code einfach wie gewohnt.This default functionality is suitable for a lot of users and if so there is no need to do anything different in EF6; just write the code as you always did.

Einige Benutzer benötigen jedoch eine bessere Kontrolle über Ihre Transaktionen – Dies wird in den folgenden Abschnitten beschrieben.However some users require greater control over their transactions – this is covered in the following sections.

Funktionsweise der APIsHow the APIs work

Vor EF6 Entity Framework das Öffnen der Datenbankverbindung selbst bestanden (es wurde eine Ausnahme ausgelöst, wenn eine bereits geöffnete Verbindung bestanden wurde).Prior to EF6 Entity Framework insisted on opening the database connection itself (it threw an exception if it was passed a connection that was already open). Da eine Transaktion nur für eine geöffnete Verbindung gestartet werden kann, bedeutete dies, dass ein Benutzer nur mehrere Vorgänge in eine Transaktion einschließen konnte, indem er entweder einen transaktionscope verwendet oder die ObjectContext. Connection -Eigenschaft verwendet und mit dem Aufrufen von Open () und BeginTransaction () direkt auf dem zurückgegebenen EntityConnection -Objekt beginnt.Since a transaction can only be started on an open connection, this meant that the only way a user could wrap several operations into one transaction was either to use a TransactionScope or use the ObjectContext.Connection property and start calling Open() and BeginTransaction() directly on the returned EntityConnection object. Außerdem würden API-Aufrufe, die die Datenbank kontaktiert haben, fehlschlagen, wenn Sie eine Transaktion auf der zugrunde liegenden Datenbankverbindung selbst gestartet haben.In addition, API calls which contacted the database would fail if you had started a transaction on the underlying database connection on your own.

Hinweis

Die Einschränkung, dass nur geschlossene Verbindungen akzeptiert werden, wurde in Entity Framework 6 entfernt.The limitation of only accepting closed connections was removed in Entity Framework 6. Weitere Informationen finden Sie unter Verbindungs Verwaltung.For details, see Connection Management.

Ab EF6 bietet das Framework nun Folgendes:Starting with EF6 the framework now provides:

  1. Database. BeginTransaction () : eine einfachere Methode für einen Benutzer, Transaktionen selbst innerhalb eines vorhandenen dbcontext zu starten und abzuschließen – so können mehrere Vorgänge in der gleichen Transaktion kombiniert werden, und es muss entweder ein Commit oder ein Rollback als ein Commit ausgeführt werden.Database.BeginTransaction() : An easier method for a user to start and complete transactions themselves within an existing DbContext – allowing several operations to be combined within the same transaction and hence either all committed or all rolled back as one. Außerdem kann der Benutzer die Isolationsstufe für die Transaktion leichter angeben.It also allows the user to more easily specify the isolation level for the transaction.
  2. Database. UseTransaction () : Hiermit kann dbcontext eine Transaktion verwenden, die außerhalb der Entity Framework gestartet wurde.Database.UseTransaction() : which allows the DbContext to use a transaction which was started outside of the Entity Framework.

Kombinieren mehrerer Vorgänge zu einer Transaktion innerhalb desselben KontextsCombining several operations into one transaction within the same context

Database. BeginTransaction () verfügt über zwei über schreibungen – eine, die einen expliziten IsolationLevel annimmt und einen, der keine Argumente annimmt und den standardmäßigen IsolationLevel des zugrunde liegenden Datenbankanbieters verwendet.Database.BeginTransaction() has two overrides – one which takes an explicit IsolationLevel and one which takes no arguments and uses the default IsolationLevel from the underlying database provider. Beide über schreibungen geben ein dbcontexttransaction -Objekt zurück, das Commit () -und Rollback () -Methoden bereitstellt, die Commit und Rollback für die zugrunde liegende Speicher Transaktion ausführen.Both overrides return a DbContextTransaction object which provides Commit() and Rollback() methods which perform commit and rollback on the underlying store transaction.

Dbcontexttransaction soll verworfen werden, nachdem ein Commit oder ein Rollback ausgeführt wurde.The DbContextTransaction is meant to be disposed once it has been committed or rolled back. Eine einfache Möglichkeit hierfür ist die Verwendung von (...) {...}One easy way to accomplish this is the using(…) {…} Syntax, die "verwerfen () " automatisch aufruft, wenn der using-Block abgeschlossen wird:syntax which will automatically call Dispose() when the using block completes:

using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Data.SqlClient;
using System.Linq;
using System.Transactions;

namespace TransactionsExamples
{
    class TransactionsExample
    {
        static void StartOwnTransactionWithinContext()
        {
            using (var context = new BloggingContext())
            {
                using (var dbContextTransaction = context.Database.BeginTransaction())
                {
                    context.Database.ExecuteSqlCommand(
                        @"UPDATE Blogs SET Rating = 5" +
                            " WHERE Name LIKE '%Entity Framework%'"
                        );

                    var query = context.Posts.Where(p => p.Blog.Rating >= 5);
                    foreach (var post in query)
                    {
                        post.Title += "[Cool Blog]";
                    }

                    context.SaveChanges();

                    dbContextTransaction.Commit();
                }
            }
        }
    }
}

Hinweis

Das Starten einer Transaktion erfordert, dass die zugrunde liegende Speicher Verbindung geöffnet ist.Beginning a transaction requires that the underlying store connection is open. Wenn Sie also Database. BeginTransaction () aufrufen, wird die Verbindung geöffnet, wenn Sie nicht bereits geöffnet ist.So calling Database.BeginTransaction() will open the connection if it is not already opened. Wenn dbcontexttransaction die Verbindung geöffnet hat, wird Sie geschlossen, wenn "verwerfen ()" aufgerufen wird.If DbContextTransaction opened the connection then it will close it when Dispose() is called.

Übergeben einer vorhandenen Transaktion an den KontextPassing an existing transaction to the context

Manchmal möchten Sie eine Transaktion, die im Bereich noch breiter ist und Vorgänge in derselben Datenbank, aber außerhalb von EF, umfasst.Sometimes you would like a transaction which is even broader in scope and which includes operations on the same database but outside of EF completely. Um dies zu erreichen, müssen Sie die Verbindung öffnen und die Transaktion selbst starten und EF a dann mitteilen, dass die bereits geöffnete Datenbankverbindung verwendet werden soll, und b), um die vorhandene Transaktion für diese Verbindung zu verwenden.To accomplish this you must open the connection and start the transaction yourself and then tell EF a) to use the already-opened database connection, and b) to use the existing transaction on that connection.

Zu diesem Zweck müssen Sie einen Konstruktor in der Kontext Klasse definieren und verwenden, die von einem der dbcontext-Konstruktoren erbt, von dem ich einen vorhandenen Verbindungsparameter und II) den booleschen Wert contextownsconnection erbt.To do this you must define and use a constructor on your context class which inherits from one of the DbContext constructors which take i) an existing connection parameter and ii) the contextOwnsConnection boolean.

Hinweis

Das contextownsconnection-Flag muss auf "false" festgelegt werden, wenn es in diesem Szenario aufgerufen wird.The contextOwnsConnection flag must be set to false when called in this scenario. Dies ist wichtig, da es Entity Framework, dass die Verbindung nicht geschlossen werden soll, wenn es damit abgeschlossen ist (z. b. siehe Zeile 4 unten):This is important as it informs Entity Framework that it should not close the connection when it is done with it (for example, see line 4 below):

using (var conn = new SqlConnection("..."))
{
    conn.Open();
    using (var context = new BloggingContext(conn, contextOwnsConnection: false))
    {
    }
}

Außerdem müssen Sie die Transaktion selbst starten (einschließlich der IsolationLevel-Einstellung, wenn Sie die Standardeinstellung vermeiden möchten) und Entity Framework wissen, dass bereits eine Transaktion für die Verbindung gestartet wurde (siehe Zeile 33 unten).Furthermore, you must start the transaction yourself (including the IsolationLevel if you want to avoid the default setting) and let Entity Framework know that there is an existing transaction already started on the connection (see line 33 below).

Anschließend können Sie Daten Bank Vorgänge entweder direkt auf der SqlConnection selbst oder im dbcontext ausführen.Then you are free to execute database operations either directly on the SqlConnection itself, or on the DbContext. Alle Vorgänge dieser Art werden innerhalb einer Transaktion ausgeführt.All such operations are executed within one transaction. Sie übernehmen die Verantwortung für das Commit oder Rollback der Transaktion und für das Aufrufen von "verwerfen ()" sowie für das Schließen und verwerfen der Datenbankverbindung.You take responsibility for committing or rolling back the transaction and for calling Dispose() on it, as well as for closing and disposing the database connection. Beispiel:For example:

using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Data.SqlClient;
using System.Linq;
using System.Transactions;

namespace TransactionsExamples
{
     class TransactionsExample
     {
        static void UsingExternalTransaction()
        {
            using (var conn = new SqlConnection("..."))
            {
               conn.Open();

               using (var sqlTxn = conn.BeginTransaction(System.Data.IsolationLevel.Snapshot))
               {
                   var sqlCommand = new SqlCommand();
                   sqlCommand.Connection = conn;
                   sqlCommand.Transaction = sqlTxn;
                   sqlCommand.CommandText =
                       @"UPDATE Blogs SET Rating = 5" +
                        " WHERE Name LIKE '%Entity Framework%'";
                   sqlCommand.ExecuteNonQuery();

                   using (var context =  
                     new BloggingContext(conn, contextOwnsConnection: false))
                    {
                        context.Database.UseTransaction(sqlTxn);

                        var query =  context.Posts.Where(p => p.Blog.Rating >= 5);
                        foreach (var post in query)
                        {
                            post.Title += "[Cool Blog]";
                        }
                       context.SaveChanges();
                    }

                    sqlTxn.Commit();
                }
            }
        }
    }
}

Löschen der TransaktionClearing up the transaction

Sie können NULL an Database. UseTransaction () übergeben, um Entity Framework Wissen der aktuellen Transaktion zu löschen.You can pass null to Database.UseTransaction() to clear Entity Framework’s knowledge of the current transaction. Entity Framework für die vorhandene Transaktion weder einen Commit durchführen noch ein Rollback durchführen, verwenden Sie daher with Care und nur, wenn Sie sicher sind, dass dies alles ist, was Sie tun möchten.Entity Framework will neither commit nor rollback the existing transaction when you do this, so use with care and only if you’re sure this is what you want to do.

Fehler in UseTransactionErrors in UseTransaction

Es wird eine Ausnahme von Database. UseTransaction () angezeigt, wenn Sie eine Transaktion übergeben, wenn Folgendes gilt:You will see an exception from Database.UseTransaction() if you pass a transaction when:

  • Entity Framework bereits eine Transaktion vorhanden ist.Entity Framework already has an existing transaction
  • Entity Framework wird bereits in einem Transaktions Bereich ausgeführt.Entity Framework is already operating within a TransactionScope
  • Das Verbindungs Objekt in der Transaktion hat den Wert NULL.The connection object in the transaction passed is null. Das heißt, die Transaktion ist keiner Verbindung zugeordnet – normalerweise ist dies ein Vorzeichen, dass diese Transaktion bereits abgeschlossen wurde.That is, the transaction is not associated with a connection – usually this is a sign that that transaction has already completed
  • Das Verbindungs Objekt in der übergebenen Transaktion entspricht nicht der Verbindung des Entity Framework.The connection object in the transaction passed does not match the Entity Framework’s connection.

Verwenden von Transaktionen mit anderen FeaturesUsing transactions with other features

In diesem Abschnitt wird erläutert, wie die obigen Transaktionen mit interagieren:This section details how the above transactions interact with:

  • VerbindungsstabilitätConnection resiliency
  • Asynchrone MethodenAsynchronous methods
  • Transaktionscope-TransaktionenTransactionScope transactions

VerbindungsstabilitätConnection Resiliency

Das neue Feature für die Verbindungs Resilienz funktioniert nicht mit vom Benutzer initiierten Transaktionen.The new Connection Resiliency feature does not work with user-initiated transactions. Weitere Informationen finden Sie unter Wiederholungs Versuche für Ausführungs Strategien.For details, see Retrying Execution Strategies.

Asynchrone ProgrammierungAsynchronous Programming

Der Ansatz, der in den vorherigen Abschnitten beschrieben wird, benötigt keine weiteren Optionen oder Einstellungen für die Verwendung der asynchronen Abfrage-und Speichermethoden.The approach outlined in the previous sections needs no further options or settings to work with the asynchronous query and save methods. Beachten Sie jedoch, dass dies in Abhängigkeit davon, was Sie in den asynchronen Methoden tun, dazu führen kann, dass Transaktionen mit langer Ausführungszeit ausgeführt werden – was wiederum zu Deadlocks oder Blockierungen führen kann, was für die Leistung der gesamten Anwendung schlecht ist.But be aware that, depending on what you do within the asynchronous methods, this may result in long-running transactions – which can in turn cause deadlocks or blocking which is bad for the performance of the overall application.

Transaktionscope-TransaktionenTransactionScope Transactions

Vor EF6 war die empfohlene Vorgehensweise zum Bereitstellen größerer Bereichs Transaktionen die Verwendung eines transaktionscope-Objekts:Prior to EF6 the recommended way of providing larger scope transactions was to use a TransactionScope object:

using System.Collections.Generic;
using System.Data.Entity;
using System.Data.SqlClient;
using System.Linq;
using System.Transactions;

namespace TransactionsExamples
{
    class TransactionsExample
    {
        static void UsingTransactionScope()
        {
            using (var scope = new TransactionScope(TransactionScopeOption.Required))
            {
                using (var conn = new SqlConnection("..."))
                {
                    conn.Open();

                    var sqlCommand = new SqlCommand();
                    sqlCommand.Connection = conn;
                    sqlCommand.CommandText =
                        @"UPDATE Blogs SET Rating = 5" +
                            " WHERE Name LIKE '%Entity Framework%'";
                    sqlCommand.ExecuteNonQuery();

                    using (var context =
                        new BloggingContext(conn, contextOwnsConnection: false))
                    {
                        var query = context.Posts.Where(p => p.Blog.Rating > 5);
                        foreach (var post in query)
                        {
                            post.Title += "[Cool Blog]";
                        }
                        context.SaveChanges();
                    }
                }

                scope.Complete();
            }
        }
    }
}

Der SqlConnection-und der-Entity Framework würden beide die Ambient-Transaktion transaktionscope verwenden und daher zusammen ein Commit ausgeführt werden.The SqlConnection and Entity Framework would both use the ambient TransactionScope transaction and hence be committed together.

Beginnend mit .NET 4.5.1 transaktionscope wurde aktualisiert und funktioniert auch mit asynchronen Methoden über die transaktionscopeasyncflowoption -Enumeration:Starting with .NET 4.5.1 TransactionScope has been updated to also work with asynchronous methods via the use of the TransactionScopeAsyncFlowOption enumeration:

using System.Collections.Generic;
using System.Data.Entity;
using System.Data.SqlClient;
using System.Linq;
using System.Transactions;

namespace TransactionsExamples
{
    class TransactionsExample
    {
        public static void AsyncTransactionScope()
        {
            using (var scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
            {
                using (var conn = new SqlConnection("..."))
                {
                    await conn.OpenAsync();

                    var sqlCommand = new SqlCommand();
                    sqlCommand.Connection = conn;
                    sqlCommand.CommandText =
                        @"UPDATE Blogs SET Rating = 5" +
                            " WHERE Name LIKE '%Entity Framework%'";
                    await sqlCommand.ExecuteNonQueryAsync();

                    using (var context = new BloggingContext(conn, contextOwnsConnection: false))
                    {
                        var query = context.Posts.Where(p => p.Blog.Rating > 5);
                        foreach (var post in query)
                        {
                            post.Title += "[Cool Blog]";
                        }

                        await context.SaveChangesAsync();
                    }
                }
            }
        }
    }
}

Es gibt noch einige Einschränkungen für den transaktionscope-Ansatz:There are still some limitations to the TransactionScope approach:

  • Erfordert .NET 4.5.1 oder höher, um mit asynchronen Methoden zu arbeiten.Requires .NET 4.5.1 or greater to work with asynchronous methods.
  • Sie kann nur in cloudumgebungen verwendet werden, wenn Sie sicher sind, dass Sie über eine und nur eine Verbindung verfügen (cloudszenarien unterstützen keine verteilten Transaktionen).It cannot be used in cloud scenarios unless you are sure you have one and only one connection (cloud scenarios do not support distributed transactions).
  • Sie kann nicht mit dem Database. UseTransaction ()-Ansatz der vorherigen Abschnitte kombiniert werden.It cannot be combined with the Database.UseTransaction() approach of the previous sections.
  • Sie löst Ausnahmen aus, wenn Sie DDL ausgeben und verteilte Transaktionen nicht über den MSDTC-Dienst aktiviert haben.It will throw exceptions if you issue any DDL and have not enabled distributed transactions through the MSDTC Service.

Vorteile des transaktionscope-Ansatzes:Advantages of the TransactionScope approach:

  • Eine lokale Transaktion wird automatisch auf eine verteilte Transaktion aktualisiert, wenn Sie mehr als eine Verbindung mit einer bestimmten Datenbank herstellen oder eine Verbindung mit einer Datenbank mit einer Verbindung mit einer anderen Datenbank innerhalb derselben Transaktion herstellen (Hinweis: der MSDTC-Dienst muss so konfiguriert sein, dass verteilte Transaktionen damit funktionieren).It will automatically upgrade a local transaction to a distributed transaction if you make more than one connection to a given database or combine a connection to one database with a connection to a different database within the same transaction (note: you must have the MSDTC service configured to allow distributed transactions for this to work).
  • Einfache Codierung.Ease of coding. Wenn Sie möchten, dass die Transaktion Ambient ist und implizit im Hintergrund und nicht explizit unter ihrer Kontrolle behandelt wird, ist der transaktionscope-Ansatz möglicherweise besser geeignet.If you prefer the transaction to be ambient and dealt with implicitly in the background rather than explicitly under you control then the TransactionScope approach may suit you better.

Zusammengefasst mit den neuen APIs "Database. BeginTransaction ()" und "Database. UseTransaction ()", ist der transaktionscope-Ansatz für die meisten Benutzer nicht mehr erforderlich.In summary, with the new Database.BeginTransaction() and Database.UseTransaction() APIs above, the TransactionScope approach is no longer necessary for most users. Wenn Sie transaktionscope weiterhin verwenden, beachten Sie die oben genannten Einschränkungen.If you do continue to use TransactionScope then be aware of the above limitations. Wir empfehlen, nach Möglichkeit den in den vorherigen Abschnitten beschriebenen Ansatz zu verwenden.We recommend using the approach outlined in the previous sections instead where possible.