Got threads issue on EF Core 8.0, .NET 8, I got this issue how to overcome this?
Error:
A second operation was started on this context instance before a previous operation completed. This is usually caused by different threads concurrently using the same instance of DbContext.
internal sealed class CommandHandler : IRequestHandler<Command, string>
{
private readonly ApplicationDbContext _dbContext;
private readonly IValidator<Command> _validator;
private readonly IWebHostEnvironment _host;
public CommandHandler(ApplicationDbContext dbContext, IValidator<Command> validator, IWebHostEnvironment host)
{
_dbContext = dbContext;
_validator = validator;
_host = host;
}
public async Task<string> Handle(Command request, CancellationToken cancellationToken)
{
var validationResult = await _validator.ValidateAsync(request);
if (!validationResult.IsValid)
{
throw new ValidationException(validationResult.Errors);
}
using (var transaction = _dbContext.Database.BeginTransaction())
{
var multiplePayment = request.PaymentRequest;
var _paymentImageId = new List<int?>();
var _paymentId = 0;
var _imageName = new List<string?>();
try
{
foreach (var item in multiplePayment)
{
var getUserId = _dbContext.Auths
.FirstOrDefaultAsync(x => x.UserId == item.CreateBy, cancellationToken);
var generatedId = "Pay-" + GenerateId.MakeId();
#region image upload
if (item.ListOfImageBase64 is null || item.ListOfImageBase64.Count == 0)
{
foreach (var imageBase64 in item.ListOfImageBase64)
{
var createdBy = await _dbContext.Auths
.FirstOrDefaultAsync(x => x.UserId == item.CreateBy, cancellationToken);
string image = await Base64ToImage.UploadImageAsync(imageBase64, "Payments", createdBy.Name, WebRootUtility.GetWwwRootPath(_host));
var img = new Image()
{
ImageId = GenerateId.MakeId(),
GeneratedId = generatedId,
ImageName = Path.GetFileName(image),
ImageUrl = image,
ImageSize = new FileInfo(image).Length.ToString(),
ImageType = ".png",
CreatedTime = DateTime.Now
};
_dbContext.Images.Add(img);
await _dbContext.SaveChangesAsync();
_imageName.Add(img.ImageName);
var paymentImage = new PaymentImage
{
ImageId = img.Id,
CreatedTime = DateTime.Now
};
_dbContext.PaymentImages.Add(paymentImage);
await _dbContext.SaveChangesAsync();
_paymentImageId.Add(paymentImage.Id);
}
}
#endregion image upload
#region Payment
var checkCreateBy = await _dbContext.Auths
.FirstOrDefaultAsync(x => x.UserId == item.CreateBy, cancellationToken);
var payment = new Payment
{
PaymentId = generatedId,
CreateBy = item.CreateBy,
CreateByName = checkCreateBy.Name,
Source = item.Source,
Amount = item.Amount,
Method = item.Method,
DepositOption = item.DepositOption,
Date = DateTime.Now,
ChequeNoOrTrackingNo = item.ChequeNoOrTrackingNo,
Branch = item.Branch,
RoutingNo = item.RoutingNo,
Status = "Pending",
AuthId = checkCreateBy.Id,
CreatedTime = DateTime.Now,
};
_dbContext.Payments.Add(payment);
await _dbContext.SaveChangesAsync();
_paymentId = payment.Id;
#endregion Payment
#region OrderProcessList
foreach (var orderId in item.OrderIds)
{
var orderProcess = await _dbContext.OrderProcesses
.FirstOrDefaultAsync(x => x.OrderProcessId == orderId, cancellationToken);
var orderProcessList = new OrderProcessList
{
OrderProcessGeneratedId = orderProcess.OrderProcessId,
OrderProcessId = orderProcess.Id,
PaymentId = _paymentId,
CreatedTime = DateTime.Now
};
_dbContext.OrderProcessLists.Add(orderProcessList);
await _dbContext.SaveChangesAsync();
}
#endregion OrderProcessList
#region OrderProcess
foreach (var orderId in item.OrderIds)
{
var orderProcess = await _dbContext.OrderProcesses
.FirstOrDefaultAsync(x => x.OrderProcessId == orderId, cancellationToken);
var paymentAmount = await _dbContext.Payments
.Where(x => x.Id == _paymentId)
.OrderByDescending(x => x.Id)
.Select(x => x.Amount)
.FirstOrDefaultAsync(cancellationToken);
var totalAmount = decimal.Parse(orderProcess.TotalAmount) + decimal.Parse(paymentAmount);
var isPartial = decimal.Parse(orderProcess.TotalAmount) >= totalAmount ? false : true;
if (isPartial)
{
orderProcess.IsFullPaid = true;
orderProcess.IsFullPaidDate = DateTime.Now;
orderProcess.IsPartial = false;
}
else
{
orderProcess.IsPartial = true;
}
orderProcess.UpdatedTime = DateTime.Now;
_dbContext.OrderProcesses.Update(orderProcess);
await _dbContext.SaveChangesAsync();
}
#endregion OrderProcess
#region PaymentImage
var paymentImageIds = _paymentImageId;
foreach (var items in paymentImageIds)
{
var paymentImageUpdate = await _dbContext.PaymentImages
.FirstOrDefaultAsync(x => x.Id == items, cancellationToken);
paymentImageUpdate.PaymentId = _paymentId;
paymentImageUpdate.UpdatedTime = DateTime.Now;
_dbContext.PaymentImages.Update(paymentImageUpdate);
await _dbContext.SaveChangesAsync();
}
#endregion PaymentImage
}
transaction.Commit();
return "Payment created successfully.";
}
catch (ValidationException ex)
{
transaction.Rollback();
//foreach (var imageName in _imageName)
//{
// string folderName = "Payments";
// string folderPath = Path.Combine(_host.WebRootPath, "Images", folderName, createBy.Name.Replace(" ", string.Empty), imageName);
// if (Directory.Exists(folderPath))
// {
// Directory.Delete(folderPath, true);
// }
//}
throw new ValidationException(ex.Errors);
}
catch (Exception ex)
{
transaction.Rollback();
//foreach (var imageName in _imageName)
//{
// string folderName = "Payments";
// string folderPath = Path.Combine(_host.WebRootPath, "Images", folderName, createBy.Name.Replace(" ", string.Empty), imageName);
// if (Directory.Exists(folderPath))
// {
// Directory.Delete(folderPath, true);
// }
//}
throw new Exception(ex.Message);
}
}
}
}
How to resolve this issue?
To summarize: When having this kind of exception with your DbContext:
A second operation was started on this context instance before a previous operation completed. This is usually caused by different threads concurrently using the same instance of DbContext.
Most probably you forgot to await
your async
methods.