2013年3月14日星期四

EntityFramework的DbUpdateConcurrencyException與OptimisticConcurrencyException的解析和解決辦法

這幾天,工作上需要製作一個Data Grid型式的Edit Form做CRUD
技術上使用ASP.NET MVC4 / EntityFramework 5.0 / Scaffolding,因為一個小小低級錯誤,導致出現DbUpdateConcurrencyException,了解這個錯誤後,在此講解一下,順道亦介紹一下相關的OptimisticConcurrencyException

DbUpdateConcurrencyException
這是EF 5.0新增的Exception,如圖所示,這通常會出現在SaveChanges()的時候。

Exception Message :
中文 :
存放區更新、插入或刪除陳述式影響到非預期數目的資料列 (0)。這些實體載入之後可能被修改或刪除了。請重新整理 ObjectStateManager 實體。
英文 :
Store update, insert, or delete statement affected an unexpected number of rows (0). Entities may have been modified or deleted since entities were loaded. Refresh ObjectStateManager entries.

原因 :
就如Exception Message所說,這個錯誤會出現在INSERT / UPDATE / DELETE,原因是出現非預期的Affected Row數目。
那我們就要想一想什麼原因會導致Affected Row是 "0"。

1. 遺失Primary Key
以我工作上的例子,MVC4 Scaffolding建立的View都是含@model keyword的strongly-typed view
理論上你按Submit ,Post form後,ActionResult應該都會做Model-Binding

但有時候,因為一些人為錯誤,就會導致接收的Model數值不完整。
例如我這個情況,把DropDownList設定disabled,Post form後,那就沒有CompanyID了。出現"0"是因為CompanyID是int Type預設值而已。
那EF 跟本找不到符合的紀錄,固然亦UPDATE不能,所以就回報"0"Rows了。



這情況亦會出現在UPDATE和INSERT的場景上,所以要多加注意。
但有時我們真的需要把DropDownList / TextBox等等設定為Disabled,那怎麼辦?
那跟ASP.NET Webforms時一樣,加上一個HiddenField吧。
Razor 方法:
@Html.HiddenFor(model => model.CompanyID)

2. 同時Parallel作業
這就是所謂的並行處理(Concurrency)。
舉一個簡單的例子,Ken開啟了某客戶的Edit Form,想把電話修改一下,開啟後,Ken離開了崗位,但Edit Form依然開著。
幾分鐘後,Cammy把這位客戶的資料刪除。
當Ken 回到崗位後,修改了客戶的電話,按儲存。
就會出現DbUpdateConcurrencyException了。
原因很明顯就是找不到紀錄。

OptimisticConcurrencyException
跟上面的同時Parallel作業很相似,但OptimisticConcurrencyException只會發生在UPDATE的時候。
兩位使用者同時修改同一項目,Ken更新電話,Cammy更新地址,只要數值正確,雙方都可以進行儲存。
只是如果Ken和Cammy都是更新電話的話,就要Handle OptimisticConcurrencyException這個Exception進行Resolve Conflicts。
可以看看下列兩條連結的做法,是不簡單的。

不過由此可以見到EF發展到5.0版本已經是一個功能強大,安全的Framework。

Handling Concurrency with the Entity Framework in an ASP.NET MVC Application
Handling Concurrency with the Entity Framework 4.0 in an ASP.NET Web Application

沒有留言:

發佈留言