1

我首先在 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 中的集合加载得很好。

我在这里错过了什么吗?

非常感谢所有的助手。

4

1 回答 1

1

我没有解释,只是使用SQL Server 2008 R2 Express在 VS 2010 中的 .NET 4.0 上使用 EF 5.0 进行了测试。我已经复制并粘贴了您的代码,只是我删除了显式连接字符串,因为显然您使用的是 SQL Server CE 4.0。

对我来说,它可以按您的预期工作,这引发了一个问题,如果 SQL Server CE 提供程序在这里有错误。

我唯一的区别是有问题的 ChildB ("ChildB3"我认为)在我的集合中具有索引 3,而不是像您的测试中那样具有索引 2。我还在删除所有virtual关键字以禁用延迟加载后进行了测试,但我得到了相同(成功)的结果。

我的测试截图:

很多包括

于 2013-05-23T18:58:55.080 回答