簡體   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