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

nd | | 访问(280)

  在使用MySQL数据库来做为Entity Framework的Provider时,有时会遇到如下问题:

存储区更新、插入或删除语句影响到了意外的行数(0)。实体在加载后可能被修改或删除。刷新 ObjectStateManager 项。

  问题产生的场景可能为:

  通常会遇到这样的问题,我们打开编辑界面,然后不更改任何内容,然后点保存,基本流程为

 var detail=db.Table.FirstOrDefault(c=>c.id==1);
 detail.Content=model.Content;
 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(当前实体已经修改 && 所有属性更改后的值与更改前的值相同){

  使之不更新

  }

  我具体代码实现如下

public class EntityFrameworkFix {
    public static void SavingChanges(object sender, EventArgs e) {
        var context = sender as ObjectContext;
        if (context == null) return;
        var updatedEntites = context.ObjectStateManager.GetObjectStateEntries(EntityState.Modified);

        foreach(var ose in updatedEntites) {
            var props = ose.GetModifiedProperties().ToList();
            int modifyCount = 0;
            int propCount = props.Count();
            for (int i = 0; i < propCount; i++) {
                var prop = props[i];
                var index = ose.OriginalValues.GetOrdinal(prop);
                if (ose.OriginalValues.GetValue(index).Equals(ose.CurrentValues.GetValue(index))) {
                    modifyCount++;
                }
            }
            if (modifyCount == propCount) {
                context.Refresh(RefreshMode.StoreWins, ose.Entity);
            }
        }
    }
}

  然后我们在我们声明Context时添加如下事件

db.SavingChanges += EntityFrameworkFix.SavingChanges;

  这样就可以不执行那些赋值了,但值没有变化的Update。

  另外,前些天EntityFramework4.1也发布了,但是因为EntityFramework4.1中不再支持SavingChanges所以我们要使用其它方法 来支持,我们可以在DbContext中override ValidateEntity方法来实现这一过程

public partial class MyEntities: DbContext
{
    protected override System.Data.Entity.Validation.DbEntityValidationResult ValidateEntity(System.Data.Entity.Infrastructure.DbEntityEntry entityEntry, System.Collections.Generic.IDictionary < object, object > items) {
        int wrongCount = 0;
        int allCount = 0;
        if (entityEntry.State == System.Data.EntityState.Modified) {
            foreach(var name in entityEntry.OriginalValues.PropertyNames) {
                var prop = entityEntry.Property(name);
                if (prop.IsModified) {
                    allCount++;
                    if (prop.OriginalValue.Equals(prop.CurrentValue)) wrongCount++;
                }

            }
            if (wrongCount == allCount) entityEntry.State = System.Data.EntityState.Unchanged;
        }
        return base.ValidateEntity(entityEntry, items);
    }
}

  文章来源:http://www.cnblogs.com/chsword/archive/2011/04/20/ef_mysql_update.html