N层架构中的SaveChanges问题:需先校验所有项再批量插入
解决方案:先全量校验再批量插入订单
要实现先校验所有条目再统一批量插入的需求,你需要调整代码执行流程,把对象创建、校验和实际数据库写入分开,同时优化库存查询以提升性能,具体步骤如下:
1. 优化库存校验:批量查询减少数据库交互
避免在循环中单次查询单个产品库存,先一次性获取所有涉及的产品库存信息,用字典存储方便快速查找:
2. 收集待插入订单+全量校验
循环遍历订单列表,创建MOrder对象并存入集合,同时用预查询的库存信息做校验,一旦发现库存不足立即返回错误,确保所有订单都通过校验后再执行插入。
3. 批量插入+统一保存
在服务层添加批量插入方法,将所有待插入订单一次性添加到EF上下文,再调用一次SaveChanges()完成数据库写入。
修改后的完整代码示例
// 1. 批量获取所有涉及的产品库存,用字典映射ID与库存、名称信息 var productIds = mOrderList.Select(item => item.MProductId).Distinct().ToList(); var productStockMap = _imProductService.GetMProductsByIds(productIds) .ToDictionary(p => p.Id, p => new { p.Stock, p.Name }); // 2. 收集待插入的订单并完成全量校验 var pendingOrders = new List<MOrder>(); foreach (var item in mOrderList) { if (!productStockMap.TryGetValue(item.MProductId, out var productInfo)) { return ReturnErrorAndSuccess(HttpStatusCode.NotFound, "MProduct", $"未找到ID为{item.MProductId}的产品"); } if (productInfo.Stock <= 0) { return ReturnErrorAndSuccess(HttpStatusCode.NotFound, "MProduct", $"{productInfo.Name} 产品库存不足,操作无法完成。"); } // 创建订单对象并加入待插入列表 var order = new MOrder { StatusAdmin = false, Date = DateTime.Now, StatusMVendor = "订单已接收。", HowMany = item.HowMany, MBasketId = item.MBasketId, MProductId = item.MProductId, MVendorId = item.MVendorId, WInvestorId = item.WInvestorId }; pendingOrders.Add(order); } // 3. 批量插入并统一保存 _imOrderService.BulkInsertAndSave(pendingOrders);
服务层对应的批量插入方法(以EF Core为例)
需要在IOrderService和其实现类中添加批量插入方法,确保只调用一次SaveChanges():
// IOrderService接口新增方法 void BulkInsertAndSave(List<MOrder> orders); // 实现类中的方法 public void BulkInsertAndSave(List<MOrder> orders) { _context.MOrders.AddRange(orders); _context.SaveChanges(); }
额外注意:并发库存问题
如果系统存在多用户并发操作场景,校验库存后到插入订单的间隙,可能出现库存被其他操作扣减的情况。可以通过数据库事务或者**乐观锁(在产品表添加版本号字段)**来避免这类问题,比如在事务中重新校验库存再执行插入。
内容的提问来源于stack exchange,提问作者Yunus Mert Çakar
相关产品推荐
相关产品推荐

