Offir
Offir

Reputation: 3491

EF core - parent.InverseParent returns null for some rows

I have a Category table and it has a Parent Category, I try to iterate over all the categories and get the parents categories with it's Inverse Parent but some of them returns without the inverse parents from unknown reason.

Categories.cs

 public partial class Categories
{
    public Categories()
    {
        InverseParent = new HashSet<Categories>();   
    }

    public int Id { get; set; }
    public int? ParentId { get; set; }
    public DateTime CreateDate { get; set; }
    public bool? Status { get; set; }

    public virtual Categories Parent { get; set; }

    public virtual ICollection<Categories> InverseParent { get; set; }

}

This is how I try to iterate them to create a select list items:

  var parentCategories = await _context.Categories.
                                                      Include(x => x.Parent).
                                                      Where(x => x.Status == true).
                                                      Where(x => x.Parent != null).
                                                      Select(x => x.Parent).
                                                      Distinct().
                                                      ToListAsync();

                foreach (var parent in parentCategories)
                {
                    SelectListGroup group = new SelectListGroup() { Name = parent.Id.ToString() };
                    foreach (var category in parent.InverseParent)
                    {
                        categories.Add(new SelectListItem { Text = category.Id.ToString(), Value = category.Id.ToString(), Group = group });
                    }
                }

So the problem is that some of my parent categories returns all their children categories and some don't and I don't why.

Upvotes: 1

Views: 922

Answers (2)

Ivan Stoev
Ivan Stoev

Reputation: 205609

There are several issues with that code, all having some explaination in the Loading Related Data section of the documentation.

First, you didn't ask EF Core to include InverseParent, so it's more logically to expect it to be always null.

What you get is a result of the following Eager Loading behavior:

Tip

Entity Framework Core will automatically fix-up navigation properties to any other entities that were previously loaded into the context instance. So even if you don't explicitly include the data for a navigation property, the property may still be populated if some or all of the related entities were previously loaded.

Second, since the query is changing it's initial shape (Select, Disctinct), it's falling into Ignored Includes category.

With that being said, you should build the query other way around - starting directly with parent categories and including InverseParent:

var parentCategories = await _context.Categories
    .Include(x => x.InverseParent)
    .Where(x => x.InverseParent.Any(c => c.Status == true)) // to match your query filter
    .ToListAsync();

Upvotes: 2

Sergii Kudriavtsev
Sergii Kudriavtsev

Reputation: 10512

While you are including Include(x => x.Parent), you don't seem to do the same for InverseParent. This might affect your results exactly the way you describe. Would including it fix it?

parentCategories = await _context.Categories.
                                                      Include(x => x.Parent).
                                                      Include(x => x.InverseParent).
                                                      Where(x => x.Status == true).
                                                      Where(x => x.Parent != null).
                                                      Select(x => x.Parent).
                                                      Distinct().
                                                      ToListAsync();

                foreach (var parent in parentCategories)
                {
                    SelectListGroup group = new SelectListGroup() { Name = parent.Id.ToString() };
                    foreach (var category in parent.InverseParent)
                    {
                        categories.Add(new SelectListItem { Text = category.Id.ToString(), Value = category.Id.ToString(), Group = group });
                    }
                }

UPD: Since you are selecting x => x.Parent anyway it might be necessary to use ThenInclude() method instead.

Upvotes: 0

Related Questions