异步提交或回滚事务范围
Asynchronously commit or rollback a transaction scope
正如许多人所知,当 async
await
模式被引入 .Net 时,TransactionScope
被遗忘了。如果我们试图在事务范围内使用一些 await
调用,它们就会被破坏。
现在由于 scope constructor option.
解决了这个问题
但在我看来仍然缺少一块,至少我无法找到如何以简单的 "transaction scope like" 方式做到这一点:如何等待范围的提交或回滚?
提交和回滚也是IO操作,应该是可以等待的。但由于它们发生在范围处置上,我们必须等待处置。如果没有 IAsyncDisposable
由事务范围实现,这是不可行的,目前情况并非如此。
我也查看了 System.Transactions.Transaction
接口:那里也没有可等待的方法。
我明白提交和回滚几乎只是向数据库发送一个标志,所以应该很快。但是对于分布式事务,速度可能会慢一些。无论如何,这仍然是一些阻塞 IO。
关于分布式案例,请记住这可能会触发两阶段提交。在某些情况下,在第一阶段(准备)征用额外的持久资源。这通常意味着针对那些最近征用的资源发出了一些额外的查询。提交期间发生的所有事情。
那么有什么方法可以等待事务作用域吗?或者 System.Transactions.Transaction
代替?
注意: 我不认为这是“Is it possible to commit/rollback SqlTransaction in asynchronous?". SqlTransaction
are more limited than system transactions. They can address only SQL-Server and are never distributed. Some other transactions do have async methods, such as Npgsql. Now for having async methods on transaction scopes/system transaction, DbTransaction
可能需要具有异步方法的副本。(我不知道系统的内部结构交易,但它可能正在使用这个 ADO.NET 合同。我们将连接加入系统交易的方式确实让我认为它没有使用它。)
更新:DbTransaction
在 .Net Core 3.0 中确实有它们,请参阅 #35012 (notably thanks to Roji)。
也许是一个迟到的答案,但你想要的基本上归结为一种可以轻松自行创建的语法糖。
概括你的问题,我实现了一个 "async using" 语法,它允许正文和 "using" 的 "dispose" 部分都可以等待。外观如下:
async Task DoSomething()
{
await UsingAsync.Do(
// this is the disposable usually passed to using(...)
new TransactionScope(TransactionScopeAsyncFlowOption.Enabled),
// this is the body of the using() {...}
async transaction => {
await Task.Delay(100); // do any async stuff here...
transaction.Complete(); // mark transaction for Commit
} // <-- the "dispose" part is also awaitable
);
}
实现就这么简单:
public static class UsingAsync
{
public static async Task Do<TDisposable>(
TDisposable disposable,
Func<TDisposable, Task> body)
where TDisposable : IDisposable
{
try
{
await body(disposable);
}
finally
{
if (disposable != null)
{
await Task.Run(() => disposable.Dispose());
}
}
}
}
与常规 using
子句相比,错误处理有所不同。使用 UsingAsync.Do
时,正文或处置抛出的任何异常都将包含在 AggregateException
中。这在 body 和 dispose 都抛出异常时很有用,并且两个异常都可以在 AggregateException
中检查。使用常规 using
子句,只会捕获 dispose 抛出的异常,除非正文明确包含在 try..catch
中。
目前还没有办法实现。但是they work on it
正如许多人所知,当 async
await
模式被引入 .Net 时,TransactionScope
被遗忘了。如果我们试图在事务范围内使用一些 await
调用,它们就会被破坏。
现在由于 scope constructor option.
解决了这个问题但在我看来仍然缺少一块,至少我无法找到如何以简单的 "transaction scope like" 方式做到这一点:如何等待范围的提交或回滚?
提交和回滚也是IO操作,应该是可以等待的。但由于它们发生在范围处置上,我们必须等待处置。如果没有 IAsyncDisposable
由事务范围实现,这是不可行的,目前情况并非如此。
我也查看了 System.Transactions.Transaction
接口:那里也没有可等待的方法。
我明白提交和回滚几乎只是向数据库发送一个标志,所以应该很快。但是对于分布式事务,速度可能会慢一些。无论如何,这仍然是一些阻塞 IO。
关于分布式案例,请记住这可能会触发两阶段提交。在某些情况下,在第一阶段(准备)征用额外的持久资源。这通常意味着针对那些最近征用的资源发出了一些额外的查询。提交期间发生的所有事情。
那么有什么方法可以等待事务作用域吗?或者 System.Transactions.Transaction
代替?
注意: 我不认为这是“Is it possible to commit/rollback SqlTransaction in asynchronous?". SqlTransaction
are more limited than system transactions. They can address only SQL-Server and are never distributed. Some other transactions do have async methods, such as Npgsql. Now for having async methods on transaction scopes/system transaction, DbTransaction
可能需要具有异步方法的副本。(我不知道系统的内部结构交易,但它可能正在使用这个 ADO.NET 合同。我们将连接加入系统交易的方式确实让我认为它没有使用它。)
更新:DbTransaction
在 .Net Core 3.0 中确实有它们,请参阅 #35012 (notably thanks to Roji)。
也许是一个迟到的答案,但你想要的基本上归结为一种可以轻松自行创建的语法糖。
概括你的问题,我实现了一个 "async using" 语法,它允许正文和 "using" 的 "dispose" 部分都可以等待。外观如下:
async Task DoSomething()
{
await UsingAsync.Do(
// this is the disposable usually passed to using(...)
new TransactionScope(TransactionScopeAsyncFlowOption.Enabled),
// this is the body of the using() {...}
async transaction => {
await Task.Delay(100); // do any async stuff here...
transaction.Complete(); // mark transaction for Commit
} // <-- the "dispose" part is also awaitable
);
}
实现就这么简单:
public static class UsingAsync
{
public static async Task Do<TDisposable>(
TDisposable disposable,
Func<TDisposable, Task> body)
where TDisposable : IDisposable
{
try
{
await body(disposable);
}
finally
{
if (disposable != null)
{
await Task.Run(() => disposable.Dispose());
}
}
}
}
与常规 using
子句相比,错误处理有所不同。使用 UsingAsync.Do
时,正文或处置抛出的任何异常都将包含在 AggregateException
中。这在 body 和 dispose 都抛出异常时很有用,并且两个异常都可以在 AggregateException
中检查。使用常规 using
子句,只会捕获 dispose 抛出的异常,除非正文明确包含在 try..catch
中。
目前还没有办法实现。但是they work on it