为什么EF Core的失败重试无法正常工作?

4
我已经配置了我的数据库上下文以在失败时进行重试,如下所示:
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次,前两次会抛出异常,最后一次则不会抛出。 但事实上,在第一次发生异常后,执行将停止,没有重试失败的操作。 我做错了什么?
我有一个用于弹性事务的帮助程序:
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();
                });

我猜如果retryCount == 0应该抛出异常,否则throw将不会被处理。 - Berkay Yaylacı
@jdweng 确实我遇到了异常,而且出于某种原因没有保存任何数据到数据库中,这是最令人沮丧的部分,问题就在于 - 为什么会失败? - Grigoryants Artem
@jdweng 你能更具体地指出我应该关注哪些日志文件吗? - Grigoryants Artem
你正在使用SQL Server(UseSqlServer)吗?服务器有一个LDF文件(MDF是数据库),这些是日志文件。使用SQL Server Management Studio,您可以查看日志。 - jdweng
刚刚检查了一下,似乎在代码执行期间没有生成任何日志。 - Grigoryants Artem
显示剩余2条评论
1个回答

3
简短回答:这是因为重试策略的特定实现仅针对特定异常(如SQL超时)进行重试。
如果您想让重试在您的测试中起作用,您需要抛出这些特定异常(这可能不是一件容易的事情,需要使用一些技巧),或者更好的方法是编写自己的自定义重试策略并将其用于db context,但仅用于测试!

这并没有回答问题。一旦您拥有足够的声望,您将能够评论任何帖子;相反,提供不需要询问者澄清的答案。- 来自审核 - rikitikitik
这是一个答案,实际上,可能是基于有限细节的答案。可惜 Maks 没有停留,但是他至少知道规则。 - darbid

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接