我正在尝试将 async
/await
集成到我们的服务总线中。我基于此示例 http://blogs.msdn.com/b/pfxteam/archive/2012/01/20/10259049.aspx 实现了一个 SingleThreadSynchronizationContext
。
它工作正常,除了一件事:TransactionScope
。我等待 TransactionScope
中的内容,它破坏了 TransactionScope
。
TransactionScope
似乎不能与 async
/await
配合使用,这肯定是因为它使用 ThreadStaticAttribute
将内容存储在线程中。我得到这个例外:
“TransactionScope 嵌套不正确。”。
我试图在排队任务之前保存 TransactionScope
数据并在运行它之前恢复它,但它似乎并没有改变任何事情。而且 TransactionScope
代码是一团糟,所以很难理解那里发生了什么。
有没有办法让它工作? TransactionScope
有什么替代品吗?
TransactionScope
,您需要一个具有自己的 SingleThreadSynchronizationContext
的单独线程。
在 .NET Framework 4.5.1 中,有一组采用 TransactionScopeAsyncFlowOption
参数的 new constructors for TransactionScope
。
根据 MSDN,它支持跨线程延续的事务流。
我的理解是,它是为了让您可以编写如下代码:
// transaction scope
using (var scope = new TransactionScope(... ,
TransactionScopeAsyncFlowOption.Enabled))
{
// connection
using (var connection = new SqlConnection(_connectionString))
{
// open connection asynchronously
await connection.OpenAsync();
using (var command = connection.CreateCommand())
{
command.CommandText = ...;
// run command asynchronously
using (var dataReader = await command.ExecuteReaderAsync())
{
while (dataReader.Read())
{
...
}
}
}
}
scope.Complete();
}
回答有点晚了,但我在 MVC4 上遇到了同样的问题,我通过右键单击项目转到属性将我的项目从 4.5 更新到 4.5.1。选择应用程序选项卡将目标框架更改为 4.5.1 并使用事务,如下所示。
using (AccountServiceClient client = new AccountServiceClient())
using (TransactionScope scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
{
}
您可以使用由 Transaction.DependentClone() 方法创建的 DependentTransaction:
static void Main(string[] args)
{
// ...
for (int i = 0; i < 10; i++)
{
var dtx = Transaction.Current.DependentClone(
DependentCloneOption.BlockCommitUntilComplete);
tasks[i] = TestStuff(dtx);
}
//...
}
static async Task TestStuff(DependentTransaction dtx)
{
using (var ts = new TransactionScope(dtx))
{
// do transactional stuff
ts.Complete();
}
dtx.Complete();
}
Managing Concurrency with DependentTransaction
http://adamprescott.net/2012/10/04/transactionscope-in-multi-threaded-applications/
await Task.Delay(500)
之类的内容,则此模式也会因 TransactionScope nested incorrectly
而失败,因为最外面的 TransactionScope(上例中未显示)在子任务正确完成之前退出范围。将 await
替换为 Task.Wait()
即可,但您失去了 async
的好处。
如果有人仍然关注这个线程......
Microsoft.Data.SqlClient v3.0.1 看起来修复了 System.Transactions 在 .NET Framework 的 async/await 函数中的死锁问题!我用的是4.8。我使用显式分布式事务方法允许通过使用多个连接对单个 sql 服务器执行并行查询,但我确信 TransactionScope 的行为也更好。
创建一个 CommittableTransaction,创建任意数量的 SqlConnections,这些 SqlConnections 包含从父可提交表创建的 DependentTransaction,并行执行连接上的查询,在查询执行后完成依赖项,然后提交/回滚父可提交表。
我在 3 个不同的连接上使用 3 个并行异步插入到同一个数据库中对其进行了测试。当父可提交正在进行时,我使用了一个消息框来提示提交或回滚。当分布式事务处于活动状态时,表被锁定。我无法在 ssms 中选择它们。选择提交或回滚后,要么按预期工作。
上周使用 Microsoft.Data.SqlClient 3.0.0,这是不可能的,因为 .Commit() 方法会在异步函数中死锁。我什至用各种方法尝试过 BeginCommit/EndCommit。甚至在异步函数中招募依赖项时也会出现问题,但这也已修复。现在最简单的显式分布式事务方法适用于 async/await。