[英]System.InvalidOperationException: when saving data to my database
ASP.Net 核心 blazor 应用程序。 当页面首次打开时,它会从数据库中提取维修订单列表并将它们显示在页面上。 这很好用。
数据表有一列带有“编辑”按钮。 当您编辑数据时,会弹出一个模式并显示 RO 信息供其他人编辑。
我的问题是当我单击“保存”时,出现以下错误。
错误代码:
System.InvalidOperationException: '在上一个操作完成之前,第二个操作在此上下文中启动。 这通常是由不同的线程使用相同的 DbContext 实例引起的,但是不保证实例成员是线程安全的。 这也可能是由在客户端上评估嵌套查询引起的,如果是这种情况,请重写查询以避免嵌套调用。
值得注意的是这个功能效果很好,我不知道我做了什么让它失败。 OnInitAsync() 方法:
protected override async Task OnInitAsync()
{
await LoadData();
}
加载所有数据的方法:
protected async Task LoadData()
{
_repairOrders = await Task.Run(() => RepairOrderService.GetAllRepairOrders().ToArray());
_vehicleLocations = await Task.Run(() => VehicleLocationService.GetAllLocations().ToArray());
_repaitStages = await Task.Run(() => RepairStageService.GetAllRepairStages().ToArray());
_employees = await Task.Run(() => EmployeeService.GetAllEmployees().ToArray());
}
为保存数据而调用的方法。
protected async Task SaveRepairOrder()
{
if (ro.Id != 0)
{
await Task.Run(() =>
{
RepairOrderService.EditRepairOrder(ro);
});
}
else
{
await Task.Run(() =>
{
RepairOrderService.CreateRepairOrder(ro);
});
}
this.isAdd = false;
await LoadData();
}
这是我的数据访问层 class 中抛出错误的方法。
//Get all repair order details as a list
public List<RepairOrder> GetAllRepairOrders()
{
try
{
return db.RepairOrder.ToList();
}
catch
{
throw;
}
}
问题开始后,我使 LoadData() 方法异步。 谁能看到我错过了什么?
**** 更新 ******** 我更改了“services.AddSingleton();” 到“services.AddTransient();” 它似乎正在工作。 我需要深入研究并尝试确定这是否是我一开始就应该这样做的方式!
正如错误所提到的,这里正在发生的事情是,当您的方法尝试使用该上下文时,其他线程已经在使用该上下文。
将注册更改为Transient是可行的,因为实例不再共享了……每次获取它,它将是一个新实例……这有一些缺点,例如,如果您尝试“加入”两个“ IQueriable”它将失败...内存消耗也应该上升...
现在,对于您的问题,您必须调试代码流,因为显然,当您的代码输入LoadData方法时,先前启动的操作正在后台运行。 因此,“ Task.Run”实际上与错误无关...如果删除它,您应该得到相同的错误...
您可以用来验证此假设的操作是在加载数据之前添加了大约一秒或两秒的延迟(等待Task.Delay(2000);)...这应该足以允许其他操作完成,从而释放你的方法
这里有多个问题。
首先,使用异步。 您到处都在await Task.Run()
,这仅是有用的。 没有Task.Run()更好地等待异步Db操作(FindAsync,ToListAsync)。
第二,服务的生命周期管理。 Blazor尚未执行AddScoped(某些启动/身份项目除外)。
因此请注意,当您以正常方式(例如,在MVC应用程序中)注入该db
时,它不会被释放。 每个客户都有自己的DbContext。 在他们的会议期间。
这不一定是问题,但是您应该格外小心,避免使用Changetracking。 也许自己管理Db上下文。
我会写:
public async Task<List<RepairOrder>> GetAllRepairOrders()
{
return await db.RepairOrder
.AsNoTracking() // tracking is expensive, only do it whne needed
.ToListAsync(); // or maybe ToArrayAsync
}
然后在LoadData()中
_repairOrders = await RepairOrderService.GetAllRepairOrders();
这是Blazor服务器端,对吗?
在服务器端Blazor中,添加到DI容器的所有对象的作用域应为“ 作用域 ”。 对象的生存期跨度到连接的生存期。 当您将一个对象添加为Singleton时,其生存期就是应用程序的持续时间。 这就是为什么都不要将任何对象添加为Singleton的原因。
我建议您不要使用Task.Run。 您的应用程序非常基础,如果您遵循上述步骤,它将可以正常工作。
我建议您使用列表而不是数组来保存数据。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.