实体框架核心:加载完整层次结构

问题描述

一个自引用表,它的ParentId属性保存着父记录的ID,我该怎么做,以便使用ef将其子级加载到每个父项中。

我想要的是转换此cte,它将返回完整的层次结构作为一个集合。

var queryString = @"
        ;WITH cte AS (
            SELECT * FROM [dbo].[Folders] _f WHERE _f.[Id] = @id

            UNION ALL

            SELECT _c.* FROM [dbo].[Folders] _c
            INNER JOIN cte _cte
            ON _cte.[Id] = _c.[ParentFolderId]
        )

        SELECT * FROM cte";


return await this.Entities.Fromsql(new RawsqlString(queryString),new sqlParameter("id",id)).ToListAsync();

通过某种方式将孩子的层次结构加载到他们的父母中,同时保持一次db访问的性能

class Folder
{ 
     public int Id { get; set; }
     public int? FolderId { get; set; }
     public Folder Folder { get; set; }
     public IEnumerable<Folder> Children { get; set; }
}

层次结构示例

- Main (Id: 1 / ParentId: null) 
     - C1 (2/1) 
           - C11 (4/2) 
                  - C111 (7/4)
           - C12 (5/2)
     - C2 (3/1) 
           - C21 (6/3) 
                  - C211 (8/6)

已配置的关系

builder.Ignore(prop => prop.Folder);
builder.HasOne(prop => prop.Folder).WithMany(prop => prop.Children).HasForeignKey(fk => fk.FolderId);

解决方法

如果您希望在一个查询中使用整个层次结构,那很容易。只需检索所有文件夹,如果启用了更改跟踪,EF将修复所有关系。 IE(如果您刚刚运行)

var folders = db.Set<Folder>().ToList();

您将拥有填充所有导航属性的整个层次。

,

就这样。

cte

var rawSqlQuery= @"
    ;WITH cte AS (
        SELECT * FROM [dbo].[Folders] _f WHERE _f.[Id] = @id
        UNION ALL
        SELECT _c.* FROM [dbo].[Folders] _c
        INNER JOIN cte _cte
        ON _cte.[Id] = _c.[ParentFolderId]
    )
    SELECT * FROM cte";

然后使用LINQ进行过滤。

(await this.Entities.FromSql(
    new RawSqlString(rawSqlQuery),new SqlParameter("@id",id))
 .ToListAsync())
 .SingleOrDefault(f => f.Id == id);