---------------------------------------------------------最新更新------------------------------------------------------------------
經實驗,新增出現並發異常的,是由於表中字段設置默認值的引起的。具體原因請看EF生成的sql代碼。
請看異常發生前,ef所執行的sql語句。
在insert 數據成功后,又執行了一個查詢語句,所返回的,正是表中設置默認值的字段。
這就是導致新增數據出現並發的原因。
原因如下:
ef追蹤管理實體狀態時,假設有個字段type設置有hasDefaultValue(),當前的type字段是沒有任何值的,臂如是null。
當向數據inset數據時,數據庫字段設置有默認值是“t”,數據庫會將type值null轉換為字段默認值t。
這種情況下,會出現ef管理的實體值與數據庫數據值不一致。
ef core 會將默認值查出來,返回程序中,重新賦值給ef管理的實體,
在新增的事務尚未結束時,又重新對ef管理的數據有默認值的字段進行修改賦值,就導致了新增數據出現並發的異常。
------------------------------------------我是分割線--------------------------------------------------------------------------------------
net core 2.0發布后,一直想體驗下,因種種原因,一直在拖着沒進行。
前陣子公司要加個新的內部管理后台,正好可以用asp.net core來做下,體驗下net core的魅力。
啃過文檔后就上手了,一切很順利。
直到周五,出現了一個並發異常的問題,本以為可以很快處理掉的,但沒想到一直花費了很長時間才解決掉,現在記錄下情況,有相同經歷的伙伴以后可以參考。
先上異常截圖。
異常提示:
Microsoft.EntityFrameworkCore.DbUpdateConcurrencyException: Database operation expected to affect 1 row(s) but actually affected 0 row(s). Data may have been modified or deleted since entities were loaded. See
http://go.microsoft.com/fwlink/?LinkId=527962
for information on understanding and handling optimistic concurrency exceptions.
再上代碼
public int Add(BannerView bannerView) { try { //sysBanner.ImgUrl = sysBanner.ImgUrl.Contains("http") ? sysBanner.ImgUrl : _programConfig.AliYunOSSUrl + sysBanner.ImgUrl; //sysBanner.CreateDate = DateTime.Now; _banner.Add(new SysBanner { Content = bannerView.Content, CreateDate = DateTime.Now, Del = (int)bannerView.Del, ImgUrl = bannerView.ImgUrl.Contains("http") ? bannerView.ImgUrl : _programConfig.AliYunOSSUrl + bannerView.ImgUrl, Operation = "", Sort = bannerView.Sort, SrcUrl = bannerView.SrcUrl, Title = bannerView.Title, Type = (int)bannerView.Type, //MaterialId = 0, //Parameter = 0, //ShareType = 0 }); return _banner.SaveChanges(); } catch (DBConcurrencyException ex) { throw; } }
/// <summary> /// 新增一條數據 /// </summary> /// <param name="model"></param> public void Add(TEntity model) { var entity=_dbSet.Add(model); } public int SaveChanges() { return _dbContext.SaveChanges(); }
代碼特別簡單,就是新增一條數據,進行保存,完全沒復雜的東西。
出現並發異常,整個人是蒙逼狀態。
單人進行調試,新增一條數據,自增ID,不存在多用戶同時操作,怎么可能出現並發?
即使出現並發,主鍵也是自增ID,只會出現多條數據,也不可能是並發異常……並且多個業務模塊,其他模塊的新增完全是正常的。
這又是為什么!!!!
首先就是審視代碼,確定沒問題……檢查依賴注入,沒有問題……檢查ef生成的sql,沒有問題……閱讀官方文檔,沒找到原因……去群里向各位朋友請教,還是無法解決。
官方文檔有一個DBConcurrencyException處理並發異常的,是基於修改和更新的,經試驗,無法解決。
一步一步調試,源碼調試,找到幾處疑點,經排查都不是引起新增並發的bug。
重新dbfrist生成model實體,無法解決……刪除表,重建表,無法解決。
似乎走進了死胡同,園子里博問發貼,也沒有解決。整個排除過程,說起來都是淚。
后來就在想,既然底層調用方法都是一樣的,那唯一不一樣的就是存儲的實體,會不會是表結構導致的?
經過對比出現異常的表與正常的表結構,發現一個不同點,里面有幾個字段設置了默認值,還有兩個非null字段,而正常操作的那些實體里是沒有的。
嘗試取消默認值后,再注釋掉dbFirst生成的一行代碼HasDefaultValueSql("xxx"),再一運行,居然成功了!!!
難道是ef core mysql的bug?表不能設置默認值?這似乎不合道理,產品發布這么久了,很多人已經用在生產環境了,不可能出現這么低級的錯誤吧?
在園子里博問發貼時,有位園友向我說,默認值的字段,要加.HasDefaultValue()。這種是code first時加的,而我用dbFirst時自動生成的是HasDefaultValueSql("xxx")。
難道是調用函數不同而產生的bug?
經過多次實際測試,數據庫設置默認值及非null字段是沒有問題的。
問題出在db first生成實體時時自動加的這行代碼上HasDefaultValueSql("xxx"),只需要注釋掉這些代碼,運行就正常。
.HasDefaultValue()一樣會引起新增並發異常,原因未知。
asp.net core 2.0
ef core mysql
pomelo.entityFrameworkCore.mySql
如果有朋友遇到類似問題,可以做為參考。
至於為什么不能用HasDefaultValueSql("xxx")及.HasDefaultValue(),目前正在研究中,有新發現,會更新文章。