Marcs Antwort funktioniert, aber in meinem Fall verursachte es eine anständige Menge an Code Bloat (und es ist leicht zu vergessen, es auf diese Weise zu tun), also kam ich mit einer Abstraktion, die das Muster erzwingt.
Hier ist, wie Sie es verwenden:
await db.TransactAsync(commands => commands
.Enqueue(tran => tran.SomeCommandAsync(...))
.Enqueue(tran => tran.SomeCommandAsync(...))
.Enqueue(tran => tran.SomeCommandAsync(...)));
Hier ist die Umsetzung:
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);
}
Eine Einschränkung: Dies ist nicht eine einfache Möglichkeit bieten am Ergebnis irgendeines der bekommen Befehle. In meinem Fall (und den OPs) ist das in Ordnung - ich verwende immer Transaktionen für eine Reihe von Schreibvorgängen. Ich fand, dass dies wirklich geholfen hat, meinen Code zu reduzieren, und indem ich nur tran
innerhalb Enqueue
exponiert (was erfordert, dass Sie eine Aufgabe zurückgeben), bin ich weniger wahrscheinlich zu "vergessen", dass ich nicht diese Befehle zu der Zeit sein sollte Ich rufe Sie an.
Für was ich meine noch nicht verfügbar: siehe "in Warteschlange" hier: http://redis.io/topics/transactions –