EF Core:渴望加载(.Include)子类别(自我引用)

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了EF Core:渴望加载(.Include)子类别(自我引用)相关的知识,希望对你有一定的参考价值。

我们有类似的东西

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

这可以处理和处理深达4级的子类别(现在已足够,但谁知道未来)

有没有更好的方法呢?

更多信息

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

  • ID
  • ParentCategoryId < - 这具有Category.Id的外键
答案

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

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);
    }
}
另一答案

在这种特殊情况下,我认为递归属性可能是一个不错的选择。试图从记忆中做到这一点(多年),表现不会很好。没有延迟加载和没有显式加载。

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

有一点需要注意。我将虚拟添加到列表中以进行延迟加载,因为我正在“手动”加载子项,可以这么说。

以上是关于EF Core:渴望加载(.Include)子类别(自我引用)的主要内容,如果未能解决你的问题,请参考以下文章

是否必须使用 .Include 加载 EF Core 5 中的子对象?

EF Core - 为啥“无限”嵌套急切加载?

如何使用 EF Core Code-First 创建具有子类别的类别表? [复制]

EF Core 5.0.4 - 从核心 3.1 升级后,通过 Include() 的急切加载不起作用

将类别父 ID 自引用表结构映射到 EF Core 实体

EF Core - 为啥显式加载非常慢?