.Net Core DBContext 依赖注入
.Net Core DBContext dependency injection
我已经根据 MS Instructions 和本网站上的其他帖子重构了我的代码,以避免出现以下错误,它似乎略有改进,但是 如果我有多个按钮快速连续点击 我仍然收到以下错误:
A second operation started on this context before a previous operation completed. This is usually caused by different threads using the same instance of DbContext, however instance members are not guaranteed to be thread safe.
startup.cs
services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
在我的控制器中:
readonly RGWIncrementUserActionClicks RGWIncrementClicks = new();
private readonly ApplicationDbContext _context;
public RGWController(IEmailSender emailSender, ApplicationDbContext context)
{
_emailSender = emailSender;
_context = context;
}
public async Task <IActionResult> RGWInsertNewActionNumber(int actionNumber)
{
string userId = ClaimsPrincipalExtensions.GetUserId<string>(User);
await RGWIncrementClicks.UserClick(actionNumber, userId, _context);
return RedirectToAction("RGW");
}
当await RGWIncrementClicks.UserClick(actionNumber, userId, _context);
被触发时,UserClick
方法(未显示)有一个switch语句,它可以调用其他方法的组合以根据actionNumber
向DB添加值参数传入。
我尝试了使用 await 的所有后续方法和调用,但仍然出现多线程错误。我也尝试在没有他们的情况下使用 await,但仍然出现多线程错误。
我不明白的是,await RGWIncrementClicks.UserClick(actionNumber, userId, _context);
是所有使用dbContext的后续函数的入口点,它使用await
,加上它为此传递了注入的dbContext调用实例,那么我怎么会在后续函数中出现线程错误...它们应该只在此处等待入口点完成后触发第二次,表明所有后续函数都已完成...不是吗?
更新: 我只是在想我实际上并不希望它是异步的,因为我希望 UI 在每次用户点击时更新RedirectToAction 在数据库更改后触发。所以也许这不是线程管理问题,而是用户管理问题....我应该防止用户在触发我的 RGWInsertNewActionNumber
方法的任何按钮上单击两次...
Update.2 - 为了保存 space:
public class RGWIncrementUserActionClicks
{
private static readonly IncrementSiteActions _IncrementSiteActions = new();
private static readonly IncrementUserActions _IncrementUserActions = new();
public async Task UserClick(int actionNumber, string userId, ApplicationDbContext _context)
{
var userActions = await _context.UserEnvironmentalActionCounts.FindAsync(userId);
var siteTotalActions = await _context.SiteEnvironmentalActionCounts.FirstOrDefaultAsync();
siteTotalActions.SiteGlobalWarmingClicks++;
switch (actionNumber)
{
case 1:
if (userActions != null)
{
await _IncrementUserActions.incrementUserActions(userId, userActions => { userActions.ReduceMeat++; userActions.UserReduceMeatCO2Total = userActions.UserReduceMeatCO2Total + 2.0075; }, userActions => { userActions.UserCO2Total = userActions.UserCO2Total + 2.0075; });
if (siteTotalActions != null)
{
await _IncrementSiteActions.incrementSiteActions(siteTotalActions => { siteTotalActions.SiteReduceMeat++; }, true, true, true);
}
else
{
await _context.SiteEnvironmentalActionCounts.AddAsync(new SiteEnvironmentalActionCounts { SiteTotal = 1, SiteGlobalWarmingTotal = 1, SiteReduceMeat = 1, SiteDeforestationTotal = 1, SiteExtinctionTotal = 1 });
}
}
else
{
await _context.UserEnvironmentalActionCounts.AddAsync(new UserEnvironmentalActionCounts { Id = userId, ReduceMeat = 1, UserTotal = 1, UserReduceMeatCO2Total = 2.0075, UserCO2Total = 2.0075 });
if (siteTotalActions != null)
{
await _IncrementSiteActions.incrementSiteActions(siteTotalActions => { siteTotalActions.SiteReduceMeat++; }, true, true, true);
}
else
{
await _context.SiteEnvironmentalActionCounts.AddAsync(new SiteEnvironmentalActionCounts { SiteTotal = 1, SiteGlobalWarmingTotal = 1, SiteReduceMeat = 1, SiteDeforestationTotal = 1, SiteExtinctionTotal = 1 });
}
}
await _context.SaveChangesAsync();
break;
case 2:
...etc.
default:
break;
}
}
}
}
错误详情:
Microsoft.EntityFrameworkCore.DbContext.SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken)
GatheringForGood.Areas.FunctionalLogic.IncrementUserActions.incrementUserActions(string userId, Action<UserEnvironmentalActionCounts> update, Action<UserEnvironmentalActionCounts> updateUserCO2Total) in IncrementUserActions.cs
+
23. await _context.SaveChangesAsync();
GatheringForGood.Areas.FunctionalLogic.RGWIncrementUserActionClicks.UserClick(int actionNumber, string userId, ApplicationDbContext _context) in RGWIncrementUserActionClicks.cs
+
30. await _IncrementUserActions.incrementUserActions(userId, userActions => { userActions.ReduceMeat++; userActions.UserReduceMeatCO2Total = userActions.UserReduceMeatCO2Total + 2.0075; }, userActions => { userActions.UserCO2Total = userActions.UserCO2Total + 2.0075; });
MyApp.Controllers.ReduceGlobalWarmingController.RGWInsertNewActionNumber(int actionNumber) in ReduceGlobalWarmingController.cs
+
354. await RGWIncrementClicks.UserClick(actionNumber, userId, _context);
Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor+TaskOfIActionResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, object controller, object[] arguments)
System.Threading.Tasks.ValueTask<TResult>.get_Result()
尝试替换
private static readonly IncrementSiteActions _IncrementSiteActions = ....
private static readonly IncrementUserActions _IncrementUserActions = ....
有非静态的
private readonly IncrementSiteActions _IncrementSiteActions = ....
private readonly IncrementUserActions _IncrementUserActions = ....
我决定使用 javascript 避免在 UI 图层上多次单击。我对此有一个小问题,但已解决。如果其他人需要防止多次 UI 点击,请在此处获取更多信息。
我已经根据 MS Instructions 和本网站上的其他帖子重构了我的代码,以避免出现以下错误,它似乎略有改进,但是 如果我有多个按钮快速连续点击 我仍然收到以下错误:
A second operation started on this context before a previous operation completed. This is usually caused by different threads using the same instance of DbContext, however instance members are not guaranteed to be thread safe.
startup.cs
services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
在我的控制器中:
readonly RGWIncrementUserActionClicks RGWIncrementClicks = new();
private readonly ApplicationDbContext _context;
public RGWController(IEmailSender emailSender, ApplicationDbContext context)
{
_emailSender = emailSender;
_context = context;
}
public async Task <IActionResult> RGWInsertNewActionNumber(int actionNumber)
{
string userId = ClaimsPrincipalExtensions.GetUserId<string>(User);
await RGWIncrementClicks.UserClick(actionNumber, userId, _context);
return RedirectToAction("RGW");
}
当await RGWIncrementClicks.UserClick(actionNumber, userId, _context);
被触发时,UserClick
方法(未显示)有一个switch语句,它可以调用其他方法的组合以根据actionNumber
向DB添加值参数传入。
我尝试了使用 await 的所有后续方法和调用,但仍然出现多线程错误。我也尝试在没有他们的情况下使用 await,但仍然出现多线程错误。
我不明白的是,await RGWIncrementClicks.UserClick(actionNumber, userId, _context);
是所有使用dbContext的后续函数的入口点,它使用await
,加上它为此传递了注入的dbContext调用实例,那么我怎么会在后续函数中出现线程错误...它们应该只在此处等待入口点完成后触发第二次,表明所有后续函数都已完成...不是吗?
更新: 我只是在想我实际上并不希望它是异步的,因为我希望 UI 在每次用户点击时更新RedirectToAction 在数据库更改后触发。所以也许这不是线程管理问题,而是用户管理问题....我应该防止用户在触发我的 RGWInsertNewActionNumber
方法的任何按钮上单击两次...
Update.2 - 为了保存 space:
public class RGWIncrementUserActionClicks
{
private static readonly IncrementSiteActions _IncrementSiteActions = new();
private static readonly IncrementUserActions _IncrementUserActions = new();
public async Task UserClick(int actionNumber, string userId, ApplicationDbContext _context)
{
var userActions = await _context.UserEnvironmentalActionCounts.FindAsync(userId);
var siteTotalActions = await _context.SiteEnvironmentalActionCounts.FirstOrDefaultAsync();
siteTotalActions.SiteGlobalWarmingClicks++;
switch (actionNumber)
{
case 1:
if (userActions != null)
{
await _IncrementUserActions.incrementUserActions(userId, userActions => { userActions.ReduceMeat++; userActions.UserReduceMeatCO2Total = userActions.UserReduceMeatCO2Total + 2.0075; }, userActions => { userActions.UserCO2Total = userActions.UserCO2Total + 2.0075; });
if (siteTotalActions != null)
{
await _IncrementSiteActions.incrementSiteActions(siteTotalActions => { siteTotalActions.SiteReduceMeat++; }, true, true, true);
}
else
{
await _context.SiteEnvironmentalActionCounts.AddAsync(new SiteEnvironmentalActionCounts { SiteTotal = 1, SiteGlobalWarmingTotal = 1, SiteReduceMeat = 1, SiteDeforestationTotal = 1, SiteExtinctionTotal = 1 });
}
}
else
{
await _context.UserEnvironmentalActionCounts.AddAsync(new UserEnvironmentalActionCounts { Id = userId, ReduceMeat = 1, UserTotal = 1, UserReduceMeatCO2Total = 2.0075, UserCO2Total = 2.0075 });
if (siteTotalActions != null)
{
await _IncrementSiteActions.incrementSiteActions(siteTotalActions => { siteTotalActions.SiteReduceMeat++; }, true, true, true);
}
else
{
await _context.SiteEnvironmentalActionCounts.AddAsync(new SiteEnvironmentalActionCounts { SiteTotal = 1, SiteGlobalWarmingTotal = 1, SiteReduceMeat = 1, SiteDeforestationTotal = 1, SiteExtinctionTotal = 1 });
}
}
await _context.SaveChangesAsync();
break;
case 2:
...etc.
default:
break;
}
}
}
}
错误详情:
Microsoft.EntityFrameworkCore.DbContext.SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken)
GatheringForGood.Areas.FunctionalLogic.IncrementUserActions.incrementUserActions(string userId, Action<UserEnvironmentalActionCounts> update, Action<UserEnvironmentalActionCounts> updateUserCO2Total) in IncrementUserActions.cs
+
23. await _context.SaveChangesAsync();
GatheringForGood.Areas.FunctionalLogic.RGWIncrementUserActionClicks.UserClick(int actionNumber, string userId, ApplicationDbContext _context) in RGWIncrementUserActionClicks.cs
+
30. await _IncrementUserActions.incrementUserActions(userId, userActions => { userActions.ReduceMeat++; userActions.UserReduceMeatCO2Total = userActions.UserReduceMeatCO2Total + 2.0075; }, userActions => { userActions.UserCO2Total = userActions.UserCO2Total + 2.0075; });
MyApp.Controllers.ReduceGlobalWarmingController.RGWInsertNewActionNumber(int actionNumber) in ReduceGlobalWarmingController.cs
+
354. await RGWIncrementClicks.UserClick(actionNumber, userId, _context);
Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor+TaskOfIActionResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, object controller, object[] arguments)
System.Threading.Tasks.ValueTask<TResult>.get_Result()
尝试替换
private static readonly IncrementSiteActions _IncrementSiteActions = ....
private static readonly IncrementUserActions _IncrementUserActions = ....
有非静态的
private readonly IncrementSiteActions _IncrementSiteActions = ....
private readonly IncrementUserActions _IncrementUserActions = ....
我决定使用 javascript 避免在 UI 图层上多次单击。我对此有一个小问题,但已解决。如果其他人需要防止多次 UI 点击,请在此处获取更多信息。