【问题标题】:Weird lazy loading in one-to-many relationship一对多关系中的奇怪延迟加载
【发布时间】:2013-03-31 12:15:11
【问题描述】:

我遇到了一个奇怪的问题,我需要一些帮助。我正在尝试在 EF Code First 中建立一个简单的一对多关系。这个想法是让一个 CardCategory 有多个 CardQuery 实例。

这是我对 CardCategory 的定义:

public abstract class CardCategory
{
    [Key, DatabaseGenerated(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.Identity)]
    public int Number { get; set; }

    [Required]
    public string Title { get; set; }

    [InverseProperty("Category")]
    public virtual ICollection<CardQuery> DataQueries { get; set; }

    public CardCategory()
    {
        // this ensures that the collection is never null (avoiding NullReferenceException).
        // it's no problem, because the constructor gets called before
        // data mapping occurs.
        DataQueries = new List<CardQuery>();
    }
}

这里是 CardQuery:

public abstract class CardQuery
{
    [Key, DatabaseGenerated(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.Identity)]
    public int Number { get; set; }

    [InverseProperty("DataQueries")]
    public virtual CardCategory Category { get; set; }
}

奇怪的是,当我用我的数据样本(少数类别与少数查询相关联)尝试此操作时,它只能以一种方式工作。我可以像这样获取每个查询的父类别:

var query = myContext.CardQueries.Find(1);
var category = query.Category; // this works alright

但是当我以相反的方式尝试时(获取某个类别的所有子查询),我得到的只是一个空集合。

var category = myContext.CardCategories.Find(1);
var queries = category.DataQueries; // here is the problem, I get null or empty collection

也许您想知道 CardCategory 构造函数中的安全线是否与此有关。 相信我,它不会。我尝试的第一件事是删除它,我得到的只是一个 null 而不是空集合。

具有讽刺意味的是,“DataQueries”导航属性在不同的情况下工作:

var query = myContext.CardQueries.Find(1);
var category = query.Category;
var queries = category.DataQueries; // this works, I don't know what's different

似乎 EF 以某种方式无法填充该属性,但我不知道原因。这以前从未发生在我身上,我有点困惑。如有任何建议,我将不胜感激。

最后一件事要清除,我的数据上下文是这样配置的:

LazyLoadingEnabled = true;
ProxyCreationEnabled = true;
AutoDetectChangesEnabled = true;

感谢您的帮助!

【问题讨论】:

    标签: c# .net entity-framework ef-code-first one-to-many


    【解决方案1】:

    我不建议使用 Include,它会导致大型基地的性能严重下降。

    您的问题是 DataQueries 是一个数组,只需调用 category.DataQueries 不会对存储执行任何查询(与您无法编写 myContext.CardQueries 并自动获取结果的方式相同)。您需要显式调用启动器,例如ToList() 或 FirstOrDefault()。

    所以,不要使用 .Include 试试这个:

    var category = myContext.CardCategories.Find(1);
    var queries = category.DataQueries.ToList();
    

    这将启动枚举器,您将获得 DataQueries 的结果。

    作为旁注:您甚至可以通过例如过滤您的 category.DataQueries使用 Where 语句。

    【讨论】:

      【解决方案2】:

      我有一个类似但不相关的问题。我试图延迟加载一些嵌套对象,例如访问 Foo.Bar.Baz 我的问题是 Baz 从不延迟加载,即使 Bar 上的 Collection 是虚拟的。

      原来问题是Bar的ctor造成的,是private的。将 ctor 更改为 protected 允许 EF 为 Bar 正确创建代理类并启用 Baz 集合的延迟加载。

      【讨论】:

        【解决方案3】:

        尝试使用IQueryable&lt;T&gt;.Include():

        var category = myContext.CardCategories
            .Include(e => e.DataQueries)
            .Find(1);
        

        【讨论】:

        • 这行得通,谢谢!但我认为这不是必要的,因为延迟加载。这基本上是急切加载。有什么方法可以确保每次都加载该属性而不像这样在查询中指定它?
        • 据我所知没有,但我可能错了。当我对这个主题进行研究时,大多数文章都说要编写像 GetCardCategoriesIncludingDataQueries() 这样的辅助方法。
        • 好的,我认为暂时可以。再次感谢!
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-08-31
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多