本文將很容易解決的問題搞複雜了,多數情況下將MySql連接字串中的Use Affected Rows參數設定為true即可。
2012-06-17 作者注。
在使用MySQL資料庫來做為Entity Framework的Provider時,有時會遇到如下問題:
儲存區更新、插入或刪除語句影響到了意外的行數(0)。實體在載入後可能被修改或刪除。重新整理 ObjectStateManager 項。
問題產生的情境可能為:
通常會遇到這樣的問題,我們開啟編輯介面,然後不更改任何內容,然後點儲存,基本流程為
1: var detail=db.Table.FirstOrDefault(c=>c.id==1);
2: detail.Content=model.Content;
3: db.SaveChanges();
因為我們沒有更改任何內容,所以更改前與更改後的內容是一致的,這時會報出以上錯誤
其原因是因為MySQL與SQLServer的處理機制不同,如果更新內容與資料庫的一致,SQLServer仍然會返回受影響行數為1,但是MySQL則會認為受影響行數為0。
而Entity Framework應該是預設以SQLServer的為準,所以認為如果更新了,但是返回受影響條數為0,則認為是更新不正確
我們處理這個問題有很多方法 ,最笨的應該是在賦值前進行判斷,detail.Content是否與model.Content相等,如果相等就不進行賦值。
不過因為EntityFramework是在我們對屬性賦值時更新了此屬性的State使之標識為Modified或Deleted等 狀態,所以我們是可以有更方便的方法來控制這些更新的。
我們可以使用EntityFramework的Context中的SavingChanges這個事件在SaveChanges之前做一些處理:
其基本原理為
if(當前實體已經修改 && 所有屬性更改後的值與更改前的值相同){
使之不更新
}
我具體代碼實現如下
1: public class EntityFrameworkFix
2: {
3: public static void SavingChanges(object sender, EventArgs e)
4: {
5: var context = sender as ObjectContext;
6: if (context == null) return;
7: var updatedEntites = context.ObjectStateManager.GetObjectStateEntries(EntityState.Modified);
8:
9: foreach (var ose in updatedEntites)
10: {
11: var props = ose.GetModifiedProperties().ToList();
12: int modifyCount = 0;
13: int propCount = props.Count();
14: for (int i = 0; i < propCount; i++)
15: {
16: var prop = props[i];
17: var index = ose.OriginalValues.GetOrdinal(prop);
18: if (ose.OriginalValues.GetValue(index).Equals(ose.CurrentValues.GetValue(index)))
19: {
20: modifyCount++;
21: }
22: }
23: if (modifyCount == propCount)
24: {
25: context.Refresh(RefreshMode.StoreWins, ose.Entity);
26: }
27: }
28: }
29: }
然後我們在我們聲明Context時添加如下事件
1: db.SavingChanges += EntityFrameworkFix.SavingChanges;
這樣就可以不執行那些賦值了,但值沒有變化的Update。
另外,前些天EntityFramework4.1也發布了,但是因為EntityFramework4.1中不再支援SavingChanges所以我們要使用其它方法 來支援,我們可以在DbContext中override ValidateEntity方法來實現這一過程
1: public partial class MyEntities : DbContext
2: {
3: protected override System.Data.Entity.Validation.DbEntityValidationResult ValidateEntity(System.Data.Entity.Infrastructure.DbEntityEntry entityEntry, System.Collections.Generic.IDictionary<object, object> items)
4: {
5: int wrongCount = 0;
6: int allCount = 0;
7: if(entityEntry.State== System.Data.EntityState.Modified)
8: {
9: foreach(var name in entityEntry.OriginalValues.PropertyNames)
10: {
11: var prop = entityEntry.Property(name);
12: if (prop.IsModified)
13: {
14: allCount++;
15: if (prop.OriginalValue.Equals(prop.CurrentValue))
16: wrongCount++;
17: }
18:
19: }
20: if (wrongCount == allCount)
21: entityEntry.State = System.Data.EntityState.Unchanged;
22: }
23: return base.ValidateEntity(entityEntry, items);
24: }
25: }