EF Core의 트리 구조: 자체 참조 테이블을 구성하고 사용하는 방법입니다.
EF Core의 트리 구조: 자체 참조 테이블을 구성하고 사용하는 방법입니다.
.NET 커뮤니티에서 받는 매우 일반적인 질문 중 하나는 EF Core에서 트리 구조를 구성하고 사용하는 방법입니다. 이 이야기는 가능한 방법 중 하나입니다.
일반적인 트리 구조는 파일 트리, 범주 계층 구조 등입니다. 예를 들어 폴더 트리라고 합시다. 엔터티 클래스는 폴더가 됩니다.
public class Folder
{
public Guid Id { get; set; }
public string Name { get; set; }
public Folder Parent { get; set; }
public Guid? ParentId { get; set; }
public ICollection<Folder> SubFolders { get; } = new List<Folder>();
}
DbContext 클래스의 OnModelCreating 메서드를 재정의하여 DB 스키마를 구성하는 방법입니다. 이는 엔터티 클래스의 구성 속성 특성을 통해 수행할 수 있지만 이 방법으로 DB 스키마를 정의하는 것을 선호합니다.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Folder>(entity =>
{
entity.HasKey(x => x.Id);
entity.Property(x=> x.Name);
entity.HasOne(x=> x.Parent)
.WithMany(x=> x.SubFolders)
.HasForeignKey(x=> x.ParentId)
.IsRequired(false)
.OnDelete(DeleteBehavior.Restrict);
});
// ...
}
다음은 DB에서 폴더 트리로 데이터를 로드하고 일반 폴더 "노드"목록으로 평면화하고 노드 수준, 노드 부모 등과 같은 트리 구조와 관련된 세부 정보를 얻는 방법입니다.
{
List<Folder> all = \_dbContext.Folders.Include(x => x.Parent).ToList();
TreeExtensions.ITree<Folder> virtualRootNode = all.ToTree((parent, child) => child.ParentId == parent.Id);
List<TreeExtensions.ITree<Folder>> rootLevelFoldersWithSubTree = virtualRootNode.Children.ToList();
List<TreeExtensions.ITree<Folder>> flattenedListOfFolderNodes = virtualRootNode.Children.Flatten(node => node.Children).ToList();
// Each Folder entity can be retrieved via node.Data property:
TreeExtensions.ITree<Folder> folderNode = flattenedListOfFolderNodes.First(node => node.Data.Name == "MyFolder");
Folder folder = folderNode.Data;
int level = folderNode.Level;
bool isLeaf = folderNode.IsLeaf;
bool isRoot = folderNode.IsRoot;
ICollection<TreeExtensions.ITree<Folder>> children = folderNode.Children;
TreeExtensions.ITree<Folder> parent = folderNode.Parent;
List<Folder> parents = GetParents(folderNode);
}
이 메서드는 특정 노드에 대한 트리에서 모든 부모를 가져오는 방법을 보여줍니다.
private static List<T> GetParents<T>(TreeExtensions.ITree<T> node, List<T> parentNodes = null) where T : class
{
while (true)
{
parentNodes ??= new List<T>();
if (node?.Parent?.Data == null) return parentNodes;
parentNodes.Add(node.Parent.Data);
node = node.Parent;
}
}
아래의 트리 작업 확장 메서드 및 모든 엔터티를 트리 노드 개체로 래핑하기 위한 도우미 인터페이스입니다. 따라서 기술적으로 엔티티는 parent->children(Flatten을 사용하여 트리에서 노드의 일반 목록을 가져오기 위해) 및 child->parent(ToTreemethod를 사용하여 목록에서 트리를 빌드하기 위해) 관계가 있는 모든 클래스가 될 수 있습니다.
public static class TreeExtensions
{
/// <summary> Generic interface for tree node structure </summary>
/// <typeparam name="T"></typeparam>
public interface ITree<T>
{
T Data { get; }
ITree<T> Parent { get; }
ICollection<ITree<T>> Children { get; }
bool IsRoot { get; }
bool IsLeaf { get; }
int Level { get; }
}
/// <summary> Flatten tree to plain list of nodes </summary>
public static IEnumerable<TNode> Flatten<TNode>(this IEnumerable<TNode> nodes, Func<TNode, IEnumerable<TNode>> childrenSelector)
{
if (nodes == null) throw new ArgumentNullException(nameof(nodes));
return nodes.SelectMany(c => childrenSelector(c).Flatten(childrenSelector)).Concat(nodes);
}
/// <summary> Converts given list to tree. </summary>
/// <typeparam name="T">Custom data type to associate with tree node.</typeparam>
/// <param name="items">The collection items.</param>
/// <param name="parentSelector">Expression to select parent.</param>
public static ITree<T> ToTree<T>(this IList<T> items, Func<T, T, bool> parentSelector)
{
if (items == null) throw new ArgumentNullException(nameof(items));
var lookup = items.ToLookup(item => items.FirstOrDefault(parent => parentSelector(parent, item)),
child => child);
return Tree<T>.FromLookup(lookup);
}
/// <summary> Internal implementation of <see cref="ITree{T}" /></summary>
/// <typeparam name="T">Custom data type to associate with tree node.</typeparam>
internal class Tree<T> : ITree<T>
{
public T Data { get; }
public ITree<T> Parent { get; private set; }
public ICollection<ITree<T>> Children { get; }
public bool IsRoot => Parent == null;
public bool IsLeaf => Children.Count == 0;
public int Level => IsRoot ? 0 : Parent.Level + 1;
private Tree(T data)
{
Children = new LinkedList<ITree<T>>();
Data = data;
}
public static Tree<T> FromLookup(ILookup<T, T> lookup)
{
var rootData = lookup.Count == 1 ? lookup.First().Key : default(T);
var root = new Tree<T>(rootData);
root.LoadChildren(lookup);
return root;
}
private void LoadChildren(ILookup<T, T> lookup)
{
foreach (var data in lookup[Data])
{
var child = new Tree<T>(data) {Parent = this};
Children.Add(child);
child.LoadChildren(lookup);
}
}
}
}
도움이 되길 바랍니다. Coding Machine로 코딩을 즐겨보세요…
Reference
이 문제에 관하여(EF Core의 트리 구조: 자체 참조 테이블을 구성하고 사용하는 방법입니다.), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/dr_dimaka/tree-structure-in-ef-core-how-to-configure-a-self-referencing-table-and-use-it-122m텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)