ef 更新数据库表结构
One of the very common questions I am getting from .NET community is how to configure and use the tree structures in EF Core. This story is one of the possible ways to do it.
我从.NET社区获得的一个非常常见的问题之一是如何在EF Core配置和使用树结构。 这个故事是实现它的可能方法之一。
The common tree structures are file tree, categories hierarchy, and so on. Let it be folders tree for example. The entity class will be a Folder:
常见的树结构是文件树,类别层次结构等。 例如,让它成为文件夹树。 实体类将是Folder :
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>();
}
This is how to configure DB schema via overriding OnModelCreating method of your DbContext class. This could be done via configuration property attributes on our entity class, but I prefer to define DB schema this way.
这是通过覆盖DbContext类的OnModelCreating方法配置数据库架构的方法。 这可以通过我们实体类上的配置属性属性来完成,但是我更喜欢以这种方式定义数据库模式。
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);
});
// ...
}
This is how to load data from DB as a tree of folders, flatten it as a plain list of folder “nodes”, and get some details related to tree structure like node level, node parents, etc.:
这是如何从数据库中将数据作为文件夹树加载,将其展平为文件夹“节点”的简单列表,以及获取与树结构相关的一些详细信息,例如节点级别,节点父级等:
{
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);
}
This method demonstrates how to get all parents from the tree for specific node:
此方法演示如何从树中为特定节点获取所有父级:
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;
}
}
Tree operation extension methods below and helper interface for wrapping any entity into the tree node object. So technically your entity can be any class where you have relation parent->children (to get the plain list of nodes from the tree using Flatten) and child-> parent (to build the tree from the list using ToTree method):
下面的树操作扩展方法和帮助程序接口,用于将任何实体包装到树节点对象中。 因此,从技术上讲,您的实体可以是您具有以下关系的任何类: parent->children (使用Flatten从树中获得节点的纯列表)和child-> parent (使用ToTree方法从列表中构建树):
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);
}
}
}
}
Hope that helps. Enjoy coding with the Coding Machine…
ef 更新数据库表结构
本文介绍了如何在EF Core中配置和使用树结构,以处理如文件树、类别层次结构等常见场景。通过覆盖DbContext类的方法来定义数据库模式,并展示了如何加载、展平数据,获取节点详情,以及进行树操作的方法。
1738

被折叠的 条评论
为什么被折叠?



