gpt4 book ai didi

asp.net - EF Core : A second operation started on this context before a previous operation completed 中的错误

转载 作者:行者123 更新时间:2023-12-04 21:56:44 25 4
gpt4 key购买 nike

我的 web api 应用程序出现奇怪的错误:
在前一个操作完成之前,在此上下文上开始了第二个操作。不保证任何实例成员都是线程安全的。

我有一个流程来计算用户的发薪日并更新其用户休假余额。所以我需要迭代用户并获得其休假余额,然后对每个余额进行更新。我还不知道如何解决这个错误。
当我调用 this.SaveAll() 时触发的错误是 containsb

    public async Task<bool> SaveAll()
{
return await _context.SaveChangesAsync() > 0;
}

datacontext 也被注入(inject)此代码:
private readonly DataContext _context;
private readonly IAdminSettingsRepository _settingRepo;
private readonly IAppRepository _appRepository;
public PayrollRepository(DataContext context, IAdminSettingsRepository settingRepo, IAppRepository appRepository)
{
_context = context;
_settingRepo = settingRepo;
_appRepository = appRepository;
}

过程如下:
public async Task<bool> ProcessPayCalendar(PayCalendar payCalendar)
{
List<User> users = payCalendar.Users.ToList();
SickLeaveEntitlement sickLeaveEntitlement = await _settingRepo.GetSickLeaveEntitlement();
AnnualLeaveEntitlement annualLeaveEntitlement = await _settingRepo.GetAnnualLeaveEntitlement();
LongServiceLeaveEntitlement longServiceLeaveEntitlement = await _settingRepo.GetLongServiceLeaveEntitlement();
DateTime tenYearsAgo = DateTime.Today.AddYears(-10);
DateTime currentMonth = new DateTime(payCalendar.NextPaymentDate.Year, payCalendar.NextPaymentDate.Month, 1);
DateTime previousMonth = currentMonth.AddMonths(-1);


/// begin calculation for leave calendar
users.ForEach(async user =>
{
decimal hoursWorked = this.CalculateWorkingHours(user);
decimal hourlyRate = this.CalculateHourlyRate(user);
// create pay day for user
Payday payday = new Payday();
payday.UserId = user.Id;
payday.PayPeriodStart = payCalendar.PayPeriodStartDate;
payday.PayPeriodEnd = payCalendar.PayPeriodEndDate;
payday.PaymentDate = payCalendar.NextPaymentDate;
payday.HoursWorked = hoursWorked;
payday.SickLeaveAccrual = this.CalculateSickLeaveEntitlement(hoursWorked, sickLeaveEntitlement);
payday.AnnualLeaveAccrual = this.CalculateAnnualLeaveEntitlement(hoursWorked, annualLeaveEntitlement);
payday.LongServiceLeaveAccrual = (user.StartDateCurrentAnnualSalary > tenYearsAgo) ? 0m : this.CalculateLongServiceEntitlement(hoursWorked, longServiceLeaveEntitlement);
payday.SickLeaveAccrualValue = payday.SickLeaveAccrual * hourlyRate;
payday.AnnualLeaveAccrualValue = payday.AnnualLeaveAccrual * hourlyRate;
payday.LongServiceLeaveAccrualValue = payday.LongServiceLeaveAccrual * hourlyRate;

this.Add(payday);

// do calculation on leave balance
// jika leave balance di awal bulan maka lakukan replikasi dr bulan sebelumnya.
// closebalance menjadi opening balance bulan selanjutnya.
LeaveBalance sickLeaveBalance = await this.GetUserLeaveBalance(user.Id, sickLeaveEntitlement, "sickLeave");
if (sickLeaveBalance == null)
{
// sickLeaveBalance = await this.CreateLeaveBalance(user.Id,sickLeaveEntitlement,"sickLeave",payday.PaymentDate);
// sickLeaveBalance.CurrentBalance =+ payday.SickLeaveAccrual ;
// sickLeaveBalance.CurrentBalanceValue += payday.SickLeaveAccrualValue;
throw new Exception($"Sick Leave balance for user: {user.Username} is not found. Please report this as bug");
}
else
{
sickLeaveBalance.CurrentBalance = +payday.SickLeaveAccrual;
sickLeaveBalance.CurrentBalanceValue += payday.SickLeaveAccrualValue;
sickLeaveBalance.LastUpdate = payday.PaymentDate;
}

LeaveBalance annualLeaveBalance = await this.GetUserLeaveBalance(user.Id, annualLeaveEntitlement, "annualLeave");
if (annualLeaveBalance == null)
{
throw new Exception($"Annual Leave balance for user: {user.Username} is not found. Please report this as bug");
}
else
{
annualLeaveBalance.CurrentBalance = +payday.AnnualLeaveAccrual;
annualLeaveBalance.CurrentBalanceValue += payday.AnnualLeaveAccrualValue;
annualLeaveBalance.LastUpdate = payday.PaymentDate;

}
LeaveBalance longServiceLeaveBalance = await this.GetUserLeaveBalance(user.Id, longServiceLeaveEntitlement, "longServiceLeave");
if (longServiceLeaveBalance == null)
{
throw new Exception($"Long Service Leave balance for user: {user.Username} is not found. Please report this as bug");
}
else
{
longServiceLeaveBalance.CurrentBalance = +payday.LongServiceLeaveAccrual;
longServiceLeaveBalance.CurrentBalanceValue += payday.LongServiceLeaveAccrualValue;
longServiceLeaveBalance.LastUpdate = payday.PaymentDate;

}
});
return await this.SaveAll();
}

