如何正确触发Entity Framework 5中集合的惰性加载?

3

我在我的应用程序中使用EF5(代码优先)。我有一个表格,其中包含一些延迟加载字段。

public class TestEntity
{
    public int Id { get; set; }

    public virtual TestEntity2 SubEntity2 { get; set; }
    public virtual TestEntity3 SubEntity3 { get; set; }

    private ICollection<SubEntity4> _subEntities;
    public ICollection<SubEntity4> SubEntities
    {
        get { return _subEntities ?? (_subEntities = new Collection<SubEntity4>()); }
        protected set { _subEntities = value; }
    }
}

当我从数据库读取这个数据时,SubEntity2和SubEntity3都能够正常加载,但是SubEntities集合就是无法加载,始终保持Count=0。因此,我采用以下方式强制加载:

db.Entry(queryResult).Collection(rr => rr.SubEntities).Load();

但是从我的理解来看,这个集合应该在第一次调用时由EF自动加载,就像SubEntity2和SubEntity3一样。为什么集合无法工作呢?

下面是我用来读取数据库的代码示例:

using (var db = new TestContext(_connection, false))
        {
            var query = from r in db.SubEntities
                        where r.Id == 10
                        select r;

            var queryRes = query.FirstOrDefault();
            if (queryRes != null)
            {
                if (queryRes.FederalRegion != null)
                {
                    // Do something
                }

                foreach (var dbEnt in queryRes.SubEntities)
                {
                    // Do something
                }
            }
        }
3个回答

8
为了使懒加载工作,EF5必须进行一些巧妙的处理。在运行时,他们创建代理类,这些类派生自你的模型类。在这些代理类中,它们覆盖了导航属性以实现懒加载机制。 你的SubEntity2SubEntity3属性是虚拟的,因此它们可以被覆盖。你的SubEntities属性不是虚拟的 - EF5无法覆盖此属性以为其实现懒加载。 当你将SubEntities属性设置为虚拟属性时,它应该可以工作。

感谢您提供详细的答案。但由于某种原因,我认为如果字段未标记为虚拟字段,则默认情况下将始终随包含它的实体一起加载,并且不需要等待第一次调用。 - Nelrum
1
@Nelrum 如果我的回答对您有帮助,请随意将其标记为接受的答案。 :-) - 当属性未标记为虚拟时,它只是不支持延迟加载。仅在您明确要求时才会发生急切加载。 - JustAnotherUserYouMayKnow

0
“子实体”(即集合)本质上是按需加载的。如果您不想让它们按需加载,应该使用.Include(x=> x.SubEntitiesCollectionName)扩展方法。 编辑 您的类应该像这样:
public class TestEntity
{
    public int Id { get; set; }    
    public virtual TestEntity2 SubEntity2 { get; set; }
    public virtual TestEntity3 SubEntity3 { get; set; }    
    public virtual ICollection<SubEntity4> SubEntities { get; set; }
}

他们没有进行懒加载,因为他的代码没有提供启用此特定属性的懒加载的方法。请看一下我的答案。 - JustAnotherUserYouMayKnow
你添加了代码,告诉他他的代码应该看起来像什么,但你没有解释一开始的问题在哪里。此外,我会重新构造你的句子,使用.Include()防止懒加载。这样你就可以进行急切加载,但不会禁用懒加载(因为这已经不必要了)。 - JustAnotherUserYouMayKnow
@LiverpoolsNumber9 啊,谢谢。我实际上错过了虚拟关键字而没有注意到。我一直在尝试找到代码中阅读记录但未在我的实体代码中找到的问题,这是我的错误。 - Nelrum
@JustAnotherUserYouMayKnow - 如果您正在获取非代理数据到另一个对象中,则有必要这样做。一旦您获取了数据并且在EF中关闭了数据连接,您将无法惰性加载任何东西 - 您将收到数据连接错误,并且IIS将关闭。我知道这是因为昨晚发生了这种情况。 - LiverpoolsNumber9

0
为了启用延迟加载,您必须将字段设置为virtual。但是您还有另一个问题。您的ICollection<SubEntity4>private的。这使得映射器无法访问您的字段。我曾经遇到过类似的问题,并询问如何将实体的私有字段设置为可见状态。答案是 - “不可能”!这里是一个链接:

如何启用映射实体的私有属性

所以就像这样做:
   public virtual ICollection<SubEntity4> SubEntities {get;set};

并移除您的属性。


1
你确定这个吗?据我所知,属性必须是虚拟的,并且需要重写属性以启用延迟加载。在这种情况下,字段并不重要。对于一个虚拟的公共导航属性,拥有一个私有的后备字段没有问题。 - JustAnotherUserYouMayKnow
嗯,这个想法很有趣,但我认为它不会起作用。等一下,我会尝试一下。 - Maris
实际上,我有一个名为SubEntities的公共属性,用于_subEntities。我这样做是为了在第一次创建此实体时实现创建此集合的逻辑,而无需每次编写新的Collection<SubEntity4>()。否则,它显然会抛出Null对象异常。 - Nelrum
哦,现在我明白你想要什么了。LiverpoolsNumber9的答案会起作用。 - Maris
@Maris public virtual ICollection<SubEntity4> SubEntities {get;set}; 我最初尝试将其设置为这样,但由于我想在保存TestEntity到数据库之前填充SubEntities集合并稍后一起保存它,所以当我尝试添加新项时会出现NullReferenceException。这是完全可以预料的,因为没有集合对象可供添加。 - Nelrum

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,