更新接口代码
/// <summary> /// 更新库存量 /// </summary> /// <param name="input"></param> /// <returns></returns> [UnitOfWork(isTransactional: false)] public SaveStockOutput SaveStock(SaveStockInput input) { using (var unitOfWork = _unitOfWorkManager.Begin()) { ……………………………………………………………………………………………………………………………………………………………… _preventDuplicationRealtimeRepository.InsertAndGetId(dupModel);//插入防重key _itemContractStockRepository.InsertAndGetId(itemContractStock);//插入库存数据 ………………………………………………………………………………………………………………………………………………………………… _unitOfWorkManager.Current.SaveChanges(); unitOfWork.Complete(); } …………………………………………………………………………………………………………………………………………………………………………… }
线上库存是加了防重Key,最近经常出现防重key插入成功,库存没插入成功,就出问题了。
今天也是测试了一下
[UnitOfWork(isTransactional: false)] public void Test() { try { using (var unitOfWork = _unitOfWorkManager.Begin()) { var dupModel1 = new PreventDuplicationRealtime() { CreatedDate = DateTime.Now, DupType = (int)PreventDuplicationDupType.SaveStock, DupKey = "00_02_01_test" }; _preventDuplicationRealtimeRepository.InsertAndGetId(dupModel1);//事先不存在 var dupModel = new PreventDuplicationRealtime() { CreatedDate = DateTime.Now, DupType = (int)PreventDuplicationDupType.SaveStock, DupKey = "00_00_01_test" }; _preventDuplicationRealtimeRepository.InsertAndGetId(dupModel);//事先存在,插入报错 _unitOfWorkManager.Current.SaveChanges(); unitOfWork.Complete(); } } catch (Abp.AbpException abpex) { var ex = abpex.GetOriginalException(); if (!ex.IsNotCallCompleteException())//不是事务提交问题才重新抛出异常 { throw new Exception("SaveStock报错:", abpex); } } catch (Exception dupEx) { var ex = dupEx.GetOriginalException(); if (ex.Message.StartsWith("Duplicate", StringComparison.OrdinalIgnoreCase) && ex.Message.Contains("key")) { var t = "重复保存"; } else throw dupEx; } }
这个测试代码运行之后,00_02_01_test被写入数据库。而去掉isTransactional:false之后,00_02_01_test不会被写入,整个测试方法会作为一个事务执行。
于是就去官网查相关文档,这个isTransactional:false一直感觉不对劲,但一直这么用着觉得问题也不大。看到了文档才知道是误用了。
意思大概是说,如果isTransactional:false,那么直接执行sql的代码就不会回滚;还有使用isTransactional:false必须要小心,假如你的方法只读取数据,那么使用它就是安全的。言外之意就是,如果有修改数据,那么不应该使用isTransactional:false。
所以,以后使用ABP框架开发的话一定要注意这个问题。
文档地址:https://aspnetboilerplate.com/Pages/Documents/Unit-Of-Work#DocUowNoTransaction
更新:2017-08-25
经测试及验证情况如下:
没有标记[UnitOfWork]属性,默认存在事务,内部事务并不会马上提交,当请求结束时才提交事务
标记[UnitOfWork(IsDisabled = true)] ,禁用了事务功能,仓储在using (var unitOfWork = _unitOfWorkManager.Begin()){}内部可以,外部可能因为连接关闭如出错。小心使用该属性
标记[UnitOfWork(isTransactional: false)],不使用事务,仓储内容会马上提交,包括使用using (var unitOfWork = _unitOfWorkManager.Begin()){}内部事务,并不会回滚
标记[UnitOfWork(isTransactional: false)],并使用using (var unitOfWork = _unitOfWorkManager.Begin(System.Transactions.TransactionScopeOption.RequiresNew)){},内部事务有效,能事务提交及回滚
所以使用内部事务,建议使用方案4
using (var unitOfWork = _unitOfWorkManager.Begin(System.Transactions.TransactionScopeOption.RequiresNew)) { switch (item.StockPattern) { case StockPattern.ItemContractStock: result = saveContractPriceInput.MsgFrom == UpdatePriceMsgFromType.Linkage ? SaveItemLinkageContractPrice(saveContractPriceInput, eventDataList) : SaveItemContractPrice(saveContractPriceInput, eventDataList); break; case StockPattern.ItemLimitedTimeStock: result = SaveItemLimitedTimeContractPrice(saveContractPriceInput, eventDataList); break; default: result = new SaveContractPriceOutput { Success = false, ErrorMessage = "未实现的元素库存类型" }; break; } unitOfWork.Complete(); }
库存开发注意事项:
所有只查询数据不修改数据的service方法,都要加上 [UnitOfWork(isTransactional: false)] 标记以提高查询性能。因为默认查询也会加事务,如果查询时间过长会报事物中止或超时等等异常。