2015-07-15 39 views
9

Używam C# i ADO.Net z TransactionScope do uruchomienia transakcji w aplikacji ASP.Net. Ta transakcja ma na celu zapisanie niektórych danych w wielu tabelach, a następnie wysłanie e-maila do subskrybentów.Korzystanie z TransactionScope wokół procedura składowana z transakcji w SQL Server 2014

Pytanie: czy jest to ważne zastosowanie TransactionScope, gdy zawiera wywołanie procedury przechowywanej, który ma swój własny transakcji w SQL Server 2014, czy powinienem usunąć oświadczenia transakcji SQL tj begin tran, commit tran i rollback tran sprawozdań z procedura składowana jest wywoływana w ramach tej TransactionScope?

Kod C# dla tego scenariusza oraz kod T-SQL procedury składowanej są wymienione poniżej.

kod C# za pomocą 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 procedury przechowywanej 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 

Odpowiedz

12

Tak, TransactionScope może nadal pracować podczas owijania TSQL BEGIN/COMMIT TRANSACTION lub ADO SqlConnection.BeginTransaction. Podczas owijania jednego połączenia, zachowanie jest zbliżone do transakcji gniazdujących w Sql:

  • @@TranCount będzie zwiększany po każdej BEGIN TRAN

  • COMMIT TRAN po prostu zmniejszamy @@TRANCOUNT. Transakcja zostanie zatwierdzona tylko wtedy, gdy @@TRANCOUNT osiągnie zero.

Jednakże:.

  • ROLLBACK TRAN będzie przerwać całą transakcję (tzn @@TRANCOUNT to zero), chyba że używasz Save Points (tj SAVE TRANSACTION xx ... ROLLBACK TRANSACTION xx
  • Przy stosowaniu procedur przechowywanych, będziesz otrzymywać błąd, jeśli połączenie @@TRANCOUNT różni się podczas opuszczania SPROC od wartości, jaką miał przy wejściu do SPROC.

W rezultacie zazwyczaj łatwiej jest zostawić semantykę transakcji na TransactionScope i usunąć wszelkie logiki ręczne BEGIN TRAN/COMMIT TRAN z zaśmiecania swojego TSQL.

Edit - objaśnianie komentarzach poniżej

  • w przypadku PO, tym sproc NIE został napisany z transakcji zagnieżdżonych w umyśle (tzn czy owinięty przez SQL lub .Net transakcją zewnętrzną), konkretnie, blok ROLLBACK w bloku BEGIN CATCH przerwie całą zewnętrzną transakcję i prawdopodobnie spowoduje dalsze błędy w zewnętrznym TransactionScope, ponieważ reguła @@TRANCOUNT nie została dotrzymana. A nested transaction pattern such as this należy przestrzegać, jeśli SPROC musi działać zarówno w trybie zagnieżdżonym, jak i samodzielnym.

  • SavePoints do not work with Distributed transactions i TransactionScope może z łatwością escalate into a distributed transaction np. jeśli używasz różnych ciągów połączeń lub kontrolujesz inne zasoby w zakresie transakcji.

W rezultacie, polecam refactoring PROC do tylko „szczęśliwy” rdzeń/wewnętrznej przypadku, nazywając tę ​​wewnętrzną proc z zakresu transakcji, a robi żadnej obsługi wyjątków i wycofywania tam. Jeśli potrzebujesz również wywołać proc z Ad Hoc Sql, a następnie dostarczyć zewnętrzne opakowanie Proc, który ma wyjątek obsługi:

-- Just the happy case. This is called from .Net TransactionScope 
CREATE PROC dbo.InnerNonTransactional 
    AS 
    BEGIN 
     UPDATE TABLE1... 
     UPDATE TABLE2 .... 
    END; 

-- Only needed if you also need to call this elsewhere, e.g. from AdHoc Sql 
CREATE PROC dbo.OuterTransactional 
    AS 
    BEGIN 
     BEGIN TRY 
     BEGIN TRAN 
      EXEC dbo.InnerNonTransactional 
     COMMIT TRAN 
     END TRY 
     BEGIN CATCH 
     -- Rollback and handling code here. 
     END CATCH 
    END; 
+0

Dzięki za szczegółową odpowiedź. Czy miałeś na myśli to, że jeśli w procedurze przechowywanej zostanie wywołany "rollback tran", to TransactionScope automatycznie wycofa całą transakcję, a nie tylko transakcję w procedurze przechowywanej, nawet jeśli nie zostanie zgłoszony błąd z procedury? – Sunil

+1

Tak, wywołanie 'ROLLBACK TRAN' z TSQL na tym samym połączeniu spowoduje cofnięcie wszelkich innych prac wykonanych na urządzeniu Conn, np. przez ADO. Tylko jeśli użyjesz 'SAVEPOINT', możesz ograniczyć zakres ROLLBACK. Należy jednak pamiętać, że [SavePoints] (http://www.sommarskog.se/wishlist.html#savepointdistr) nie działają z transakcjami rozproszonymi, co może się zdarzyć dość subtelnie w przypadku transakcji TransactionScope, np. jeśli otworzysz więcej niż jedno połączenie w tym samym czasie. Znalazłem jedyny powód, dla którego 'BEGIN/COMMIT TRAN' w SPROC nazwie przez .Net App jest, jeśli SPROC również musi być wykonany w innym miejscu np. ad-hoc z SSMS. – StuartLC

+0

Ale wycofanie tran w procedurze przechowywanej nie spowoduje wycofania transakcji TransactionScope, chyba że zostanie również zgłoszony błąd z procedury składowanej, gdy zostanie wywołana instrukcja wycofania transakcji? – Sunil