我已经声明了一个这样的实体(实际的类显然也具有ID属性,映射已完成等,但这不是问题,因此我在这里跳过了它):
public class Parent
{
public virtual ICollection<Child> Children {get; set;}
}
这很完美:
public class Consumer
{
void DoBusiness()
{
using (var ctx = new MyDbContext())
{
var entity = ctx.Parents.Find(keyOfParent);
// This is as expected: entity.Children refers to a collection which
// Entity Framework has assigned, a collection which supports lazy loading.
}
}
}
现在,我将Children集合的可见性更改为受保护的:
public class Parent
{
protected virtual ICollection<Child> Children {get; set;}
}
这带来了意外的结果:
public class Consumer
{
void DoBusiness()
{
using (var ctx = new MyDbContext())
{
var entity = ctx.Parents.Find(keyOfParent);
// This is NOT as expected: entity.Children is null. I would expect, that it
// had been referring to a collection which Entity Framework would have been
// assigning, a collection which should support lazy loading.
}
}
}
此外,如果我处于儿童受保护的情况,请尝试通过以下方式明确加载儿童:
ctc.Entry(entity).Collection(x => x.Children)
然后我得到这个异常:
类型'Parent'的属性“ Children”不是导航属性,Reference和Collection方法只能与导航属性一起使用,请使用Property或ComplexProperty方法。
因此:为了使用Entity Framework具有受保护的导航属性,我应该怎么做?
最佳答案
这就是我的工作方式。
public class Parent
{
public int Id { get; set; }
public string Name { get; set; }
internal ICollection<Child> children;
protected virtual ICollection<Child> Children { get { return children; } set { children = value; } }
internal ICollection<Child> GetChildren() => Children;
internal static Expression<Func<Parent, ICollection<Child>>> ChildrenSelector => p => p.Children;
}
public class Child
{
public int Id { get; set; }
public string Name { get; set; }
internal Parent parent;
protected virtual Parent Parent { get { return parent; } set { parent = value; } }
internal Parent GetParent() => Parent;
internal static Expression<Func<Child, Parent>> ParentSelector => c => c.Parent;
}
public class MyDbContext : DbContext
{
public DbSet<Parent> Parents { get; set; }
public DbSet<Child> Children { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Parent>()
.HasMany(Parent.ChildrenSelector)
.WithRequired(Child.ParentSelector)
.Map(a => a.MapKey("ParentId"));
base.OnModelCreating(modelBuilder);
}
}
所使用的显式字段不是必不可少的-为了便于查看其中的内容,我将其放入其中,您可以继续使用自动属性。
基本部分是提供属性访问器表达式,并将其与Fluent API配置一起使用。如果没有显式配置,您将获得所描述的行为。有了它,一切正常。
例如,延迟加载:
var parent = ctx.Parents.Find(keyOfParent);
var children = parent.GetChildren();
或显式加载:
var parent = ctx.Parents.Find(keyOfParent);
db.Entry(parent).Collection("Children").Load();
var children = parent.children;
更新:很奇怪,如果我们替换配置代码
modelBuilder.Entity<Parent>()
.HasMany(Parent.ChildrenSelector)
.WithRequired(Child.ParentSelector)
.Map(a => a.MapKey("ParentId"));
根据定义完全相等
modelBuilder.Entity<Child>()
.HasRequired(Child.ParentSelector)
.WithMany(Parent.ChildrenSelector)
.Map(a => a.MapKey("ParentId"));
数据库表和FK相同,但是加载不起作用!因此,可行的解决方案偶然碰到了后门,或者EF中存在错误。在这两种情况下,该功能对我来说都是有问题的,为了避免意外,我将仅使用
public
访问器。