完整的错误:

fail: Microsoft.EntityFrameworkCore.Update[10000] An exception occurred in the database while saving changes for context type 'CRSApp.API.Data.DataContext'. System.InvalidOperationException: A second operation started on this context before a previous operation completed. Any instance members are not guaranteed to be thread safe. at Microsoft.EntityFrameworkCore.Internal.ConcurrencyDetector.EnterCriticalSection() at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChangesAsync(IReadOnlyList1 entriesToSave, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChangesAsync(Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.DbContext.SaveChangesAsync(Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken)
System.InvalidOperationException: A second operation started on this context before a previous operation completed. Any instance members are not guaranteed to be thread safe.
at Microsoft.EntityFrameworkCore.Internal.ConcurrencyDetector.EnterCriticalSection()
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChangesAsync(IReadOnlyList
1 entriesToSave, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChangesAsync(Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.DbContext.SaveChangesAsync(Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken) fail: Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware[1] An unhandled exception has occurred while executing the request. System.InvalidOperationException: A second operation started on this context before a previous operation completed. Any instance members are not guaranteed to be thread safe. at Microsoft.EntityFrameworkCore.Internal.ConcurrencyDetector.EnterCriticalSection() at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChangesAsync(IReadOnlyList1 entriesToSave, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChangesAsync(Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.DbContext.SaveChangesAsync(Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken)
at CRSApp.API.Data.PayrollRepository.SaveAll() in E:\CRSApp\crsapp.api\Data\PayrollRepository.cs:line 35
at CRSApp.API.Data.PayrollRepository.ProcessPayCalendar(PayCalendar payCalendar) in E:\CRSApp\crsapp.api\Data\PayrollRepository.cs:line 229
at CRSApp.API.Controllers.Admin.PayrollController.ProcessPayCalendar(PayCalendarParam param) in E:\CRSApp\crsapp.api\Controllers\Admin\PayrollController.cs:line 68
at Microsoft.AspNetCore.Mvc.Internal.ActionMethodExecutor.TaskOfIActionResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
at System.Threading.Tasks.ValueTask
1.get_Result() at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.InvokeActionMethodAsync() at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.InvokeNextActionFilterAsync() at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Rethrow(ActionExecutedContext context) at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted) at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.InvokeInnerFilterAsync() at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeNextResourceFilter() at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Rethrow(ResourceExecutedContext context) at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted) at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeFilterPipelineAsync() at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeAsync() at Microsoft.AspNetCore.Builder.RouterMiddleware.Invoke(HttpContext httpContext) at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context) at Microsoft.AspNetCore.Cors.Infrastructure.CorsMiddleware.Invoke(HttpContext context) at Microsoft.AspNetCore.StaticFiles.StaticFileMiddleware.Invoke(HttpContext context) at Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware.Invoke(HttpContext context)



仅供引用:我使用 Dotnet Core 2.1.1,如下所述。
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.App" Version="2.1.1"/>
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="2.1.0" PrivateAssets="All"/>
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="4.0.1"/>
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="2.1.1"/>
<PackageReference Include="MailKit" Version="2.0.6"/>
</ItemGroup>

最佳答案

DbContext不是线程安全的

我认为这是 ForEach是你的问题。

 users.ForEach(async user =>
{
//....

LeaveBalance sickLeaveBalance = await this.GetUserLeaveBalance(user.Id,});
// .....
});

正如你使用的 async关键字,因此每个用户都会调用一个操作 异步 因此您的代码执行将类似于以下代码的执行:
foreach(var user in users){
//without await
//it's an async method
DoSomeThingAsync(user); //you called GetUserLeaveBalance in DoSomeThingAsync
}

我猜, GetUserLeaveBalance正在使用 DbContext所以你使用 DbContext 异步 你将面临这样的错误

A second operation started on this context before a previous operation completed ...



您需要更改您的 ForEach像这样
foreach(var user in users){
await DoSomeThingAsync(user); //you called GetUserLeaveBalance in DoSomeThingAsync
}

关于asp.net - EF Core : A second operation started on this context before a previous operation completed 中的错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53948384/

25 4 0