Я настроил свой контекст db для повторной попытки при сбое следующим образом:
optionsBuilder.UseSqlServer(connectionString, sqlServerOptionsAction: sqlOptions => {
sqlOptions.EnableRetryOnFailure(
maxRetryCount: 10,
maxRetryDelay: TimeSpan.FromSeconds(30),
errorNumbersToAdd: null);
});
Также я настроил следующий перехватчик:
public class DbCommandFailureInterceptor : DbCommandInterceptor
{
private int retryCount = 2;
public override async Task<InterceptionResult<int>> NonQueryExecutingAsync(DbCommand command, CommandEventData eventData, InterceptionResult<int> result, CancellationToken cancellationToken = default)
{
Throw(command);
return result;
}
public override async Task<InterceptionResult<DbDataReader>> ReaderExecutingAsync(DbCommand command, CommandEventData eventData, InterceptionResult<DbDataReader> result, CancellationToken cancellationToken = default)
{
Throw(command);
return result;
}
private void Throw(DbCommand command)
{
if (!(command.CommandText.Contains("serverproperty")
|| command.CommandText.Contains("_MigrationHistory"))
&& retryCount > 0) {
--retryCount;
throw SqlExceptionFaker.Error10053;
}
}
}
Так что в основном я ожидаю, когда сущности из базы данных, чтобы поразить метод Throw 3 раза, 2 раза с исключением исключения и последний раз без его выброса. Но на самом деле повторная попытка при сбое не выполняется, а выполнение первого исключения останавливается. Что я делаю не так?
У меня есть такой помощник для отказоустойчивых транзакций:
public class ResilientTransaction
{
private readonly DbContext _context;
private ResilientTransaction(DbContext context) =>
_context = context ?? throw new ArgumentNullException(nameof(context));
public async Task ExecuteAsync(Func<Task> action)
{
var strategy = _context.Database.CreateExecutionStrategy();
await strategy.ExecuteAsync(async () => {
using (var transaction = await _context.Database.BeginTransactionAsync()) {
await action();
await transaction.CommitAsync();
}
});
}
public static ResilientTransaction New(DbContext context) =>
new ResilientTransaction(context);
}
А вот как я его выполняю:
await ResilientTransaction.New(_dbContext).ExecuteAsync(async () => {
_dbContext.Tenants.Add(new Tenant());
await _dbContext.SaveChangesAsync();
});