具有部分更新的实体框架验证

Sko*_*kog 21 validation entity-framework updates

我正在使用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; }
}
Run Code Online (Sandbox Code Playgroud)

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;
   }
}
Run Code Online (Sandbox Code Playgroud)

和电话:

repository.PartialUpdate(updatedRecord, r => r.IsActive);
Run Code Online (Sandbox Code Playgroud)

调用SaveChanges方法,我得到的DbEntityValidationException,告诉我,Title是必需的.当我设置dbContext.Configuration.ValidateOnSaveEnabled = false,一切都很好.有没有办法避免在整个上下文中禁用验证,并告诉EF不要验证未更新的属性?提前致谢.

Lad*_*nka 22

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

dbContext.Configuration.ValidateOnSaveEnabled = false
Run Code Online (Sandbox Code Playgroud)

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

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

如果您想更进一步,您可以尝试覆盖ValidateEntity您的上下文并重新验证验证的方式是验证整个实体或仅根据实体IsModified状态和属性状态选择属性 - 这将允许您使用EF验证和部分更新和存根实体.

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

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

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


Shi*_*mmy 18

在参考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;
}
Run Code Online (Sandbox Code Playgroud)