我不是一个DB人,我对EF很新。我对FK关系中延迟加载的不稳定行为感到困惑。
假设我有一个表Parent和一个表Children。儿童对父母有非独特的FK。现在有些代码:
Parent GetParent(int parentId)
{
return Entities.Parents.Single(p => p.ParentId == parentId);
}
Child GetChildByName(int parentId, string name)
{
var parent = GetParent(parentId);
return parent.Children.Single(c => c.Name == name);
}
现在,有时对parent.Children.Single
的调用将失败,因为序列Parent.Children
为空。不始终,但有时,这使得这非常令人沮丧。当它失败时,我已通过Intellitrace验证没有执行对fetch Children
的SQL调用。
此关系已在数据库中正确创建,并且所有输入参数均正确无误。
如果我使用Include("Children")
并急切地加载所有孩子,那么每次都会有效。
如果我在获得父级之后但是在我过滤子级之前抛出Thread.Sleep(1000)
,则子级将经常被加载并且调用成功。
因此,据我所知,这是与时间相关的问题。 FK关系没有按需加载,但它们正在看似随意加载。我不明白这种行为,我认为我必须遗漏一些东西。
我真的不想在任何地方添加Include("SomeFK")
因为它会让我的代码变得更脆弱,而且,为什么我必须这样做呢?如果我必须在我所在的地方({1}}拨打电话
A)抓取我经常不需要的数据 B)编写代码,每当我添加或删除关系时都必须编辑 C)我的ORM没有得到太多(除了琐碎的代码生成)。我此时也可以编写原始SQL调用。
所以是的,我必须遗漏一些东西,但我看起来并且无法找到使用POCO类型(数据库优先!)延迟加载的全面解释。
答案 0 :(得分:0)
我想知道这是否与Parent方法中的Entities对象有关。可能是Entities对象被处置或不可用,以便在抓取Parent对象时,子节点不是因为在进行延迟加载之前处置了实体?
如果您要在实体上下文的范围之外检索它们,我建议使用这样的代码模式来使您的对象具有显式加载。否则随着上下文的消失,对象将无法延迟加载?这不是100%,但这就是为什么我总是在没有DbContext对象的情况下返回记录时显式加载。
答案 1 :(得分:0)
好的,所以这是我自己的愚蠢错误。当然,这个错误出现在我的代码中,而不是成千上的开发人员使用的库。
我没有在问题中提供足够的信息。我会忽略一些细节,但根本原因是竞争条件。
我正在通过在线程A上创建的上下文创建/加载实体对象。然后将它们传递给调整新线程(B)的函数并完成它们的操作。对导航属性的调用最终在线程B上执行。
从阅读文档可以清楚地看出,上下文不是线程安全的。从我可以收集的内容来看,EF在创建上下文的线程上执行延迟加载,即线程A.
这就是为什么Thread.Sleep()似乎“修复”了这个问题;线程B空闲一秒,而主线程有机会填充导航属性。
在我们的代码中,主线程忙于在调用异步函数后立即执行一堆UI更新,因此它会阻止上下文执行其操作。同步执行所有操作都很正常。
希望这有助于某天遇到类似情况的人。正如古老的谚语所说,RTFM。