[英]How to delete self referencing entity?
In my current project, there is a hierarchical folder structure saved in a database.在我当前的项目中,有一个层次结构的文件夹结构保存在数据库中。 The entity is the following:
该实体如下:
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
}
Only the root folder has null as ParentFolderId
.只有根文件夹具有 null 作为
ParentFolderId
。 All other folders have a non-null ParentFolderId
.所有其他文件夹都有一个非空的
ParentFolderId
。 EFCore doesn't allow to set up cascade delete on ParentFolderId
. EFCore 不允许在
ParentFolderId
上设置级联删除。
When a folder is deleted, I would like to delete all subfolders recursively as well.删除文件夹时,我也想递归删除所有子文件夹。 How can this be implemented?
如何实施?
In DbContext:在数据库上下文中:
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<Folder>()
.HasOne(e => e.ParentFolder)
.WithMany(e => e.ChildFolders)
.OnDelete(DeleteBehavior.ClientCascade);
}
Delete call:删除通话:
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();
}
However, in this situation, I am only loading subfolders one level below in the hierarchy.但是,在这种情况下,我只加载层次结构中下一级的子文件夹。 I can call
ThenInclude
a bunch of times but cannot guarantee to have reached all subfolders.我可以多次调用
ThenInclude
,但不能保证到达所有子文件夹。
Here's a way to do it by including a method to get all the sub-folders.这是一种通过包含获取所有子文件夹的方法来实现的方法。 The down side is this requires one select for each level of subfolders in the hierarchy.
不利的一面是,层次结构中的每一级子文件夹都需要一个 select。 The alternative would be SQL where you can write a recursive query to get all the ids to delete and then delete them.
替代方法是 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;
}
Note this with run forever if you have any cycles in your folders (like folder A's parent is folder B and folder B's parent is folder A)如果文件夹中有任何循环,请注意永远运行(例如文件夹 A 的父级是文件夹 B,文件夹 B 的父级是文件夹 A)
I ended up changing @juharr's answer a little bit.我最终稍微改变了@juharr 的回答。 Instead of creating a bunch of lists, I create one list and pass its reference among the recursive function calls.
我没有创建一堆列表,而是创建一个列表并在递归 function 调用之间传递它的引用。
Here is my final code:这是我的最终代码:
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.