Deze fout treedt op wanneer u een try/catch-blok in een transactie gebruikt. Laten we een triviaal voorbeeld bekijken:
SET XACT_ABORT ON
IF object_id('tempdb..#t') IS NOT NULL
DROP TABLE #t
CREATE TABLE #t (i INT NOT NULL PRIMARY KEY)
BEGIN TRAN
INSERT INTO #t (i) VALUES (1)
INSERT INTO #t (i) VALUES (2)
INSERT INTO #t (i) VALUES (3)
INSERT INTO #t (i) VALUES (1) -- dup key error, XACT_ABORT kills the batch
INSERT INTO #t (i) VALUES (4)
COMMIT TRAN
SELECT * FROM #t
Wanneer de vierde insert een fout veroorzaakt, wordt de batch beëindigd en wordt de transactie teruggedraaid. Tot nu toe geen verrassingen.
Laten we nu proberen die fout af te handelen met een TRY/CATCH-blok:
SET XACT_ABORT ON
IF object_id('tempdb..#t') IS NOT NULL
DROP TABLE #t
CREATE TABLE #t (i INT NOT NULL PRIMARY KEY)
BEGIN TRAN
INSERT INTO #t (i) VALUES (1)
INSERT INTO #t (i) VALUES (2)
BEGIN TRY
INSERT INTO #t (i) VALUES (3)
INSERT INTO #t (i) VALUES (1) -- dup key error
END TRY
BEGIN CATCH
SELECT ERROR_MESSAGE()
END CATCH
INSERT INTO #t (i) VALUES (4)
/* Error the Current Transaction cannot be committed and
cannot support operations that write to the log file. Roll back the transaction. */
COMMIT TRAN
SELECT * FROM #t
We hebben de dubbele sleutelfout ontdekt, maar verder zijn we niet beter af. Onze batch wordt nog steeds beëindigd en onze transactie wordt nog steeds teruggedraaid. De reden is eigenlijk heel simpel:
TRY/CATCH-blokkeringen hebben geen invloed op transacties.
Omdat XACT_ABORT AAN staat, is de transactie gedoemd op het moment dat de dubbele sleutelfout optreedt. Het is gedaan voor. Het is dodelijk gewond. Het is door het hart geschoten... en de fout is de schuld. TRY/CATCH geeft SQL Server... een slechte naam. (sorry, kon het niet laten)
Met andere woorden, het zal NOOIT zich committeren en zal ALTIJD teruggedraaid worden. Het enige dat een TRY/CATCH-blok kan doen, is de val van het lijk breken. We kunnen de XACT_STATE() . gebruiken functie om te zien of onze transactie begaanbaar is. Als dit niet het geval is, is de enige optie om de transactie terug te draaien.
SET XACT_ABORT ON -- Try with it OFF as well.
IF object_id('tempdb..#t') IS NOT NULL
DROP TABLE #t
CREATE TABLE #t (i INT NOT NULL PRIMARY KEY)
BEGIN TRAN
INSERT INTO #t (i) VALUES (1)
INSERT INTO #t (i) VALUES (2)
SAVE TRANSACTION Save1
BEGIN TRY
INSERT INTO #t (i) VALUES (3)
INSERT INTO #t (i) VALUES (1) -- dup key error
END TRY
BEGIN CATCH
SELECT ERROR_MESSAGE()
IF XACT_STATE() = -1 -- Transaction is doomed, Rollback everything.
ROLLBACK TRAN
IF XACT_STATE() = 1 --Transaction is commitable, we can rollback to a save point
ROLLBACK TRAN Save1
END CATCH
INSERT INTO #t (i) VALUES (4)
IF @@TRANCOUNT > 0
COMMIT TRAN
SELECT * FROM #t
Triggers worden altijd uitgevoerd binnen de context van een transactie, dus als u het gebruik van TRY/CATCH erin kunt vermijden, is het veel eenvoudiger.
Voor een oplossing voor uw probleem kan een CLR Stored Proc in een aparte verbinding terug verbinding maken met SQL Server om de dynamische SQL uit te voeren. U krijgt de mogelijkheid om de code in een nieuwe transactie uit te voeren en de logica voor foutafhandeling is zowel gemakkelijk te schrijven als gemakkelijk te begrijpen in C#.