繁体   English   中英

如何删除自引用实体?

[英]How to delete self referencing entity?

语境

在我当前的项目中,有一个层次结构的文件夹结构保存在数据库中。 该实体如下:

public class Folder
{
    public string Id { get; set; }
    public string ParentFolderId { get; set; }
    public Folder ParentFolder { get; set; }
    public List<Folder> ChildFolders { get; set; } // Relationship property
    // Other properties
}

只有根文件夹具有 null 作为ParentFolderId 所有其他文件夹都有一个非空的ParentFolderId EFCore 不允许在ParentFolderId上设置级联删除。

问题

删除文件夹时,我也想递归删除所有子文件夹。 如何实施?

我试过的

在数据库上下文中:

protected override void OnModelCreating(ModelBuilder builder)
{
    base.OnModelCreating(builder);

    builder.Entity<Folder>()
        .HasOne(e => e.ParentFolder)
        .WithMany(e => e.ChildFolders)
        .OnDelete(DeleteBehavior.ClientCascade);
}

删除通话:

public void DeleteFolder(string folderId)
{
    var folder = await _db.Folders
        .Include(d => d.ParentFolder)
        .Include(f => f.ChildFolders)
        .FirstOrDefaultAsync(d => d.Id == folderId);

    if (folder == null) return;
    if (folder.ParentFolderId == null) return null;

    _db.Folders.Remove(folder);
    _db.SaveChanges();
}

但是,在这种情况下,我只加载层次结构中下一级的子文件夹。 我可以多次调用ThenInclude ,但不能保证到达所有子文件夹。

这是一种通过包含获取所有子文件夹的方法来实现的方法。 不利的一面是,层次结构中的每一级子文件夹都需要一个 select。 替代方法是 SQL,您可以在其中编写递归查询以获取所有要删除的 ID,然后删除它们。

public void DeleteFolder(string folderId)
{
    var folder = await _db.Folders
        .FirstOrDefaultAsync(d => d.Id == folderId);

    if (folder == null) return;

    var foldersToDelete = await SubFolders(folder.Id);
    foldersToDelete.Add(folder);

    _db.Folders.RemoveRange(foldersToDelete);
    _db.SaveChanges();
}

public async Task<List<Folder>> SubFolders(string folderId)
{
    var subFolders = await _db.Folders
        .Where(d => d.ParentFolderId == folderId)
        .ToListAsync();

    var allFolders = new List<Folder>();
    foreach(var subFolder in subFolders)
    {
        allFolders.Add(subFolder);
        allFolders.AddRange(await SubFolders(subFolder.Id));
    }

    return allFolders;
}

如果文件夹中有任何循环,请注意永远运行(例如文件夹 A 的父级是文件夹 B,文件夹 B 的父级是文件夹 A)

我最终稍微改变了@juharr 的回答。 我没有创建一堆列表,而是创建一个列表并在递归 function 调用之间传递它的引用。

这是我的最终代码:

public void DeleteFolder(string userId, string folderId)
{
    var folder = await _db.Folders
        .Include(d => d.ParentFolder)
        .Include(f => f.ChildFolders)
        .FirstOrDefaultAsync(d => d.Id == folderId);

    if (folder == null) return;
    if (folder.ParentFolderId == null) return;

    var foldersToBeDeleted = new List<Folder>();

    folder.ChildFolders.ForEach(childFolder => PopulateSubFolder(childFolder.Id, foldersToBeDeleted));
    foldersToBeDeleted.Add(folder);

    _db.Folders.RemoveRange(foldersToBeDeleted);
    _db.SaveChanges();
}

private void PopulateSubFolder(string folderId, ICollection<Folder> foldersToDelete)
{
    var folder = _db.Folders
        .Include(f => f.ChildFolders)
        .FirstOrDefault(f => f.Id == folderId);

    if (folder == null) return;

    // Add child folders
    folder.ChildFolders.ForEach(subFolder => PopulateSubFolder(subFolder.Id, foldersToDelete));

    // Add current folder
    foldersToDelete.Add(folder);
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM