我正在使用带有DbContext和POCO实体的Entity Framework 5.0。有一个包含3个属性的简单实体:
public class Record
{
public int Id { get; set; }
public string Title { get; set; }
public bool IsActive { get; set; }
}
“标题”字段始终未修改,并且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不要验证未更新的属性?
提前谢谢。
答案 0 :(得分:21)
如果您使用部分更新或存根实体(两种方法都非常有效!),您无法使用全局EF验证,因为it doesn't respect your partial changes - 它始终验证整个实体。使用默认验证逻辑,您必须通过调用上述提示将其关闭:
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及其自己拥有自己的验证API Validator
上课,由于表现很差,我很快就停止使用它了。
使用本机SQL的解决方法与使用存根实体或关闭验证的部分更新具有相同的效果=您的实体仍未经过验证,但此外您的更改不属于同一工作单元。
答案 1 :(得分:17)
在参考Ladislav's answer时,我已将其添加到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;
}
答案 2 :(得分:1)
这是之前@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;
}