【问题标题】:Loading of Lazy collections in entity framework too lazy在实体框架中加载延迟集合太懒了
【发布时间】:2017-09-14 12:15:55
【问题描述】:

我遇到了实体框架无法正确处理我用一些对象手动初始化的集合的问题。

我正在使用基于使用私有字段来支持公共集合属性的众多示例中的任何一个的代码。例如按照这个question

但是,如果我以后想将此字段用于 LINQ 查询,我会遇到问题。

我的模型结构如下。

public abstract class Parent
{
    [Key]
    public int Id { get; set; }

    public virtual List<Child> Childeren

    ...
}

public abstract class Child
{
    [Key]
    public int Id { get; set; }

    protected List<Grandchild> _grandchilderen;

    public virtual List<Grandchild> Grandchilderen
    {
        get
        {
            if (_grandchilderen== null)
                SpawnGrandchilderen();
            return _grandchilderen;
        }
        set { _grandchilderen= value; }
    }

    private void SpawnGrandchilderen(){
        _grandchilderen=new List<Grandchild>(){new Grandchild(), new Grandchild()};
    }

    ...
}

public abstract class Child
{
    [Key]
    public int Id { get; set; }

    ...
}

如果我使用类似的查询

parent_instance.Childeren.SelectMany(C=>C.Grandchilderen).ToList()

SpawnGrandchilderen() 在每个覆盖本应从数据库加载的数据的子节点上调用。

如果我在 getter 的开头放置一个断点,我可以看到 _grandchilderen 始终为空。

如果我在 linq 查询之前放置一个断点并在继续执行之前使用监视窗口手动探索 childeren,我可以让代码按预期工作。

我认为这是与延迟加载有关的问题,但我不确定如何有效地解决此问题。


编辑: 我做了更多的探索,我发现的只是:

第一次访问时,Grandchilderen 列表始终为空。

如果我将调用 SpawnGrandchilderen() 替换为 _grandchilderen = new List&lt;Grandchild&gt;() 并在该行上放置一个断点,我可以看到该行正在被命中,但是当我期望一个空数组时,实际上返回了来自数据库的数据返回。

但是,如果我用

替换该行
_grandchilderen = new List<Grandchild>(){new Grandchild()};

然后从 getter 返回的值不包含我的数据库数据,但包含我新创建的孙子。

这里发生了某种奇怪的 EF 巫毒,我不知道如何解决它。

【问题讨论】:

  • 尝试以下操作:parent_instance.Childeren.Select(x => x.Grandchilderen).SelectMany(c => c).ToList();
  • 你试过用ICollection代替List吗?
  • 我要求我的收藏可以通过索引访问,所以我不得不使用列表
  • @jdweng 我尝试了不同的 linq 查询,但得到了相同的结果。
  • 想象一个代理 getter 如下:get { var res = base.Property; if (res == null || !res.Any()) { LoadFromDatabase(ref res); } return res; }。我不知道实际的实现是怎样的,但这表明你不能假设在你的 getter 被调用之前会发生什么特别的事情,你不能期望你的 getter 结果是最终的。

标签: c# entity-framework linq


【解决方案1】:

我根据 grek40 的提示自己解决了这个问题,我能够通过确保对函数的第一次调用完成而不修改值来使事情正常进行。 (来自非 EF 代码)以确保正确链接。

通过将我的类修改为以下函数

private bool _grandchilderenloaded = false;
protected List<Grandchild> _grandchilderen;

public virtual List<Grandchild> Grandchilderen
{
    get
    {
        if (!_grandchilderenreloaded)
            {
            _grandchilderenreloaded = true;
            if (Grandchilderen == null) // Note this getter calling itself here is where the magic happens
                SpawnGrandchilderen(); // The Db value has now been loaded, if it was non null do some setup.
            }
        return _grandchilderen;
    }
    set { _grandchilderen= value; }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-14
    相关资源
    最近更新 更多