23

我将 Entity Framework 5.0 与 DbContext 和 POCO 实体一起使用。有一个包含 3 个属性的简单实体:

public class Record
{
    public int Id { get; set; }
    public string Title { get; set; }
    public bool IsActive { get; set; }
}

Title 字段始终未修改,UI 只是显示它,而不提供任何输入框来修改它。这就是为什么该Title字段设置为null当表单发送到服务器时。

这是我告诉 EF 执行实体的部分更新(IsActive仅限字段)的方式:

public class EFRepository<TEntity>
{
   ...
   public void PartialUpdate(TEntity entity, params Expression<Func<TEntity, object>>[] propsToUpdate)
   {
       dbSet.Attach(entity);
       var entry = _dbContext.Entry(entity);
       foreach(var prop in propsToUpdate)
           contextEntry.Property(prop).IsModified = true;
   }
}

和电话:

repository.PartialUpdate(updatedRecord, r => r.IsActive);

调用SaveChanges方法,我得到DbEntityValidationException,告诉我,Title是必需的。当我设置dbContext.Configuration.ValidateOnSaveEnabled = false时,一切正常。有什么方法可以避免禁用整个上下文的验证并告诉 EF 不要验证未更新的属性?提前致谢。

4

3 回答 3

25

如果您使用部分更新或存根实体(这两种方法都非常有效!)您不能使用全局 EF 验证,因为它不尊重您的部分更改- 它总是验证整个实体。使用默认验证逻辑,您必须通过调用以下方法将其关闭:

dbContext.Configuration.ValidateOnSaveEnabled = false

并分别验证每个更新的属性。这应该有希望发挥作用,但我没有尝试,因为我根本不使用 EF 验证:

foreach(var prop in propsToUpdate) {
    var errors = contextEntry.Property(prop).GetValidationErrors();
    if (erros.Count == 0) {
        contextEntry.Property(prop).IsModified = true;
    } else {
        ...
    }
}

如果您想更进一步,您可以尝试ValidateEntity在您的上下文中覆盖并重新实现验证,以验证整个实体或仅基于实体状态和属性状态的选定IsModified属性 - 这将允许您使用带有部分更新的 EF 验证和存根实体。

EF 中的验证是恕我直言的错误概念 - 它在逻辑不属于的数据访问层中引入了额外的逻辑。它主要基于这样的想法,即如果您在导航属性上放置所需的验证规则,您始终使用整个实体甚至整个实体图。一旦你违反了这种方法,你总是会发现硬编码到你的实体的单个固定的验证规则集是不够的。

我在很长的待办事项中的一件事是调查验证如何影响SaveChanges操作速度 - 我曾经在 EF4(EF4.1 之前)中基于 DataAnnotations 及其Validator类拥有自己的验证 API,但我很快就停止使用它由于性能非常差。

使用本机 SQL 的解决方法与使用存根实体或关闭验证的部分更新具有相同的效果 = 您的实体仍未验证,但此外您的更改不是同一工作单元的一部分。

于 2012-10-14T08:00:48.310 回答
20

参考Ladislav 的回答,我已将其添加到DbContext类中,现在它删除了所有未修改的属性。
我知道它并没有完全跳过对这些属性的验证,而只是省略了它,但是 EF 验证的是每个实体而不是属性,并且重新重写整个验证过程对我来说太麻烦了。

protected override DbEntityValidationResult ValidateEntity(
  DbEntityEntry entityEntry,
  IDictionary<object, object> items)
{
  var result = base.ValidateEntity(entityEntry, items);
  var falseErrors = result.ValidationErrors
    .Where(error =>
    {
      if (entityEntry.State != EntityState.Modified) return false;
      var member = entityEntry.Member(error.PropertyName);
      var property = member as DbPropertyEntry;
      if (property != null)
        return !property.IsModified;
      else
        return false;//not false err;
    });

  foreach (var error in falseErrors.ToArray())
    result.ValidationErrors.Remove(error);
  return result;
}
于 2015-04-17T03:21:31.487 回答
3

这是先前@Shimmy 响应的混音,它是我目前使用的版本。

我添加的是中的(entityEntry.State != EntityState.Modified) return false;子句Where

protected override DbEntityValidationResult ValidateEntity(DbEntityEntry entityEntry, IDictionary<object, object> items)
{
    var result = base.ValidateEntity(entityEntry, items);

    var falseErrors = result
        .ValidationErrors
        .Where(error =>
        {
            if (entityEntry.State != EntityState.Modified) return false;
            var member = entityEntry.Member(error.PropertyName);
            var property = member as DbPropertyEntry;
            if (property != null) return !property.IsModified;
            return false;
        });

    foreach (var error in falseErrors.ToArray())
    {
        result.ValidationErrors.Remove(error);
    }

    return result;
}
于 2017-03-23T09:47:01.480 回答