MySQL EntityFramework Error with Pomelo.EntityFrameworkCore.MySql

122 Views Asked by At

I'm getting a random System.NullReferenceException when I dispose of the IDbContextTransaction.

This is my DbContext Class

public class SampleContext : DbContext, ISampleContext 
{
    private readonly AuditableEntitySaveChangesInterceptor _auditableEntitySaveChangesInterceptor;
    private IDbContextTransaction dbContextTransaction;
    public SampleContext(DbContextOptions<SampleContext > options,
            AuditableEntitySaveChangesInterceptor auditableEntitySaveChangesInterceptor
            ) : base(options)
    {
        this._auditableEntitySaveChangesInterceptor = auditableEntitySaveChangesInterceptor;
    }

    public DbSet<Accommodation> Accommodations => Set<Accommodation>();

    public DbSet<AccommodationLevel> AccommodationLevels => Set<AccommodationLevel>();

    public DbSet<City> Cities => Set<City>();

    public DbSet<CityPopulationType> CityPopulationTypes => Set<CityPopulationType>();

    public DbSet<Contact> Contacts => Set<Contact>();


    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.ApplyConfigurationsFromAssembly(Assembly.GetExecutingAssembly());
        base.OnModelCreating(modelBuilder);
    }
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.LogTo(message => Debug.WriteLine(message))
            .EnableSensitiveDataLogging();
        optionsBuilder.UseLazyLoadingProxies();
        optionsBuilder.AddInterceptors(_auditableEntitySaveChangesInterceptor);
    }
    public override Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
    {
        return base.SaveChangesAsync(cancellationToken);
    }

    public async Task BeginTransactionAsync(CancellationToken cancellationToken)
    {
        dbContextTransaction ??= await Database.BeginTransactionAsync(cancellationToken);
    }

    public async Task CommitTransactionAsync(CancellationToken cancellationToken)
    {
        try
        {
            await SaveChangesAsync(cancellationToken);
            dbContextTransaction?.CommitAsync(cancellationToken);
        }
        catch
        {
            await RollbackTransactionAsync(cancellationToken);
            throw;
        }
        finally
        {
            if (dbContextTransaction != null)
            {
                DisposeTransaction();
            }
        }
    }

    public async Task RetryOnExceptionAsync(Func<Task> func)
    {
        await Database.CreateExecutionStrategy().ExecuteAsync(func);
    }

    public async Task RollbackTransactionAsync(CancellationToken cancellationToken)
    {
        try
        {
            await dbContextTransaction?.RollbackAsync(cancellationToken);
        }
        finally
        {
            DisposeTransaction();
        }
    }

    private void DisposeTransaction()
    {
        try
        {
            if (dbContextTransaction != null)
            {
                dbContextTransaction.Dispose();
            }
        }
        catch (Exception ex)
        {

        }
    }
}

This is my TransactionBehavior Class which is implemented using MediatR

public class TransactionBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse> where TRequest : MediatR.IRequest<TResponse>
{
    private readonly ILogger<TransactionBehavior<TRequest, TResponse>> _logger;
    private readonly ISampleContext _sampleContext ;

    public TransactionBehavior(ILogger<TransactionBehavior<TRequest, TResponse>> logger, ISampleContext sampleContext )
    {
        _logger = logger;
        _sampleContext = sampleContext ;
    }
    public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next, CancellationToken cancellationToken)
    {
        TResponse response = default;

        try
        {
            await _AATQuickQuoteContext.RetryOnExceptionAsync(async () =>
            {
                _logger.LogInformation($"Begin Transaction : {typeof(TRequest).Name}");
                await _sampleContext .BeginTransactionAsync(cancellationToken);

                response = await next();

                await _sampleContext.CommitTransactionAsync(cancellationToken);
                _logger.LogInformation($"End transaction : {typeof(TRequest).Name}");
            });
        }
        catch (Exception ex)
        {
            _logger.LogInformation($"Rollback transaction executed {typeof(TRequest).Name}");
            await _sampleContext.RollbackTransactionAsync(cancellationToken);
            _logger.LogError(ex.Message, ex.StackTrace);

            throw;
        }

        return response;
    }
}

When I run the code I get the following exception randomly. Did I miss anything on my code? Can someone help me,enter image description here please?

This is my call stack

at MySqlConnector.MySqlTransaction.d__27.MoveNext()
at MySqlConnector.MySqlTransaction.d__25.MoveNext()
at MySqlConnector.MySqlTransaction.Dispose(Boolean disposing)
at Microsoft.EntityFrameworkCore.Storage.RelationalTransaction.Dispose()
at Sample.Infrastructure.Data.SampleContext.DisposeTransaction() in D:\Projects\Sample\src\Infrastructure\Sample.Infrastructure\Data\SampleContext.cs:line
0

There are 0 best solutions below