Odpowiedź Marka działa, ale w moim przypadku spowodowała przyzwoitą ilość kodu (i łatwo jest zapomnieć zrobić to w ten sposób), więc wymyśliłem abstrakcję, która wymusza ten schemat.
Oto jak go używać:
await db.TransactAsync(commands => commands
.Enqueue(tran => tran.SomeCommandAsync(...))
.Enqueue(tran => tran.SomeCommandAsync(...))
.Enqueue(tran => tran.SomeCommandAsync(...)));
oto realizacja:
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);
}
Jedno zastrzeżenie: nie zapewniają łatwy sposób uzyskać na wyniku którejkolwiek z polecenia. W moim przypadku (i OP) wszystko jest w porządku - zawsze używam transakcji dla serii zapisów. Zauważyłem, że to naprawdę pomogło w przycięciu kodu w dół i tylko wystawiając tran
wewnątrz Enqueue
(co wymaga, abyś zwrócił zadanie), mniej "zapomnę", że nie powinienem być await
tymi poleceniami w tym czasie Wzywam ich.
Co mam na myśli przez jeszcze nie dostępne: patrz "w kolejce" tutaj: http://redis.io/topics/transactions –