You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

EFCore中如何删除自引用实体并递归删除所有层级的子文件夹?

问题原因说明

你之前配置的DeleteBehavior.ClientCascade仅会对已经加载到当前DbContext上下文中的关联实体执行级联删除,你只Include了一级ChildFolders,所以上下文里只有一级子文件夹,自然只能删除一级,就算多次调用ThenInclude也只能覆盖固定层级,无法适配无限层级的场景。

解决方案

方案1:递归加载所有后代后批量删除(纯EF Core实现)

该方案不需要写原生SQL,可适配所有EF Core支持的数据库,适合文件夹层级不深、数据量不大的场景。

首先实现递归获取所有后代文件夹ID的辅助方法:

private async Task<List<string>> GetAllDescendantFolderIds(string parentId)
{
    // 先查当前父级的直接子文件夹ID
    var childIds = await _db.Folders
        .Where(f => f.ParentFolderId == parentId)
        .Select(f => f.Id)
        .ToListAsync();
    
    var allDescendantIds = new List<string>(childIds);
    // 递归查询每个子文件夹的后代
    foreach (var childId in childIds)
    {
        allDescendantIds.AddRange(await GetAllDescendantFolderIds(childId));
    }
    return allDescendantIds;
}

修改删除方法如下:

public async Task DeleteFolder(string folderId)
{
    var folder = await _db.Folders.FindAsync(folderId);
    // 文件夹不存在或为根目录时直接返回
    if (folder == null || folder.ParentFolderId == null) return;

    // 获取所有后代文件夹ID,再把当前文件夹加入删除列表
    var allToDeleteIds = await GetAllDescendantFolderIds(folderId);
    allToDeleteIds.Add(folderId);

    // 查询所有要删除的文件夹实体,批量标记删除
    var allToDeleteFolders = await _db.Folders
        .Where(f => allToDeleteIds.Contains(f.Id))
        .ToListAsync();
    
    _db.Folders.RemoveRange(allToDeleteFolders);
    await _db.SaveChangesAsync();
}

方案2:数据库端递归CTE删除(高性能实现)

该方案直接在数据库层执行递归删除,无需将实体加载到内存,性能极高,适合数据量大、层级深的场景,仅需要根据你使用的数据库调整CTE语法即可,以下是SQL Server的示例:

public async Task DeleteFolder(string folderId)
{
    var folder = await _db.Folders.FindAsync(folderId);
    if (folder == null || folder.ParentFolderId == null) return;

    // 递归CTE查询所有要删除的文件夹ID,直接执行删除
    var deleteSql = @"
WITH RecursiveFolders AS (
    SELECT Id FROM Folders WHERE Id = @FolderId
    UNION ALL
    SELECT f.Id FROM Folders f
    INNER JOIN RecursiveFolders rf ON f.ParentFolderId = rf.Id
)
DELETE FROM Folders WHERE Id IN (SELECT Id FROM RecursiveFolders)
";

    await _db.Database.ExecuteSqlInterpolatedAsync($"EXEC {deleteSql} @FolderId = {folderId}");
}

内容的提问来源于stack exchange,提问作者M. Akar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.01 16:15:03