【问题标题】:Why does LINQ with LazyLoading randomly NOT load children?为什么带有 LazyLoading 的 LINQ 不会随机加载子项?
【发布时间】:2014-12-13 05:11:23
【问题描述】:

我正在尝试加载具有一系列 MyObjectChild 对象的 MyObject。我的上下文对象默认启用 LazyLoading,我没有禁用它。我的代码中有以下查询:

    public MyObject GetMyObject(string objectNumber)
    {
        List<MyObject> query = (from obj in Context.MyObjects
                                where obj.ObjectNumber == objectNumber
                                orderby obj.LastUpdateDate descending
                                select obj).ToList();

        return query.FirstOrDefault();
    }

这会将第一项作为对象返回。是的,我们想断开从数据库返回的对象,所以我希望这会返回 MyObject 并附加所有 MyObjectChild 对象。尴尬的部分是数据库有数以千计的 MyObjectChilds,但是一旦在一个蓝月亮它决定不加载它们。

在代码的后面,我有:

    MyObject myObject = GetMyObject("123456");

    foreach (MyObjectChild c in myObject.MyObjectChilds)
    {
        ... /* Do some stuff */
    }

非常随机(一百分之一??)这个 foreach 循环没有任何要“foreach”的记录。请注意,它也不会引发异常!它只是完全绕过这部分代码,就好像 MyObjectChilds 集合是空的一样。

1) 谁能解释为什么会发生这种情况? 99% 的情况下,它都能正常工作,但当它失败时,对我们来说非常糟糕。我需要了解在什么情况下这可能会失败,以便我可以围绕它进行编码。

2) 防止这种情况/情况的最佳方法是什么? 我们到处都在做这种事情,而且我们的数据库布局很大,所以我宁愿不要偷懒加载并且必须重写我们所有的底层代码以在整个地方使用数千个 .Include(...) 语句。是否有更好/更简单/更快的解决方案仍能保证我们每次都能获得信息?

更新

在进行了更多挖掘之后,我找到了两个“半简单”的解决方案来解决这个问题。我知道但忽略了一个:ToList()。是的,我在查询上执行了ToList(),但我没有在foreach 上执行此操作,因此它有机会不加载子对象。

    foreach (MyObjectChild c in myObject.MyObjectChilds.ToList())
    {

另一个像.ToList() 一样非常工作的解决方案是在每次使用之前强制加载子对象。这对我来说实现起来会更加麻烦(基于我当前的代码)。但是,(从我从 SO 周围的其他帖子中可以看出)这更有可能总是得到我正在寻找的结果。

    if (!obj.MyObjectChilds.IsLoaded)
    {
        obj.MyObjectChilds.Load();
    }
    foreach (MyObjectChild c in myObject.MyObjectChilds)
    {
        ...

现在,我正在检查我的代码,以确保我已经 ToList-ed 我所有的循环子数据库对象的 foreach 循环。这似乎是最简单的解决方法,尽管我不确定它是否是“最正确”的解决方法。

【问题讨论】:

  • 这听起来像是一个超时错误被抛出,可能被捕获但未处理。
  • 我有一个 try/catch 和一个通用的 catch(异常)。它从不进入那段代码。
  • 你实际上不需要ToList,因为FirstOrDefault 将返回结果。并不是说我认为这会解决任何问题。
  • 很高兴知道。 :) 谢谢。
  • 你为什么不用Include()

标签: c# linq linq-to-sql entity-framework-4


【解决方案1】:

所以我以前也遇到过同样的问题。不过,关键是在您获得初始收藏后立即检测到它。

一旦您检测到这一点,您就需要重新加载子集合。

这里有一个扩展方法来帮助解决这个问题。

public static void RefreshChildCollections<TEntity, TContext>(this ICollection<TEntity> entityCollection, TContext content)
{
    ((IObjectContextAdapter)content).ObjectContext.Refresh(RefreshMode.StoreWins, entityCollection);
}

用法

collectionToReload.RefreshChildCollections(Context);

【讨论】:

  • 您提到“一旦检测到”。您通常如何检测到这一点,而无需我在每次查询后检查计数?
  • 遗憾的是,您必须检查计数。至少我还没有找到更好的方法。因此,我正在检查的所有收藏品我只是在它们上调用“任何”。
【解决方案2】:

ToList() 将执行查询并创建无法加载子项的数据的本地副本。但要 100% 确定孩子会被加载,请使用 Include 方法。另外,我建议您重写查询(使用 FirstOrDefault 而不是 ToList 并手动复制对象(使用 Clone 方法))。要检查 MyObjectChilds 是否已加载,请使用 IsLoaded 并手动加载子项使用 Load 方法。

【讨论】:

  • 完全不正确。列表中的对象附加到上下文对象。它们的功能完全相同。
  • 这只是部分正确。列表中的任何包含子集合都不会通过延迟加载被带入内存。父集合将被带入内存。然后父集合将包含动态实体对象以供稍后加载。但是,从问题来看,对象没有被加载和/或内容不再在范围内,因此无法加载这些子集合。
猜你喜欢
  • 2018-09-13
  • 2013-09-26
  • 2017-06-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-01-27
  • 1970-01-01
  • 2018-03-21
相关资源
最近更新 更多