2015-07-15 9 views
9

Ich verwende C# und ADO.Net mit einem TransactionScope, um eine Transaktion in einer ASP.Net App auszuführen. Diese Transaktion soll einige Daten über mehrere Tabellen hinweg speichern und dann eine E-Mail an die Abonnenten senden.Verwenden von TransactionScope um eine gespeicherte Prozedur mit Transaktion in SQL Server 2014

Frage: ist es eine gültige Verwendung von TransactionScope, wenn es einen Anruf an eine gespeicherte Prozedur enthält, die eine eigene Transaktion in SQL Server 2014, oder soll ich die SQL-Transaktionsanweisungen dh begin tran, commit tran und rollback tran Aussagen entfernen hat von der gespeicherten Prozedur aufgerufen wird innerhalb dieser TransactionScope?

Der C# -Code für dieses Szenario und auch der T-SQL-Code der gespeicherten Prozedur werden beide unten erwähnt.

C# -Code mit TransactionScope:

try 
    { 
     using (TransactionScope scope = new TransactionScope()) 
     { 
      using (SqlConnection connection1 = new SqlConnection(connectString1)) 
      { 
       // Opening the connection automatically enlists it in the 
       // TransactionScope as a lightweight transaction. 
       connection1.Open(); 

       // SaveEmailData is a stored procedure that has a transaction within it 
       SqlCommand command1 = new SqlCommand("SaveEmailData", connection1); 
       command1.CommandType = CommandType.StoredProcedure; 
       command1.ExecuteNonQuery(); 

      } 

      //Send Email using the helper method 
      EmailHelper.SendCustomerEmails(customerIds); 

      // The Complete method commits the transaction. If an exception has been thrown, 
      // Complete is not called and the transaction is rolled back. 
      scope.Complete(); 

     } 
    } 
    catch(Exception ex) 
    { 
     Logger.Log(ex); 
    } 

TSQL von Stored Procedure SaveEmailData:

SET NOCOUNT ON 

    BEGIN TRY 
     DECLARE @emailToUserId BIGINT 

     BEGIN TRAN 
     -- //update statement. detail statement omitted 
     UPDATE TABLE1... 

     --update statement. detail statement omitted 
     UPDATE TABLE2... 

     IF @@trancount > 0 
     BEGIN 
      COMMIT TRAN 
     END 
    END TRY 

    BEGIN CATCH 

     IF @@TRANCOUNT > 0 
     BEGIN 
      ROLLBACK TRAN 
     END 

     EXEC Error_RaiseToADONET 

    END CATCH 

Antwort

12

Ja, TransactionScope noch arbeiten, wenn SqlConnection.BeginTransaction eine TSQL BEGIN/COMMIT TRANSACTION oder eine ADO-Verpackung. Wenn eine einzelne Verbindung Einwickeln, ist das Verhalten ähnlich wie Verschachtelung Transaktionen in Sql:

  • @@TranCount wird an jedem BEGIN TRAN

  • COMMIT TRAN einfach verringern @@TRANCOUNT erhöht werden. Die Transaktion wird nur festgeschrieben, wenn Null erreicht.

jedoch:.

  • ROLLBACK TRAN wird die gesamte Transaktion abbrechen (dh @@TRANCOUNT to zero), es sei denn, Sie verwenden Save Points (dh SAVE TRANSACTION xx ... ROLLBACK TRANSACTION xx
  • Wenn gespeicherte Prozeduren verwenden, werden Sie erhalten ein Fehler, wenn die @@TRANCOUNT der Verbindung sich beim Verlassen des SPROC von dem Wert unterscheidet, den sie beim Eingeben eines SPROC hatte

Als Ergebnis ist es in der Regel viel einfacher, die Transaktionssemantik auf TransactionScope zu belassen und jede manuelle BEGIN TRAN/COMMIT TRAN Logik aus Ihrem TSQL zu entfernen.

bearbeiten - Klärung der Kommentare unter

  • Im Fall des OP hat die SPROC NICHT mit verschachtelten Transaktionen im Auge (dh ob gewickelt durch eine SQL- oder .Net äußere Transaktion) geschrieben worden ist, Genauer gesagt wird der ROLLBACK Block BEGIN CATCH die gesamte äußere Transaktion abbrechen und wahrscheinlich weitere Fehler im äußeren TransactionScope verursachen, da die @@TRANCOUNT Regel nicht eingehalten wurde. A nested transaction pattern such as this sollte beachtet werden, wenn ein SPROC sowohl in einer verschachtelten als auch in einer Standalone-Transaktion arbeiten muss.

  • SavePoints do not work with Distributed transactions und TransactionScope können leicht escalate into a distributed transaction z.B. wenn Sie andere Verbindungszeichenfolgen verwenden oder andere Ressourcen im Transaktionsbereich steuern.

Als Ergebnis würde ich empfehlen, die PROC in ein nur das ‚glücklich‘ Kern/Innengehäuse Refactoring, diese innere proc aus dem Transaktionsbereich Aufruf und jede Ausnahmebehandlung zu tun und es Rollback. Wenn Sie auch den Proc von Ad-hoc-SQL aufrufen müssen, dann stellen Sie einen externen Wrapper Proc zur Verfügung, der die Ausnahmebehandlung hat:

+0

Danke für die ausführliche Antwort. Meinten Sie, dass, wenn 'rollback tran' in der gespeicherten Prozedur aufgerufen wird, TransactionScope automatisch die gesamte Transaktion und nicht nur die Transaktion der gespeicherten Prozedur zurücksetzt, auch wenn kein Fehler von der Prozedur ausgelöst wird? – Sunil

+1

Ja, wenn 'ROLLBACK TRAN' von TSQL aus aufgerufen wird, wird die Verbindung jede andere Arbeit rückgängig machen, die am Conn ausgeführt wird, z. von ADO. Nur wenn Sie 'SAVEPOINT' verwenden, können Sie den Umfang eines ROLLBACKs begrenzen. Beachten Sie jedoch, dass [SavePoints] (http://www.sommarskog.se/wishlist.html#savepointdistr) nicht mit Distributed Transactions arbeiten, was mit TransactionScope, z. wenn Sie gleichzeitig mehr als einen Conn im Bereich öffnen. Ich habe den einzigen Grund gefunden, 'BEGIN/COMMIT TRAN' in einem SPROC zu behalten, der von einer .Net App aufgerufen wird, wenn der SPROC auch anderswo ausgeführt werden muss, z. Ad-hoc von SSMS. – StuartLC

+0

Aber rollback tran in gespeicherten Prozedur Rollback der TransactionScope-Transaktion nicht, es sei denn, ein Fehler wird auch von gespeicherten Prozedur ausgelöst, wenn Rollback-Anweisung aufgerufen wird? – Sunil

Verwandte Themen