我在Parallel.Invoke()
内部有多个需要在事务内部运行的方法。这些方法都调用SqlBulkCopy
的实例。用例是" all-or-none",所以如果一个方法失败,则不会提交任何内容。当我在父事务上调用Complete()方法时,我得到TransactionAbortedException ({"Transaction Timeout"})
。
这是父交易:
using (var ts = new TransactionScope())
{
var saveClone = Transaction.Current.DependentClone(DependentCloneOption.BlockCommitUntilComplete);
var saveErrorsClone = Transaction.Current.DependentClone(DependentCloneOption.BlockCommitUntilComplete);
var saveADClone = Transaction.Current.DependentClone(DependentCloneOption.BlockCommitUntilComplete);
var saveEnrollmentsClone = Transaction.Current.DependentClone(DependentCloneOption.BlockCommitUntilComplete);
Parallel.Invoke(_options, () =>
{
Save(data, saveClone);
},
() =>
{
SaveErrors(saveErrorsClone);
},
() =>
{
SaveEnrollments(data, saveEnrollmentsClone);
});
ts.Complete();
}//***** GET THE EXCEPTION HERE *****
这是一个使用SqlBulkCopy
的依赖交易(它们都是相同的结构)。我传递了父母,并将其分配给孩子TransactionScope
private void Save(IDictionary<string, string> data, Transaction transaction)
{
var dTs = (DependentTransaction)transaction;
if (transaction.TransactionInformation.Status != TransactionStatus.Aborted)
{
using (var ts = new TransactionScope(dTs))
{
_walmartData.Save(data);
Debug.WriteLine("Completed Processing XML - {0}", _stopWatch.Elapsed);
ts.Complete();
}
}
else
{
Debug.WriteLine("Save Not Executed - Transaction Aborted - {0}", _stopWatch.Elapsed);
dTs.Complete();
}
dTs.Complete();
}
编辑(添加了我的SqlBulkCopy方法...... 通知事件参数的)
private void SqlBulkCopy(DataTable dt, SqlBulkCopyColumnMappingCollection mappings)
{
try
{
using (var sbc = new SqlBulkCopy(_conn, SqlBulkCopyOptions.TableLock, null))
{
sbc.BatchSize = 100;
sbc.BulkCopyTimeout = 0;
sbc.DestinationTableName = dt.TableName;
foreach (SqlBulkCopyColumnMapping mapping in mappings)
{
sbc.ColumnMappings.Add(mapping);
}
sbc.WriteToServer(dt);
}
}
catch (Exception)
{
throw;
}
}
除了修正错误外,我还可以选择替代方案。感谢。
答案 0 :(得分:2)
您可以选择DependentCloneOption.BlockCommitUntilComplete
创建一种死锁形式。
Parallel.Invoke
阻塞调用线程,直到所有处理完成。尝试由Parallel.Invoke
完成的作业在等待父事务完成时都会阻塞(由于DependentCloneOption)。所以这两个人正在等待......死锁。父事务最终会超时并从阻塞中释放依赖事务,这会阻止您的调用线程。
您可以使用DependentCloneOption.RollbackIfNotComplete
吗?
答案 1 :(得分:0)
http://msdn.microsoft.com/en-us/library/system.transactions.transactionscope.complete.aspx表示TransactionScope.Complete
只提交它包含的事务,如果它是创建事务的事务。由于您是从现有事务创建范围,我相信您需要在调用范围完成之前提交事务。
来自MSDN:
资源管理器之间的实际提交工作发生在 如果TransactionScope对象创建了,则结束使用语句 交易。如果它没有创建事务,则发生提交 每当Committable由CommittableTransaction的所有者调用时 宾语。此时,事务管理器调用该资源 管理员并通知他们提交或回滚,基于 是否在TransactionScope对象上调用此方法
答案 2 :(得分:0)
在经历了很多痛苦,研究和缺乏有效答案后,我不得不相信我在问题中描述的堆栈是不可能的。我相信,痛点在于TransactionScope和SqlBulkCopy之间。我把这个答案放在这里是为了未来观众的利益。如果有人能证明可以做到,我很乐意将其删除作为答案。
答案 3 :(得分:0)
我相信您创建_conn
- 实例的方式非常重要,如果您创建并在TransactionScope
- 实例中打开它,则任何SqlBulkCopy
相关问题都应该得到解决。
查看Can I use SqlBulkCopy inside Transaction和Is it possible to use System.Transactions.TransactionScope with SqlBulkCopy?,看看它是否对您有帮助。
void MyMainMethod()
{
using (var ts = new TransactionScope())
{
Parallell.InvokeOrWhatNotOrWhatEver(() => DoStuff());
}
}
void DoStuff()
{
using (var sqlCon = new SqlConnection(conStr))
{
sqlCon.Open(); // ensure to open it before SqlBulkCopy can open it in another transactionscope.
using (var bulk = new SqlBulkCopy(sqlCon))
{
// Do you stuff
bulk.WriteToServer...
}
ts.Complete(); // finish the transaction, ie commit
}
}
简而言之:
SqlBulkCopy
- 具有上面创建的conncection的实例transaction.Complete()