简体   繁体   中英

Loading of Lazy collections in entity framework too lazy

I am having issues with entity framework not properly handling collections that I manually initialize with a few objects.

I am using code based off any of the numerous examples of using a private field to back a public collection property. eg as per this question

However I am running into issues if I latter want to use this field with LINQ queries.

My model structure is as follows.

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; }

    ...
}

If I use a query like

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

SpawnGrandchilderen() is called on every child overwriting data that should have been loaded from the database.

If I place a breakpoint at the start of the getter, I can see that _grandchilderen is always null.

I am able to get the code to work as expected if I place a breakpoint before the linq query and manually explore childeren using the watch window before continuing execution.

I believe this is an issue to do with lazy-loading but I am not sure how to go about solving this issue efficiently.


Edit: I have done a bit more poking around and all I have found is:

When it is first accessed the Grandchilderen list is ALWAYS null.

If I replace the call SpawnGrandchilderen() with _grandchilderen = new List<Grandchild>() and put a breakpoint on the line I can see that the line is being hit and yet my data from the database is actually returned when I would expect an empty array to be returned.

However if I replace the line with

_grandchilderen = new List<Grandchild>(){new Grandchild()};

Then the value returned from the getter contains none of my database data, but does include my newly created grandchild.

Some kind of weird EF voodoo is going on here and I am not sure how to troubleshoot it.

I managed to solve this myself based on the hints from grek40 I was able to get things working by ensuring that the first call to the function finishes without modification to the value. (from non EF code) to ensure things get linked through properly.

by modifying my class to the following function

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; }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM