我首先在 EF 5 中使用代码。这是我的测试域
public class Master
{
public int Id { get; set; }
public string Name { get; set; }
public virtual Query Query { get; set; }
}
public class Query
{
public Query()
{
ChildrenA = new HashSet<ChildA>();
ChildrenB = new HashSet<ChildB>();
}
public int Id { get; set; }
public string Name { get; set; }
public Master Master { get; set; }
public ChildB SpecialChild { get; set; }
public virtual ICollection<ChildA> ChildrenA { get; private set; }
public virtual ICollection<ChildB> ChildrenB { get; private set; }
public ChildB GetChildByName(string name)
{
return ChildrenB.Where(c => c.Name == name).FirstOrDefault();
}
}
public class ChildA
{
public int Id { get; set; }
public string Name { get; set; }
public int Query_Id { get; set; }
public virtual Query Query { get; set; }
}
public class ChildB
{
public int Id { get; set; }
public string Name { get; set; }
public virtual Query SpecialQuery { get; set; }
public int Query_Id { get; set; }
public Query Query { get; set; }
}
这是我的背景:
public class TestContext : DbContext
{
public TestContext()
:base()
{
}
public TestContext(string connectionString)
: base(connectionString)
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Master>()
.Property(m => m.Id)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
modelBuilder.Entity<Master>()
.HasKey(m => m.Id);
modelBuilder.Entity<Query>()
.Property(q => q.Id)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
modelBuilder.Entity<Query>()
.HasKey(q => q.Id);
modelBuilder.Entity<Query>()
.HasOptional(q => q.Master)
.WithOptionalPrincipal(m => m.Query);
modelBuilder.Entity<Query>()
.HasOptional(q => q.SpecialChild)
.WithOptionalPrincipal(c => c.SpecialQuery);
modelBuilder.Entity<ChildA>()
.Property(c => c.Id)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
modelBuilder.Entity<ChildA>()
.HasKey(c => new { c.Query_Id, c.Id });
modelBuilder.Entity<ChildA>()
.HasRequired(c => c.Query)
.WithMany(q => q.ChildrenA)
.HasForeignKey(c => c.Query_Id);
modelBuilder.Entity<ChildB>()
.Property(c => c.Id)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
modelBuilder.Entity<ChildB>()
.HasKey(c => new { c.Query_Id, c.Id});
modelBuilder.Entity<ChildB>()
.HasRequired(c => c.Query)
.WithMany(m => m.ChildrenB)
.HasForeignKey(c => c.Query_Id);
}
public DbSet<Master> Masters { get; set; }
public DbSet<ChildA> ChildrenA { get; set; }
public DbSet<ChildB> ChildrenB { get; set; }
}
注意:复合 PK 允许 EF 在从 POCO 类的集合中删除对象时强制删除实体。
这是一些加载测试数据的代码:
System.Data.Entity.Database.DefaultConnectionFactory = new SqlCeConnectionFactory("System.Data.SqlServerCe.4.0");
string connectionString = string.Format("Data Source={0};", SQLCEFileName());
using (TestContext context = new TestContext(connectionString))
{
Master master = new Master() { Name = "Master1", Query = new Query() { Name = "Query1" } };
master.Query.ChildrenA.Add(new ChildA() { Name = "ChildA1" });
master.Query.ChildrenB.Add(new ChildB() { Name = "ChildB1" });
master.Query.ChildrenB.Add(new ChildB() { Name = "ChildB2" });
master.Query.ChildrenB.Add(new ChildB() { Name = "ChildB3" });
master.Query.ChildrenB.Add(new ChildB() { Name = "ChildB4" });
context.Masters.Add(master);
Query special = new Query() { Name = "Special" };
ChildB c3 = master.Query.GetChildByName("ChildB3");
c3.SpecialQuery = special;
special.ChildrenA.Add(new ChildA() { Name = "SpecialChildA1" });
special.ChildrenB.Add(new ChildB() { Name = "SpecialChildB1" });
special.ChildrenB.Add(new ChildB() { Name = "SpecialChildB2" });
special.ChildrenB.Add(new ChildB() { Name = "SpecialChildB3" });
special.ChildrenB.Add(new ChildB() { Name = "SpecialChildB4" });
context.SaveChanges();
}
现在,您将看到从 ChildB 到 Query 的可选关系。所以 ChildB 可以有一个查询,然后可以有它自己的 ChildA 和 ChildB 列表。在实践中,它已经深入人心。
当我在查询的最低级别(Master.Query.ChildrenB.SpecialQuery.Children[A 或 B])上使用 include() 来查询 Master 并强制 Eager Loading 时,只会加载一个集合(ChildrenA 或 ChildrenB)。
这是一个示例查询,它应该强制加载整个图形:
using (TestContext context = new TestContext(connectionString))
{
Master master = context.Masters
.Where(m => m.Name == "Master1")
.Include("Query.ChildrenA")
.Include("Query.ChildrenB.SpecialQuery.ChildrenA")
.Include("Query.ChildrenB.SpecialQuery.ChildrenB")
.FirstOrDefault();
ChildB c = master.Query.GetChildByName("Child3");
}
注意:我知道下面集合中引用实体的语法是错误的,我只是用它来说明问题。
此时master.Query.ChildrenB[2].SpecialQuery.ChildrenA.Count 为1(正确) master.Query.ChildrenB[2].SpecialQuery.ChildrenB.Count 为0(应该为4)
如果我修改查询并删除 .Include("Query.ChildrenB.SpecialQuery.ChildrenA") 那么 master.Query.ChildrenB[2].SpecialQuery.ChildrenB.Count 是 4 如预期的那样。
这真的很奇怪,因为 master.Query.ChildrenA 和 master.Query.ChildrenB 中的集合加载得很好。
我在这里错过了什么吗?
非常感谢所有的助手。