Reputation: 7829
I have this model:
public class Person
{
public string Name { get; set; }
public Person Parent { get; set; }
public virtual ICollection<Person> Children { get; set; }
}
My question: how do I return all children, no matter how deep they are? For example, I could have a Person Alice
, who has a child Bob
, who in turn has a child Charlie
. It's possible that this chain of parents and children goes on for 10 more Persons. Without knowing beforehand how many children Alice has, how do I return all of them in a query? I'm currently doing this:
using (var ctx = new PersonContext())
{
var result = ctx.People
.Include(x => x.Parent)
.Include(x => x.Children)
.FirstOrDefault();
return result;
}
But this doesn't seem to load Charlie
. Using ctx.Configuration.ProxyCreationEnabled = false;
also does not seem to make a difference.
Upvotes: 1
Views: 1393
Reputation: 28272
Loading grand-children is not possible unless you know exactly the amount of children. So you either lazy-load them, or you can explicitly load them recursively... something like:
private void LoadChildren(DbContext context, Person person)
{
var childCollection = context.Entry(person).Collection(x => x.Children);
if(!childCollection.IsLoaded)
{
childCollection.Load();
foreach(var child in person.Children)
LoadChildren(context, child);
}
}
/* ... */
using (var ctx = new PersonContext())
{
var result = ctx.People
.Include(x => x.Parent)
.FirstOrDefault();
LoadChildren(ctx, result);
return result;
}
If you are ok with an approximation (having a "maximum" amount of grand children), I think you should be able to do this using the string format of Include
(totally untested though, just posted this for you to experiment):
/* ... */
var numOfGrandChildren = 30; // 30 maximum grandchildren
string childInclude = "";
for(var i = 0; i < numOfChildren; i++)
{
if(childInclude != "") childInclude+=".";
childInclude+="Children";
}
using (var ctx = new PersonContext())
{
var result = ctx.People
.Include(x => x.Parent)
.Include(childInclude)
.FirstOrDefault();
return result;
}
Upvotes: 3
Reputation: 6959
You can disable lazy loading for a navigation property by making it non-virtual: https://msdn.microsoft.com/en-au/data/jj574232.aspx#lazyOffProperty
So your class would be:
public class Person
{
public string Name { get; set; }
public Person Parent { get; set; }
public ICollection<Person> Children { get; set; }
}
You can disable lazy loading for an entire context through configuration: https://msdn.microsoft.com/en-au/data/jj574232.aspx#lazyOff
public class YourContext : DbContext
{
public YourContext()
{
this.Configuration.LazyLoadingEnabled = false;
}
}
Upvotes: 1
Reputation: 15005
try this code
first get all people then get the one you want.
using (var ctx = new PersonContext())
{
var people = ctx.People.ToList();
var result = People.Where(P=>P.Name == "Alice").FirstOrDefault();
return result;
}
Upvotes: 0