Handling DbUpdateConcurrencyException with SaveChangesInterceptors

190 views Asked by At

Does anyone have an idea if it is possible to implement the retry pattern with SaveChangesInterceptor interceptor?

Here is the SavingChangesAsync method.

  public override async ValueTask<InterceptionResult<int>> SavingChangesAsync(DbContextEventData eventData, InterceptionResult<int> result,
        CancellationToken cancellationToken = new CancellationToken())
    {
        int tryCount = 0;
        while (tryCount < _maxRetries)
        {
            try
            {
                return await new ValueTask<InterceptionResult<int>>(result); //it doesn't cause an exception.
            }
            catch (DbUpdateConcurrencyException ex)
            {
                if (ex.Entries.All(e => e.Entity.GetType() != typeof(TEntity)))
                {
                    throw;
                }

                Console.WriteLine(tryCount);

                foreach (var entry in ex.Entries)
                {
                    if (entry is TEntity)
                    {
                        await entry.ReloadAsync(cancellationToken);
                    }
                }

                await Task.Delay(_delay, cancellationToken);
            }
            catch (DbUpdateException ex)
            {
                Console.WriteLine("Testing");
            }

            tryCount += 1;
        }

        throw new DbUpdateConcurrencyException($"The maximum number of retries {_maxRetries} has been exceeded.");
    }

also I implemented the SaveChangesFailedAsync method.

        public override async Task SaveChangesFailedAsync(DbContextErrorEventData eventData,
        CancellationToken cancellationToken = new CancellationToken())
    {

        if (eventData.Exception is DbUpdateConcurrencyException myException)
        {
            await Task.Delay(_delay, cancellationToken);
            foreach (var entry in myException.Entries)
            {
                await entry.ReloadAsync(cancellationToken);
            }

            try
            {
                await eventData.Context.SaveChangesAsync(cancellationToken);
                InterceptionResult.Suppress();
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
                throw;
            }

        }

    }

BTW, I know there is a usual way to handle that. inherit from DBContext and override savechanges, and I am using EF Core 5.0.

0

There are 0 answers