Can someone please help me understand if there would be any issue regarding this transactions extension methods and its implementation? I am not sure if this could cause any issue, on locking the Db, or also if the current transactinos will be set or not.
Te method Transaction is working fine, but i wonder if it could cause any side effects.
I would need some help to address any possible issue, aappreciate it XD
extension class:
public static class TransactionExtensions
{
public static async Task<IDbContextTransaction> BeginTransactionAsync(this DbContext context)
{
if (context == null)
throw new ArgumentNullException(nameof(context));
if (context.Database.CurrentTransaction != null)
throw new InvalidOperationException("An active transaction already exists.");
return await context.Database.BeginTransactionAsync();
}
public static async Task CommitTransactionAsync(this DbContext context, IDbContextTransaction transaction)
{
if (context == null)
throw new ArgumentNullException(nameof(context));
if (transaction == null)
throw new ArgumentNullException(nameof(transaction));
if (transaction != context.Database.CurrentTransaction)
throw new InvalidOperationException($"Transaction {transaction.TransactionId} is not the current transaction.");
try
{
await context.SaveChangesAsync();
await transaction.CommitAsync();
}
catch
{
await RollbackTransactionAsync(context, transaction);
throw;
}
}
public static async Task<T> Transaction<T>(this DbContext context, Func<Func<Task>, Task<T>> action)
{
using (var transaction = await context.BeginTransactionAsync())
{
try
{
var result = await action(() => context.CommitTransactionAsync(transaction));
return result;
}
catch (Exception)
{
await RollbackTransactionAsync(context, transaction);
throw;
}
}
}
private static async Task RollbackTransactionAsync(DbContext context, IDbContextTransaction transaction)
{
try
{
await transaction.RollbackAsync();
}
finally
{
await transaction.DisposeAsync();
}
}
}
Usage:
private async Task chargeSearch(JobOfferCriteria criteria, int fetchedRecords)
{
var freeSearches = await GetFreeSearches();
var charge = new OfferSearchCharge(_auth.User, fetchedRecords - (freeSearches?.Total ?? 0));
if ((criteria.Credits ?? 0) < charge.Price) throw new Exception("Credits not enough");
await _context.Transaction(async (commit) =>
{
var added = _context.Add(charge);
await _context.SaveChangesAsync();
charge = added.Entity;
var credits = await _wallet.Api.ConsumeCredits(new(_auth.UserId, charge.Price));
try
{
charge.CreditsOnHold = credits;
charge.Confirm();
await commit();
}
catch
{
_wallet.Broker.RevertCreditsConsumption(new(_auth.UserId, charge.Price));
throw;
}
return charge;
});
await updateAnalytics(charge);
}