【问题标题】:Eager Loading in EF 5 fails with deep graph (using .include())EF 5 中的急切加载因深度图而失败(使用 .include())
【发布时间】:2013-05-23 18:17:49
【问题描述】:

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

我错过了什么吗?

非常感谢所有的助手。

【问题讨论】:

    标签: c# .net ef-code-first entity-framework-5 eager-loading


    【解决方案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 关键字以禁用延迟加载后,我也进行了测试,但结果相同(成功)。

    我的测试截图:

    【讨论】:

    • 您好,感谢您的回复。我正在使用 VS2012、.Net 4.5 和 EF5。我刚刚将其切换为使用 SQL Express 2012 并获得相同的结果。我将在“答案”中发布
    • 很遗憾,由于没有达到一些奇怪的分数,无法发布屏幕截图,但结果保持不变,一个空的 ChildrenB 集合。还有其他想法吗?
    • 嗯,情节变厚了。刚刚在 Win7 x64、VS2010、SQL Express 2008 R2、.Net4 上设置了一个虚拟机,并且根据 Slauma 的响应一切正常。现在将把工作项目转移到其他环境,看看会发生什么。
    • 将工作项目移回开发环境(Windows8 x64、VS2010、SQL Server 2008 R2 或 SQL Server 2012 Express)并返回问题。将发布到 Microsoft 进行调查。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-12
    • 1970-01-01
    • 1970-01-01
    • 2014-01-26
    • 1970-01-01
    相关资源
    最近更新 更多