Marc 的答案有效,但在我的情况下,它导致了大量的代码膨胀(而且很容易忘记这样做),所以我想出了一种强制执行模式的抽象。
以下是你如何使用它:
await db.TransactAsync(commands => commands
.Enqueue(tran => tran.SomeCommandAsync(...))
.Enqueue(tran => tran.SomeCommandAsync(...))
.Enqueue(tran => tran.SomeCommandAsync(...)));
这是实现:
public static class RedisExtensions
{
public static async Task TransactAsync(this IDatabase db, Action<RedisCommandQueue> addCommands)
{
var tran = db.CreateTransaction();
var q = new RedisCommandQueue(tran);
addCommands(q);
if (await tran.ExecuteAsync())
await q.CompleteAsync();
}
}
public class RedisCommandQueue
{
private readonly ITransaction _tran;
private readonly IList<Task> _tasks = new List<Task>();
public RedisCommandQueue Enqueue(Func<ITransaction, Task> cmd)
{
_tasks.Add(cmd(_tran));
return this;
}
internal RedisCommandQueue(ITransaction tran) => _tran = tran;
internal Task CompleteAsync() => Task.WhenAll(_tasks);
}
一个警告:这并没有提供一种简单的方法来获取任何命令的结果。在我的情况下(和 OP)没关系 - 我总是使用事务进行一系列写入。我发现这确实有助于精简我的代码,并且只暴露tran
内部Enqueue
(这需要你返回一个任务),我不太可能“忘记”await
在我调用这些命令时我不应该使用这些命令。