2013-01-12 80 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