Entity Framework with MySQL Provider 更新行数为0的Bug

在使用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: }
标签: ef, Entity, Framework
posted @ 2011-04-20 18:14 重典 阅读(3334) 评论(16) 编辑 收藏

 回复 引用 查看   
#1楼2011-04-20 18:23 | (张超)      
微软的 Entity Framework 支持MYSQL 吗????
 回复 引用 查看   
#2楼[楼主]2011-04-20 18:32 | 重典      
@(张超)
支持

 回复 引用 查看   
#3楼2011-04-20 18:38 | 蓝鱼哥哥      
想知道 entity framework 语法。 到底该怎么查询呀!
 回复 引用 查看   
#4楼2011-04-20 22:33 | mickeysuda      
4.1新增的支持?
 回复 引用 查看   
#5楼2011-04-21 08:51 | 黑耗子      
学长 顶你一下!
 回复 引用 查看   
#6楼2011-04-21 09:15 | Virus-BeautyCode      
代码能够搞个垂直的滚动条吗!!!看的不完整啊!!!
我的浏览器是IE8 系统windows2003。

 回复 引用 查看   
#7楼2011-04-21 09:21 | testzhangsan      
不错,标记一下!
 回复 引用 查看   
#8楼[楼主]2011-04-21 09:56 | 重典      
@Virus-BeautyCode
已测,IE8下是有滚动条的,你看看你是不是IE7模式或Quirks模式

 回复 引用 查看   
#9楼2011-04-21 10:20 | 风云      
好方法,支持!
 回复 引用 查看   
#10楼2011-04-21 12:35 | 秋色      
在ado.net里这样的错误是经常的。非三层框架。
 回复 引用 查看   
#11楼2011-04-22 09:42 | 落小呆      
if(ose.OriginalValues.GetValue(index).Equals(ose.CurrentValues.GetValue(index)))
{
   modifyCount++;
}else
{
   break;
}

这样子是不是好点,有一个值改变了就应该没必要继续循环了吧

 回复 引用 查看   
#12楼[楼主]2011-04-22 13:22 | 重典      
@落小呆
嗯,是的,呵呵,谢谢提醒

 回复 引用 查看   
#13楼2011-04-22 17:23 | 落小呆      
public virtual void Update(TObjectQuery entity)
{
    if (entity.EntityKey == null)
    {
        entity.EntityKey = _context.CreateEntityKey(ObjectSet.EntitySet.Name, entity);
    }
    object temp = null;
    if (_context.TryGetObjectByKey(entity.EntityKey, out temp))
    {
        ObjectSet.ApplyCurrentValues(entity);
    }
    else
    {
        throw new InvalidOperationException("更新的对象不存在");
    }
}

如果每次更新的对象都没在Context上下文得时候,用上面这个方法,不会出现你这个问题
但如果对象已经加载在Context上下文的时候,一样没效果,试了下感觉也可能只能你这样处理,但总感觉有点别扭

 回复 引用 查看   
#14楼2011-05-06 11:22 | 暴走      
有沒有測試過 MySql的存儲過程啊,Entity Framework 的設計介面不能自動生成參數列表,也不能手動,不知道能不能改Edmx的文件實現,這個不會。然後試著用 ExecuteStoreCommand執行 call spxx(@p1, @p2..)就提示 OUT or INOUT argument is not a variable or NEW pseudo-variable in BEFORE trigger.
不知道老大有沒有解決方案

 回复 引用 查看   
#15楼2011-05-18 17:16 | andylouis      
sql2008R2也存在这个问题了,不仅仅是MYsql
 回复 引用 查看   
#16楼2011-09-28 09:28 | 风的传说      
路过。。。