Reputation:
I've model Category
that has a self-referencing relation:
public class Category
{
public Category()
{
this.Childs = new HashSet<Category>();
}
public int CategoryId { get; set; }
public int? ParentId { get; set; }
public virtual Category Parent { get; set; }
public HashSet<Category> Childs { get; set; }
//... other useless properties
}
Relation configuration is defined in context:
modelBuilder.Entity<Category>()
.HasOptional(c => c.Parent)
.WithMany(c => c.Childs)
.HasForeignKey(m => m.ParentId);
I've also generic Repository
that includes methods:
public IEnumerable<T> GetAll()
{
return this.dbSet.ToList();
}
public IQueryable<T> AsQuerable()
{
return this.dbSet.AsQueryable();
}
And now very strange behaviour occurs. I call a method:
var categoriesEntites =
this.catogries.AsQuerable()
.Where(cat => cat.ProjectId == projectId
&& cat.ParentId == null)
.ToList();
// this.categories is of type `Repository<Category>`
In the result, Childs
are not loaded..:
But when I call GetAll
before:
this.catogries.GetAll();
var categoriesEntites =
this.catogries.AsQuerable()
.Where(cat => cat.ProjectId == projectId
&& cat.ParentId == null)
.ToList();
Suddenly navigation properties works..
I've used such an approach many times before, and I have never had such a problem..
Some ideas? LazyLoading
is turned on of course..
To make things more strange, I've other self-referencing entity (that is build in exactly the same way) and there is no such a bug.
BTW. Sorry for typo mistake in nav property, it should be Children
of course, not Childs
...
Upvotes: 1
Views: 165
Reputation: 7135
When you do ToList()
in your GetAll()
method, EF loads the entire table and will store those entities locally. This is why they appear loaded when you perform this query:
var categoriesEntites =
this.catogries.AsQuerable()
.Where(cat => cat.ParentId == null)
.ToList();
If you look at the generated SQL for the above query, it actually only fetches categories where ParentId
is null:
SELECT
[Extent1].[CategoryId] AS [CategoryId],
[Extent1].[ParentId] AS [ParentId]
FROM [dbo].[Categories] AS [Extent1]
WHERE [Extent1].[ParentId] IS NULL
The child collection isn't fetched even after inspecting the collection in VS debugger (triggers lazy loading) because its already loaded and stored locally by EF.
If you want to get child objects without using ToList()
( or GetAll()
), use the virtual
keyword on your child collection. This is a requirement for lazy loading and will fetch when that child collection is accessed, by code or VS debugger.
public virtual HashSet<Category> Childs { get; set; }
Upvotes: 1