2010-05-08 179 views
7

我使用C#將發票的平面文件導入到數據庫中。如果遇到問題,我正在使用TransactionScope回滾整個操作。在C#中的交易中的交易#

這是一個棘手的輸入文件,其中一行不一定等於一條記錄。它還包括鏈接的記錄。發票將包含標題行,訂單項和總計行。一些發票將需要跳過,但我可能不知道它需要跳過,直到我到達總線。

一種策略是將標題,訂單項和總計行存儲在內存中,並在到達總行後保存所有內容。我現在正在追求這一點。

但是,我想知道是否可以用不同的方式完成。圍繞發票創建「嵌套」交易,插入標題行和訂單項,然後在達到總計行時更新發票。如果確定需要跳過發票,此「嵌套」交易將回滾,但整個交易將繼續。

這是可能的,實用的,你會如何設置?

回答

3

這是通過transaction savepoint完成的。它通常看起來是這樣的:

BEGIN TRANSACTION 
for each invoice 
    SAVE TRANSACTION InvoiceStarted 
    BEGIN TRY 
    Save header 
    Save line 1 
    Save line 2 
    Save Total 
    END TRY 
    BEGIN CATCH 
    ROLLBACK TO Invoicestarted 
    Log Failed Invoice 
    END CATCH 
end for 
COMMIT 

我使用的Transact-SQL基於僞代碼,這絕非偶然。保存點是一個數據庫概念,而.Net事務不支持它們。您可以直接使用SqlTransaction並利用SqlTransaction.Save或者您可以使用建模在exception safe template之後的T-SQL存儲過程。在這種情況下,我建議你避免使用.Net事務(即TransactionScope)。

+0

+1 - 保存點是完美的選擇 – 2010-05-08 23:58:27

2

而不是使用嵌套交易,您可以創建每個發票交易。這樣,只有整個發票的成功更新纔會發生。

如果您按照您描述的方式嵌套事務,那麼您將面臨將整個數據集回滾的危險,這不是您想要的。

+0

如果出現問題,我希望一切回滾。 – Rosco 2010-06-04 15:43:31

2

就我個人而言,我首先會看到是否需要添加發票 - 如果確實需要添加,那麼請插入(在交易中)。否則,只需轉到下一張發票。

我不認爲插入然後按照你描述的方式做回滾是非常棒的。

+0

KISS解決方案的+1。我會將整個文件首先處理到暫存區域(或者是內存中,或者如果它是數據庫中的臨時表),那麼將有效數據保存到主表中。 – Joe 2010-05-08 16:59:13

+0

到目前爲止,在內存中保存當前的發票和關聯的訂單項正在工作。我使用Linq2SQL來存儲它們,所以我不必編寫大量的SQL來處理對象圖。一旦確定發票應該保存,它就會提交併處理數據上下文。 – Rosco 2010-05-10 12:46:24

0

失敗的內部事務將回滾外部事務,因此您無法訪問該路由。

儘管如此,您也可以使用temp(或load)表來僞造它。將每張發票事務性地插入到裝載表中,然後以原子方式從裝載表移動到永久表。

29

TransactionScope和SQL Server都不支持嵌套事務。

您可以嵌套TransactionScope實例,但只具有嵌套事務的外觀。實際上,有一種叫做「環境」交易的東西,一次只能有一件。環境交易的交易取決於您在創建範圍時使用的TransactionScopeOption的內容。

爲了更詳細地解釋,考慮以下因素:

using (var outer = new TransactionScope()) 
{ 
    DoOuterWork(); 

    using (var inner1 = new TransactionScope(TransactionScopeOption.Suppress)) 
    { 
     DoWork1(); 
     inner1.Complete(); 
    } 

    using (var inner2 = new TransactionScope(TransactionScopeOption.RequiresNew)) 
    { 
     DoWork2(); 
     inner2.Complete(); 
    } 

    using (var inner3 = new TransactionScope(TransactionScopeOption.Required)) 
    { 
     DoWork3(); 
     inner3.Complete(); 
    } 

    outer.Complete(); 
} 

下面是每個內部範圍的會發生什麼:

  • inner1在一個隱含的事務執行,獨立的outer 。保證在DoWork1中發生的任何事情都是原子的。如果這個過程中途失敗,你將會有不一致的數據。無論outer發生了什麼,任何在這裏發生的工作都會一直承諾。

  • inner2在新事務中執行,獨立於outer。這是一個不同的交易從outer但它是不是嵌套。如果失敗了,在outerDoOuterWork())發生的工作和任何其他範圍仍然可以提交,但這裏的難題是:如果它完成,然後回滾整個outer交易將回滾所做的工作inner2之內。這就是爲什麼它沒有真正嵌套。另外,inner2將無法​​訪問由outer鎖定的任何行,因此如果您不小心,最終可能會導致死鎖。

  • inner3在執行相同交易爲outer。這是默認行爲。如果DoWork3()失敗且inner3永不完成,則整個事務回滾。同樣,如果inner3成功完成,但回滾outer,則在DoWork3()中完成的任何工作也將回滾。

所以你可以希望看到這些選項都沒有實際嵌套,並且不會給你你想要的。 Required選項近似於嵌套事務,但不能讓您獨立提交或回滾事務內的特定工作單元。

您可以在SQL Server中最真實的嵌套事務是SAVE TRAN語句與一些TRY/CATCH塊相結合。如果你可以把你的邏輯放在一個或多個存儲過程中,這將是一個不錯的選擇。

否則,根據Oded的建議,您需要爲每張發票使用單獨的交易。

+0

感謝您的詳細信息。這在MSDN文檔中並不清楚。 – Rosco 2010-05-10 12:35:19

+0

+1非常好,非常詳細的解釋。幹得好,先生。 – 2011-12-16 17:15:11

+0

+1就像@Remi - 非常好的解釋,它幫助了我今晚救了我。希望我能+更多!謝謝! – Catchops 2013-05-19 05:22:46