52

Ben yan etkiler ve aşağıdaki desen potansiyel sorunlar ilgileniyorum: Tek prosedür ile kullanıldığında ses benim anlayış bu desen kadarıylaTL CATCH ROLLBACK kalıbı içeren iç içe geçmiş prosedürler?

CREATE PROCEDURE [Name] 
AS 
BEGIN 
    BEGIN TRANSACTION 
    BEGIN TRY 
     [...Perform work, call nested procedures...] 
    END TRY 
    BEGIN CATCH 
     ROLLBACK TRANSACTION 
     RAISERROR [rethrow caught error using @ErrorNumber, @ErrorMessage, etc] 
    END CATCH 
END 

edilir - tüm prosedür olacak ya tam onun hatasız ifadeler veya tüm eylemleri geri yükler ve hatayı rapor eder.

Ancak, bir saklı yordam, işin bir alt birimi yapmak için saklı yordam çağrısı yaptığında (daha küçük yordamın bazen kendi kendine çağrıldığı anlaşılırken), geri almalarla ilgili bir sorun görüyorum - bilgi iletisi (Seviye 16) The ROLLBACK TRANSACTION request has no corresponding BEGIN TRANSACTION. belirtilerek yayınlanmıştır. Alt-yordamdaki geri dönüş, yalnızca alt işlemde başlatılan işlemden ziyade, en dıştaki işlemi geri aldığından kaynaklanmaktadır.

Herhangi bir hata meydana geldiğinde (ve SQL hatası olarak müşteriye rapor edilirken hata oluştuğunda) tüm şeyin geri alınmasını ve iptal edilmesini istiyorum, sadece dış katmanlardan gelen tüm yan etkilerden emin değilim. daha önce geri alınmış bir işlemi geri almak için Her bir TL CATCH katmanında bir geri dönüş yapmadan önce @@TRANCOUNT'un bir kontrolü olabilir mi? can, bir saklı yordam, "MySubProcedure", MyStoredProcedure hata yükseltir içine denilen durumunda

try 
{ 
    var context = new MyDataContext(); 
    using (var transaction = new TransactionScope()) 
    {  
      // Some Linq stuff 
     context.SubmitChanges(); 
     context.MyStoredProcedure(); 
     transactionComplete(); 
    } 
} 
catch 
{ 
    // An error occured! 
} 

:

Son olarak, bu kendi işlem katmanına sahiptir istemci ucu (Linq2SQL) vardır Daha önce MyStoredProcedure'da yapılan her şeyin geri alınacağından, SubmitChanges tarafından yapılan tüm Linq işlemlerinin geri alınacağından ve son olarak hatanın kaydedileceğinden emin olabilirim. Veya tüm işlemin atomik olduğundan emin olmak için desenimde değiştirmek zorundayım, ancak yine de çocuk parçalarının tek tek kullanılmasına izin veriliyor (yani alt prosedürler hala aynı atom korumasına sahip olmalıdır)

+0

Alt yordamınız, dış işleminizden (örn. Işlem) bir şeye güveniyorsa, bana öyle olmamalı ki, çok fazla eşleşme var gibi görünüyor. Düzenleme: Sorunu yanlış okudum galiba –

cevap

103

Bu edilir şablon (hata günlüğü kaldırıldı)

Bu işlemek için tasarlanmıştır

açıklamalar:

  • tüm TXN başlayacak ve taahhüt/rollbacks @@TRANCOUNT giriş ve çıkış

  • aynıdır böylece eşleştirilmesi gerekir

  • @@TRANCOUNT uyuşmazlığı neden 266 hatası neden oldu

    • BEGIN TRAN artışlarla @@TRANCOUNT

    • COMMIT@@TRANCOUNT

    • ROLLBACK döndürür @@TRANCOUNT

  • sıfıra Geçerli kapsamiçin @@TRANCOUNT azaltma edemez azaltır Bu ne düşündüğünüzü ediyorum "içeri işlem"

  • SET XACT_ABORT ON eşleşmeyen @@TRANCOUNT
    kaynaklanan hata 266 bastırır Ve ayrıca bu olanak dba.se bu "SQL Server Transaction Timeout" gibi konularda

  • ilgilenir olduğunu istemci tarafı TXN'leri (LINQ gibi) Tek bir saklı yordam, dağıtılmış veya XA işleminin bir parçası veya yalnızca istemci kodunda başlatılan bir işlem olabilir (say.Net TransactionScope)

Kullanımı:

  • Her saklı yordam aynı şablona uygun olmalıdır

  • Yani daha oluşturmaz Özeti TXN'ler senden daha iyi. d

kod

CREATE PROCEDURE [Name] 
AS 
SET XACT_ABORT, NOCOUNT ON 

DECLARE @starttrancount int 

BEGIN TRY 
    SELECT @starttrancount = @@TRANCOUNT 

    IF @starttrancount = 0 
     BEGIN TRANSACTION 

     [...Perform work, call nested procedures...] 

    IF @starttrancount = 0 
     COMMIT TRANSACTION 
