2013-01-12 82 views
0

我需要從外部數據源向我的數據庫執行數據導入。由於需要下載大量數據,因此導入執行的時間很長,我需要將有關當前導入狀態的定期更新保存到數據庫(供用戶使用)。交易操作同時與非交易操作混合

假設我有兩個表:Import(存儲導入數據)和Status(導入狀態監視表)。

數據導入代碼:

public class Importer 
{ 
    public delegate void ImportHandler(string item); 
    public event ImportHandler ImportStarted; 

    public void OnStart(string item) 
    {    
     ImportStarted(item); 
    } 

    public void Execute(string[] items) 
    { 
     foreach (var item in items) 
     { 
      OnStart(item);      
      PersistImportedData(Download(item)); 
     } 
    } 

    private void PersistImportedData(object data) 
    { 
     using (var connection = new SqlConnection()){ /*saving imported data*/ } 
    } 
} 

起動代碼 - 用於調用導入任務和更新其狀態:

public class Starter 
{ 
    public void Process(string[] items) 
    { 
     var importer = new Importer(); 
     importer.ImportStarted += UpdateImportState; 
     importer.Execute(items); 
    } 

    private void UpdateImportState(string item) 
    { 
     using (var connection = new SqlConnection()){ /*status updates*/ } 
    } 
} 

現在一切工作正常。正在執行導入,並且隨着導入的繼續,用戶正在獲取狀態更新(從Status表中)。

發生該問題是因爲此類邏輯不安全。我必須確定,進口是一項原子操作。我不想部分下載和保存數據。我使用的交易方式爲這個(我包裹importer.ExecuteTransactionScope)的解決方案:

importer.ImportStarted += UpdateImportState; 
using (var scope = new TransactionScope()) 
{ 
    importer.Execute(items); 
    scope.Complete(); 
} 

現在我已經安全 - 回滾例如發生在進程中止的情況下。

我現在面臨着不同的問題 - 我想解決的問題。我需要狀態更新信息才能顯示,但Status表不受更新的影響,而事務尚未完成。即使我嘗試使用RequiresNew選項來創建單獨的事務(不是環境事務),也沒有任何更改。 Execute函數創建它自己的數據庫連接,並且UpdateImportState也一樣。連接不共享。我不知道爲什麼State表不會受到影響,即使TransactionScope僅涵蓋與Import表連接的邏輯。

如何保留一致的導入並允許定期狀態更新?

+0

這【答案】(http://stackoverflow.com/a/2886326/1402923)在TransactionScope的行爲可能是有用的。 – RobH

回答

0

使用TransactionScopeOption.Suppress在UpdateImportState而不是TransactionScopeOption.RequiresNew

+0

作用域必須保留在'importer.Execute()'的周圍,因爲它需要是原子工作單元。事實上,添加到'UpdateImportState'的新範圍的'Suppress'選項解決了這個問題。順便說一句:你知道爲什麼使用'RequiresNew'選項來防止外部邏輯影響數據庫嗎?我認爲它會產生新的交易並忘記其他地方發生了什麼(分離當前交易)。看起來外部邏輯現在必須用'Suppress'選項標記新的範圍。 – jwaliszko