La risposta di Marc funziona, ma nel mio caso ha causato una notevole quantità di codice gonfio (ed è facile dimenticarsi di farlo in questo modo), così mi è venuta in mente un'astrazione che impone lo schema.
Ecco come lo si utilizza:
await db.TransactAsync(commands => commands
.Enqueue(tran => tran.SomeCommandAsync(...))
.Enqueue(tran => tran.SomeCommandAsync(...))
.Enqueue(tran => tran.SomeCommandAsync(...)));
Ecco l'implementazione:
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);
}
Un avvertimento: questo non fornisce un modo semplice per arrivare al risultato di uno dei comandi. Nel mio caso (e negli OP) va bene - utilizzo sempre le transazioni per una serie di scritture. Ho trovato che questo mi ha davvero aiutato a tagliare il mio codice, e solo esponendo lo (che richiede di restituire un'attività), io sono meno propenso a "dimenticare" che non dovrei essere await
in quei comandi al momento Li chiamo.
fonte
2018-02-15 18:47:54
Per cosa intendo non ancora disponibile: vedere "in coda" qui: http://redis.io/topics/transactions –