END TRY 
BEGIN CATCH 
    IF XACT_STATE() <> 0 AND @starttrancount = 0 
     ROLLBACK TRANSACTION; 
    THROW; 
    --before SQL Server 2012 use 
    --RAISERROR [rethrow caught error using @ErrorNumber, @ErrorMessage, etc] 
END CATCH 
GO 

Notlar:

  • geri alma çek nedeniyle SET XACT_ABORT ON aslında gereksiz olduğunu. Ancak, beni daha iyi hissettiriyor, tek yapmamı sağlıyor ve

  • Remus Rusanu'da kaydetme noktalarını kullanan bir similar shell olmasını istemediğiniz durumlar için izin veriyor. Ben bir atom DB çağrısı tercih ve @AlexKuznetsov bahsettiği hata numarası ve satır numarası dönen sorununu çözmek için kendi makalesinde

+0

SET NOCOUNT ON gerekli değil mi? –

+7

Her zaman SET NOCOUNT ON imho'yu kullanmalısınız. –

+1

@Carl R: SET NOCOUNT ON, kod ve tetikleyicilerde oldukça önemlidir: o kadar çok istemci kodu onsuz kırılır. Ayrıca, "xx satırlarını etkilenen iletiler" işlemek için geçen süreyi ölçmek mümkün. Bkz. Http://stackoverflow.com/q/1483732/27535 (sorum) lütfen – gbn

1

gibi kısmi güncelleştirmeleri kullanmayın, tek hatayı aşağıdaki şekilde yükseltebilirsiniz:

DECLARE @ErrorMessage NVARCHAR(4000) 
DECLARE @ErrorSeverity INT 
DECLARE @ErrorState INT 
DECLARE @ErrorLine INT 
DECLARE @ErrorNumber INT 

SELECT @ErrorMessage = ERROR_MESSAGE(), 
@ErrorSeverity = ERROR_SEVERITY(), 
@ErrorState = ERROR_STATE(), 
@ErrorNumber = ERROR_NUMBER(), 
@ErrorLine = ERROR_LINE() 

RAISERROR (@ErrorMessage, @ErrorSeverity, @ErrorState, @ErrorNumber, @ErrorLine) 
0

- durumda doesnt geri doğru hata sayısı

DECLARE 
    @ErrorMessage nvarchar(4000), 
    @ErrorSeverity int, 
    @ErrorState int, 
    @ErrorLine int, 
    @ErrorNumber int 

BEGIN TRY 
SELECT 1/0; -- CATCH me 
END TRY 

BEGIN CATCH 

    DECLARE @err int = @@ERROR 

    PRINT @err   -- 8134, divide by zero 
    PRINT ERROR_NUMBER() -- 8134 

    SELECT 
    @ErrorMessage = ERROR_MESSAGE(), 
    @ErrorSeverity = ERROR_SEVERITY(), 
    @ErrorState = ERROR_STATE(), 
    @ErrorNumber = ERROR_NUMBER(), 
    @ErrorLine  = ERROR_LINE() 

    -- error number = 50000 :( 
    RAISERROR (@ErrorMessage, @ErrorSeverity, @ErrorState, @ErrorNumber, @ErrorLine) 

END CATCH 

-- error number = 8134 
SELECT 1/0 
+0

Haklısınız, ama bu Amanda'nın cevabı hakkında bir yorumda bulunuyor. –

-1

yukarıda @Amanda yöntemi rethrow dışında CATCH gerekli depolanmıştır yordamlara zincir çağrı işleme hiçbir özel bir hata çok uzun değildir o olabilir Böyle basit şablonu kullanmak uygun olacaktır:

create procedure someNestedSP 
as 
SET XACT_ABORT ON 
begin transaction 
-- do some work or call some other similar SP 
commit transaction 

Ayrıca herhangi bir hata durumunda tüm "iç içe" olanlarla kök İşlem geri istiyorum ama kod @ GBN çözümüyle daha kısa ve daha basittir. Yine de XACT_ABORT, burada bahsedilen sorunların çoğunu halleder.

İşlem yuvalama için ek yük ek yükü olabilir, ancak çok yüksek olmayabilir, sanırım.

+0

TL/CATCH blokları, özellikle tetikleyicilerin dahil olduğu durumlarda daha fazla tahmin edilebilir hata işleme sunar. Ayrıca, bu desen hatayı yakalama, günlüğe kaydetme veya yeniden düzenleme seçeneği sunmaz. Ya da veri doğrulama başarısız olursa kendi atmak. Ve her biri bana benzer bir desen kullanıyor https://www.google.ch/webhp?sourceid=chrome-instant&ion=1&espv=2&ie=UTF-8#safe=off&q=erland+sommarskog+try+catch&* Seçiminiz, ama bu kod asla iş yerimde yaşanmaz. – gbn

İlgili konular