EF Core: Eager loading (.Include) sub-categories (self-reference)

本秂侑毒 提交于 2020-02-06 10:44:34

问题


We have something like this

var categories = _context.Categories.Include("Categories1.Categories1.Categories1");

That works and handles sub-categories up to 4-level deep (which is enough for now but who knows the future)

Is there a better way to do it?

More info

We use database-first. Category table has these columns:

  • Id
  • ParentCategoryId <-- this has foreign key to Category.Id

回答1:


In this particular case I think a recursive property might be a good bet. Trying to do this from memory (been years), performance won't be great. NO lazy loading and NO explicit loading.

public class Category {
    public int Id {get; set;}
    // other stuff
    public List<Category> MyChildren {
        get { return _context.Categories.Where(x => x.ParentCategoryId == Id).ToList<Category>(); }
    } 
}

This should give you a hierarchical graph starting with a given node.

var justOne = _context.Categories.FirstOrDefault(x => x.Id = <myval>);

Downside is you will have to parse/use the result recursively and potentially it grows exponentially.

Clarification: The use of _context in the recursion is not allowed by EF and was used for illustration purposes. In the repository/UoW or business layer you could use the same technique to "assemble" the finished entity by having a property of a method that calls the method recursively.

Just for fun, here's the same recursion technique (but not as a property, don't have time right now).

public class Category       // EF entity
{
    public int Id { get; set; }
    public int ParentId { get; set; }
    public virtual List<Category> MyChildren { get; set; }
}
public static class MVVMCategory
{
    public static Category GetCategory(int id)
    {
        Category result = _context.Categories.FirstOrDefault(x => x.Id == id);

        result.MyChildren = GetChildren(id);

        return result;
    }

    public static List<Category> GetChildren(int id)
    {
        List<Category> result = _context.Categories.Where(x => x.ParentId == id).ToList<Category>();
        foreach (var item in result)
        {
            item.MyChildren = GetChildren(item.Id);
        }
        return result;
    }
}

One thing to notice. I added virtual to the list for lazy loading because I am loading children "by hand" so to speak.




回答2:


Firstly, add data annotations and make properties readable

public partial class Category
{

    public Category()
    {
        this.Children = new HashSet<Category>();
    }

    [Key]
    public int Id { get; set; }

    public string WhatEverProperties { get; set; }

    public int ParentCategoryId { get; set; }


    [ForeignKey("ParentCategoryId")]
    [InverseProperty("Category")]
    public Category Parent { get; set; } // name "Category1" as "Parent"

    [InverseProperty("Category")]
    public ICollection<Category> Children { get; set; } // Name it as Children
}

then, let's say we have got a category,

var category = context.Categories
    .Include(x => x.Parent)
    .Include(x => x.Children)
    .FirstOrDefault(filter);

then we get its parents with:

var rootCategory = category.Parent?.Parent?.Parent; //up-to 4 levels by your request

by following extension, we can easily get its level:

///this extension works only if you used `.Include(x => x.Parent)` from query
public static class CategoryExtensions
{
    public static int Level(this Category category)
    {
        if (category.Parent == null)
        {
            return 0;
        }

        return category.Parent.Level() + 1;
    }
}


来源:https://stackoverflow.com/questions/51395852/ef-core-eager-loading-include-sub-categories-self-reference

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!