EF核心:急切加载(.include)子类别(自我参考)

时间:2018-07-18 07:18:50

标签: entity-framework entity-framework-core

我们有这样的东西

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

该方法可以处理并处理多达4级深度的子​​类别(对于现在来说已经足够了,但谁知道未来)

有更好的方法吗?

更多信息

我们首先使用数据库。类别表包含以下列:

  • 编号
  • ParentCategoryId <-这具有Category.Id的外键

2 个答案:

答案 0 :(得分:1)

在这种情况下,我认为递归属性可能是一个不错的选择。试图从内存中做到这一点(几年后),性能将不会很好。没有延迟加载,也没有显式加载。

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>(); }
    } 
}

这应该给您一个从给定节点开始的层次图。

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

缺点是您将不得不递归地解析/使用结果,并且可能以指数形式增长。

说明:EF不允许在递归中使用_context,它仅用于说明目的。在存储库/ UoW或业务层中,可以通过具有递归调用方法的方法的属性,使用相同的技术来“组装”完成的实体。

只是出于娱乐目的,这是相同的递归技术(但不是作为属性,现在没有时间)。

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;
    }
}

需要注意的一件事。我将virtual添加到列表中以进行延迟加载,因为可以说可以“手动”加载孩子。

答案 1 :(得分:1)

首先,添加数据注释并使您的属性可读

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
}

然后,您不需要任何.Include(...)语法即可显式加载父对象,它将与功能中的任何级别一起使用。假设您有一个类别,然后您将其父类别带有:

category.Parent?.Parent?.Parent....

如果您需要获得等级,请使用扩展名:

public static class CategoryExtensions
{
    public static int Level(this Category category)
    {
        if (category.Parent == null)
        {
            return 0;
        }

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

}

还有一件事情:以下部分应该已经在您的业务规则层中,以避免加载不必要的对象。

public class CategoryBLLWhatever
{
    var context = new YourContext();
    public IQueryable<Category> RootCategories()
    {
        return context.Categories.Where(x => x.Paerent == null).Include(x => x.Children);
    }

    //Include parent in your query if you need it
    public IQueryable<Category> WhateverQuery(Expression<Func<Category, bool>> filter)
    {
        return context.Categories.Where(filter).Include(x => x.Parent);
    }
}