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,
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