Loading lazy collections into entity structure is too lazy
I am having issues with an entity framework that does not correctly handle collections that I have manually initialized with multiple objects.
I am using code based on any of the many private field examples to return a public collection property. for example according to this question
However, I run into problems if I want to use this field with LINQ queries.
My model structure looks like this.
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 for each child, which overwrites the data that should have been loaded from the database.
If I put a breakpoint at the beginning of the getter, I see that _grandchilderen is always null.
I can get the code to work as expected if I put a breakpoint in front of the linq request and manually examine childeren with the clock window before continuing with execution.
I believe this is a lazy loading issue, but I am not sure how to effectively address this issue.
Edit: I was joking a bit and all I found was this:
When it first opens, the Grandchilderen roster is ALWAYS zero.
If I replace the call SpawnGrandchilderen()
with _grandchilderen = new List<Grandchild>()
and put a breakpoint on the line, I can see that the line hits, and yet my data from the database is actually being returned when I 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 does not contain any of my database data, but does include my newly created grandson.
Some weird EF voodoo is happening here and I'm not sure how to fix it.
source to share
I managed to solve this on my own based on grek40's hints. I managed to get everything to work, ensuring that the first call to the function completes without changing the value. (from non-EF code) to ensure correct connection.
changing 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; }
}
source to share