Die*_*hon 69 entity-framework ef-code-first
鉴于这个非常简单的模型:
public class MyContext : BaseContext
{
public DbSet<Foo> Foos { get; set; }
public DbSet<Bar> Bars { get; set; }
}
public class Foo
{
public int Id { get; set; }
public int Data { get; set; }
[Required]
public virtual Bar Bar { get; set; }
}
public class Bar
{
public int Id { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
以下程序失败:
object id;
using (var context = new MyContext())
{
var foo = new Foo { Bar = new Bar() };
context.Foos.Add(foo);
context.SaveChanges();
id = foo.Id;
}
using (var context = new MyContext())
{
var foo = context.Foos.Find(id);
foo.Data = 2;
context.SaveChanges(); //Crash here
}
Run Code Online (Sandbox Code Playgroud)
有了DbEntityValidationException.找到的消息EntityValidationErrors是Bar字段是必需的..
但是,如果我Bar通过添加以下行强制加载属性SaveChanges:
var bar = foo.Bar;
Run Code Online (Sandbox Code Playgroud)
一切正常.如果我删除该[Required]属性,这也有效.
这真的是预期的行为吗?是否有任何变通方法(除了每次我想要更新实体时加载每个必需的引用)
Xha*_*ent 54
我发现以下帖子有同样问题的答案:
导致此问题的原因是在RC和RTM验证中不再延迟加载任何属性.之所以进行此更改,是因为在保存大量具有延迟加载属性的实体时,验证会逐个获取这些实体,从而可能导致大量意外事务并降低性能.
解决方法是在使用.Include()保存或验证之前显式加载所有经过验证的属性,您可以在此处阅读有关如何执行此操作的更多信息:http: //blogs.msdn.com/b/adonet/archive/2011/01 /31/using-dbcontext-in-ef-feature-ctp5-part-6-loading-related-entities.aspx
我对此的看法是,这是一个非常糟糕的代理实现.虽然不必要地走对象图并重新启动延迟加载的属性自然是有待避免的(但在微软的EF的第一个版本中显然被忽略了),你不必需要去代理包装器来验证它是否存在.再想一想,我不知道为什么你还需要走对象图,当然ORM的变化跟踪器知道哪些对象需要验证.
我不确定为什么会出现问题,但我确信如果我使用说NHibernate,我就不会遇到这个问题.
我的"解决方法" - 我所做的是在EntityTypeConfiguration类中定义关系的必需属性,并删除Required属性.这应该使它工作正常.这意味着您不会验证关系,但它将无法更新.不是理想的结果.
小智 44
好的,这是真正的答案=)
如果你有一个属性(比如你的Bar)注意到FK(ForeignKey),你也可以在你的模型中有相应的FK字段,所以如果我们只需要FK而不是实际,Bar我们不需要它去数据库:
[ForeignKey("BarId")]
public virtual Bar Bar { get; set; }
public int BarId { get; set; }
Run Code Online (Sandbox Code Playgroud)
现在,为了回答你的问题,你能做些什么来让Bar为Required是标志BarId为所需的属性,而不是Bar它本身:
[ForeignKey("BarId")]
public virtual Bar Bar { get; set; }
[Required] //this makes the trick
public int BarId { get; set; }
Run Code Online (Sandbox Code Playgroud)
这就像一个魅力=)
透明的解决方法,以忽略卸载的引用上的错误
在您的DbContext,覆盖ValidateEntity方法中删除未加载的引用上的验证错误.
private static bool IsReferenceAndNotLoaded(DbEntityEntry entry, string memberName)
{
var reference = entry.Member(memberName) as DbReferenceEntry;
return reference != null && !reference.IsLoaded;
}
protected override DbEntityValidationResult ValidateEntity(DbEntityEntry entityEntry,
IDictionary<object, object> items)
{
var result = base.ValidateEntity(entityEntry, items);
if (result.IsValid || entityEntry.State != EntityState.Modified)
{
return result;
}
return new DbEntityValidationResult(entityEntry,
result.ValidationErrors
.Where(e => !IsReferenceAndNotLoaded(entityEntry, e.PropertyName)));
}
Run Code Online (Sandbox Code Playgroud)
优点:
这是一个半可接受的解决方案:
var errors = this.context.GetValidationErrors();
foreach (DbEntityValidationResult result in errors) {
Type baseType = result.Entry.Entity.GetType().BaseType;
foreach (PropertyInfo property in result.Entry.Entity.GetType().GetProperties()) {
if (baseType.GetProperty(property.Name).GetCustomAttributes(typeof(RequiredAttribute), true).Any()) {
property.GetValue(result.Entry.Entity, null);
}
}
}
Run Code Online (Sandbox Code Playgroud)