Per velocità a volte restituiamo la risposta al consumatore prima che lo stato venga salvato nel DB. A volte (principalmente per i nostri utenti automatizzati) questo può rompersi perché si desidera eseguire azioni sui dati salvati prima che vengano salvati. Ho scritto questo piccolo aiuto
public async Task<TEntity> GetWithRetry<TEntity>(Expression<Func<TEntity, bool>> predicate, string errorOnTimeout) where TEntity : class
{
var stopwatch = new Stopwatch();
stopwatch.Start();
do
{
var entity = await _context.DbSet<TEntity>().FirstOrDefaultAsync(predicate);
if(entity == null)
await Task.Delay(100);
else
return entity;
} while(stopwatch.Elapsed < TimeSpan.FromSeconds(1000));
throw new Exception(errorOnTimeout);
}
Usato come
var existingBooking = await GetWithRetry<Booking>(b => b.BookingKey == confirmCommand.BookingKey, "Booking key not found");
Qualche tranello? Task.Delay
dovrebbe scalare bene dal momento che restituisce il thread al pool, e se i dati esistono nel DB la prima volta non dovrebbe dare un sovraccarico maggiore rispetto a un'attività extra wrapping?
Questa è la versione corrente del codice:
public async Task<TEntity> FirstAsync<TEntity>(Expression<Func<TEntity, bool>> predicate, string errorOnTimeout, TimeSpan? timeout = null) where TEntity : class
{
var entity = await FirstAsyncOrDefault(predicate, timeout);
if(entity != null) return entity;
throw new Exception(errorOnTimeout);
}
public async Task<TEntity> FirstOrDefaultAsync<TEntity>(Expression<Func<TEntity, bool>> predicate, TimeSpan? timeout = null) where TEntity : class
{
var stopwatch = new Stopwatch();
stopwatch.Start();
if(timeout == null)
timeout = TimeSpan.FromSeconds(1);
do
{
var entity = await DbSet<TEntity>().FirstOrDefaultAsync(predicate);
if(entity != null) return entity;
await Task.Delay(100);
} while(stopwatch.Elapsed < timeout);
return null;
}