📜  linq 查询以在 c# 中从同一个表中获取父子数据(1)

📅  最后修改于: 2023-12-03 15:17:19.974000             🧑  作者: Mango

使用 LINQ 查询从同一个表中获取父子数据

在 C# 中使用 LINQ 查询可以非常方便地从数据库中获取数据。在本文中,我们将介绍如何使用 LINQ 查询从同一个表中获取父子数据。

场景描述

假设我们有这样一张表:

CREATE TABLE [dbo].[Category](
    [CategoryId] [int] IDENTITY(1,1) NOT NULL,
    [CategoryName] [nvarchar](50) NOT NULL,
    [ParentCategoryId] [int] NULL,
    CONSTRAINT [PK_Category] PRIMARY KEY CLUSTERED 
    (
        [CategoryId] ASC
    )
);

这个表表示一个分类,其中 ParentCategoryId 表示父分类的 CategoryId。例如,我们有一个分类结构如下:

1-食品
  2-水果
  3-蔬菜
    4-叶菜
    5-根菜

在表中,对应数据应该是这样的:

CategoryId | CategoryName | ParentCategoryId
-----------|--------------|----------------
         1 | 食品         | NULL
         2 | 水果         | 1
         3 | 蔬菜         | 1
         4 | 叶菜         | 3
         5 | 根菜         | 3

我们的目标是通过一次查询获取所有分类,包括每个分类的子分类。

实现过程
第一步:创建数据模型

首先我们需要创建数据模型,可以使用 Entity Framework 或者 Dapper 等 ORM 工具。这里我们使用 Entity Framework。

public class Category
{
    public int CategoryId { get; set; }
    public string CategoryName { get; set; }
    public int? ParentCategoryId { get; set; }

    public virtual Category ParentCategory { get; set; }
    public virtual ICollection<Category> ChildCategories { get; set; }
}

注意,我们在实体类中添加了 ParentCategoryChildCategories 两个导航属性,用于表示父分类和子分类。这里使用了 Entity Framework 的延迟加载技术,使得在查询时可以自动获取父子分类的数据。

第二步:创建 LINQ 查询

有了数据模型之后,我们就可以使用 LINQ 查询来获取数据了。首先我们需要获取根分类,即 ParentCategoryId 为 NULL 的分类。

var context = new DbContext();
var rootCategories = context.Categories
    .Where(c => c.ParentCategoryId == null)
    .ToList();

接着,我们可以递归获取每个分类的子分类。这里可以使用迭代或者递归两种方式,这里我们演示递归方式。

foreach (var category in rootCategories)
{
    category.ChildCategories = GetChildCategories(category.CategoryId);
}

List<Category> GetChildCategories(int categoryId)
{
    var childCategories = context.Categories
        .Where(c => c.ParentCategoryId == categoryId)
        .ToList();

    foreach (var category in childCategories)
    {
        category.ChildCategories = GetChildCategories(category.CategoryId);
    }

    return childCategories;
}

递归方法 GetChildCategories 会根据传入的分类 ID 获取该分类的子分类,并将子分类分别递归获取子分类。最后返回子分类列表。

第三步:输出数据

获取到数据之后,我们可以将数据输出到控制台或者 Web 页面上。这里我们使用 HTML 的无序列表(<ul><li> 标签)来表示分类,每个分类下面的子分类使用嵌套列表表示。

void RenderCategory(Category category)
{
    Console.WriteLine("<li>");
    Console.WriteLine(category.CategoryName);

    if (category.ChildCategories.Count > 0)
    {
        Console.WriteLine("<ul>");

        foreach (var childCategory in category.ChildCategories)
        {
            RenderCategory(childCategory);
        }

        Console.WriteLine("</ul>");
    }

    Console.WriteLine("</li>");
}

Console.WriteLine("<ul>");

foreach (var category in rootCategories)
{
    RenderCategory(category);
}

Console.WriteLine("</ul>");
完整代码
public class Category
{
    public int CategoryId { get; set; }
    public string CategoryName { get; set; }
    public int? ParentCategoryId { get; set; }

    public virtual Category ParentCategory { get; set; }
    public virtual ICollection<Category> ChildCategories { get; set; }
}

class Program
{
    static void Main(string[] args)
    {
        var context = new DbContext();
        var rootCategories = context.Categories
            .Where(c => c.ParentCategoryId == null)
            .ToList();

        foreach (var category in rootCategories)
        {
            category.ChildCategories = GetChildCategories(category.CategoryId);
        }

        Console.WriteLine("<ul>");

        foreach (var category in rootCategories)
        {
            RenderCategory(category);
        }

        Console.WriteLine("</ul>");
    }

    static List<Category> GetChildCategories(int categoryId)
    {
        var context = new DbContext();

        var childCategories = context.Categories
            .Where(c => c.ParentCategoryId == categoryId)
            .ToList();

        foreach (var category in childCategories)
        {
            category.ChildCategories = GetChildCategories(category.CategoryId);
        }

        return childCategories;
    }

    static void RenderCategory(Category category)
    {
        Console.WriteLine("<li>");
        Console.WriteLine(category.CategoryName);

        if (category.ChildCategories.Count > 0)
        {
            Console.WriteLine("<ul>");

            foreach (var childCategory in category.ChildCategories)
            {
                RenderCategory(childCategory);
            }

            Console.WriteLine("</ul>");
        }

        Console.WriteLine("</li>");
    }
}
结论

使用 LINQ 查询从同一个表中获取父子数据并不难,只需要使用递归的方式获取每个分类的子分类即可。这里我们使用 Entity Framework 实现了数据模型,并使用 LINQ 查询获取数据。最后使用 HTML 的列表标签将数据输出到控制台。