在实体框架中加载Lazy集合太懒了

时间:2017-04-18 15:08:12

标签: c# entity-framework linq

我遇到的问题是实体框架没有正确处理我用几个对象手动初始化的集合。

我使用的代码基于使用私有字段来支持公共集合属性的众多示例。例如按照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总是为null。

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

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

修改 我做了一些更多的探索,我发现的是:

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

如果我用SpawnGrandchilderen()替换调用_grandchilderen = new List<Grandchild>()并在行上放置一个断点,我可以看到该行被命中,但是当我期望要返回的空数组。

但是,如果我用

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

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

某种奇怪的EF伏都教正在这里进行,我不知道如何解决它。

1 个答案:

答案 0 :(得分:0)

我设法根据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; }
}