12

Mit Entity Framework (Code zuerst in meinem Fall), habe ich eine Operation, die erfordert, dass ich SaveChanges aufrufen, um ein Objekt in der DB zu aktualisieren, und dann SaveChanges erneut, um ein anderes Objekt zu aktualisieren. (Ich brauche die ersten SaveChanges, um ein Problem zu lösen, bei dem EF nicht herausfinden kann, welches Objekt zuerst aktualisiert werden soll).EF: Wie rufe ich SaveChanges zweimal innerhalb einer Transaktion auf?

Ich habe versucht zu tun:

using (var transaction = new TransactionScope()) 
{ 
    // Do something 

    db.SaveChanges(); 

    // Do something else 

    db.SaveChanges(); 

    tramsaction.Complete(); 
} 

Wenn ich laufe, dass ich eine Ausnahme bei dem zweiten SaveChanges Anruf erhalten, sagen „die zugrunde liegenden Anbieter auf offene fehlgeschlagen“. Die innere Ausnahme besagt, dass MSDTC auf meinem Computer nicht aktiviert ist.

Jetzt habe ich Posts an anderer Stelle gesehen, die beschreiben, wie MSDTC zu aktivieren, aber es scheint, dass ich auch Netzwerkzugriff usw. aktivieren müssen. Das klingt wie komplette Overkill hier, da keine anderen Datenbanken beteiligt sind, lassen allein andere Server. Ich möchte nichts tun, was meine gesamte Anwendung weniger sicher (oder langsamer) macht.

Sicherlich muss es eine leichtere Möglichkeit geben, dies zu tun (idealerweise ohne MSDTC) ?!

+0

Verwenden Sie SQL 2008? Abhängig von Ihrer tatsächlichen Logik können Sie mehrere Verbindungen öffnen, ohne eskalieren zu müssen. Hier ist ein großer Beitrag, der zusammenbricht, wenn das DTC aufgerufen wird: [Transaktionsbereich Eskaliert automatisch zu MSDTC] (http: // stackoverflow.com/questions/1690892/transactionscope-automatisches-escalieren-zu-msdtc-on-some-machines) –

+0

Wenn Sie alles in nur einer Transaktion machen wollen, was ist der Unterschied zwischen einmal alles speichern oder alles speichern? In beiden Fällen wird entweder alles gespeichert oder es wird nichts gespeichert, sodass ich keinen Vorteil mehrer Speichervorgänge sehe. Laut dem Kommentar unten - in Sql Server 2005 öffnen mehrere Verbindungen innerhalb einer Transaktion (auch wenn die Quelle identisch ist) bewirkt, dass die Transaktion als eine verteilte Transaktion heraufgestuft wird. Dies wurde in Sql Server 2008 verbessert, wo Sie mehrere Verbindungen zu derselben Datenquelle innerhalb eines Trx öffnen können, ohne dass es zu einer Promotion kommt. – Pawel

+0

@markoreta: Ich benutze SQL Server 2012. Ich habe (oder möchte) nicht mehrere Verbindungen, und don ' t wollen wirklich MSDTC benutzen! –

Antwort

6

Es wird wahrscheinlich durch zwei verschiedene Verbindungen in Ihrer Transaktion verursacht. Versuchen Sie Verbindung für Ihren Betrieb zu steuern manuell:

var objectContext = ((IObjectContextAdapter)db).ObjectContext; 

try { 
    object.Context.Connection.Open(); 
    using (var transaction = new TransactionScope()) { 
     // Do something 

     db.SaveChanges(); 

     // Do something else 

     db.SaveChanges(); 

     transaction.Complete(); 
    } 
} finally { 
    objectContext.Connection.Close(); 
} 
+0

Wenn es in demselben DbContext ist, haben EF6.0 context.Database.BeginTransaction(). Aber das funktioniert gut bei Operationen in verschiedenen DBContexts. –

7

von Savechanges() aufrufen, wie Sie wird es bewirkt, dass die Daten in der Datenbank und dem EF beibehalten werden über die Änderungen zu vergessen es einfach gemacht.

Der Trick besteht darin, SaveChanges (false) zu verwenden, so dass die Änderungen in der DB beibehalten werden, aber EF nicht die Änderungen vergessen, die es macht, so dass Protokollierung/Wiederholung möglich.

 var scope = new TransactionScope(
      TransactionScopeOption.RequiresNew, 
      new TransactionOptions() { IsolationLevel = IsolationLevel.Serializable } 
     ); 

     using (scope) 
     { 
      Entities context1 = new Entities(); 
      // Do Stuff 
      context1.SaveChanges(false); 

      Entities context2 = new Entities(); 
      // Do Stuff 
      context2.SaveChanges(false); 

      scope.Complete(); 
      context1.AcceptAllChanges(); 
      context2.AcceptAllChanges(); 
     } 

P.S. Sobald innerhalb von transactionscope mehr als eine Verbindung geöffnet ist, eskaliert es zu DTC.

+0

Danke. Das klingt sehr plausibel, obwohl mir in meinem Fall der Zustand des lokalen Kontexts egal ist, wenn es einen Fehler gibt, da ich dann sowieso bombardiere und den Kontext zerstöre. Trotzdem, gut zu wissen. –

+0

Was passiert, wenn der erste Update-Vorgang erfolgreich verläuft, aber der zweite Update-Vorgang fehlschlägt? Sind Ihre Daten im DB konsistent? –

+0

Da das Ganze in TransactionScope verpackt ist, sollte ich es hoffen! –

11

Ich weiß, es ist eine Art späte Antwort, aber ich fand es nützlich zu teilen.

Jetzt in EF6 es einfacher ist, dies acheeve von dbContext.Database.BeginTransaction()

wie folgt aus:

using (var context = new BloggingContext()) 
{ 
    using (var dbContextTransaction = context.Database.BeginTransaction()) 
    { 
     try 
     { 
      // do your changes 
      context.SaveChanges(); 

      // do another changes 
      context.SaveChanges(); 

      dbContextTransaction.Commit(); 
     } 
     catch (Exception) 
     { 
      dbContextTransaction.Rollback(); 
     } 
    } 
} 

für weitere Informationen Blick auf this

wieder ist es in EF6 Onwards

+0

Danke, das ist mehr wie es! –

+0

Sie sind willkommen :) –

+0

Sie sollten mehr mit dem Catch-Block als nur Rollback und schlucken Sie die Ausnahme. Log, retrow, ... –

1

Für das Finale Antwort oben ausgewählt, gibt es einen Tippfehler. Korrigierte Zeile unter:

objectContext.Connection.Open(); 

Außerdem müssen Referenzen für System.Data.Entity.Infrastructure hinzufügen und System.Transactions