EF基础知识小记六(使用Code First建模自引用关系,常用于系统菜单文件目录等有层级之分的实体)
Posted Green.Leaf
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了EF基础知识小记六(使用Code First建模自引用关系,常用于系统菜单文件目录等有层级之分的实体)相关的知识,希望对你有一定的参考价值。
日常开发中,经常会碰到一些自引用的实体,比如系统菜单、目录实体,这类实体往往自己引用自己,所以我们必须学会使用Code First来建立这一类的模型.
以下是自引用表的数据库关系图:
ok,下面开始介绍从零创建一个Code First版的自引用模型.
1、往目标项目中添加EF包,通过NuGet程序包添加
导入相关的程序集.
2、创建自引用实体类
public class Category { [Key] [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int CategoryId { get; private set; } public string Name { get; set; } public int? ParentCategoryId { get; private set; } [ForeignKey("ParentCategoryId")] public virtual Category ParentCategory { get; set; } public virtual List<Category> Subcategories { get; set; } public Category() { Subcategories = new List<Category>(); } }
3、创建一个数据库上下文,该上下文必须继承DbContext,代码如下:
public class EF6RecipesContext : DbContext { public DbSet<Category> Categories { get; set; } public EF6RecipesContext() : base("name=EF6RecipeEntities") { } protected override void OnModelCreating(DbModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); modelBuilder.Entity<Category>().HasMany(cat => cat.Subcategories).WithOptional(cat => cat.ParentCategory); } }
4、截至这一步,分析下代码,典型的目录实体,从实体类可以看出该实体拥有单个父类型、子类型集合,这里比较特殊的是,这里的父类型和子类型都是自己,也就是自引用.注意:一个没有付类型的实体,该实体就是整个继承类型的最顶端.
5、编写测试代码:
static void Main(string[] args) { Example(); } static void Example() { using (var context = new EF6RecipesContext()) { var first = new Category { Name = "第一级菜单" }; var second = new Category { Name = "第二级菜单" }; first.Subcategories.Add(second); second = new Category { Name = "第二级菜单" }; first.Subcategories.Add(second); second = new Category { Name = "第二级菜单" }; first.Subcategories.Add(second); var top = new Category { Name = "顶级菜单" }; top.Subcategories.Add(first); context.Categories.Add(top); context.SaveChanges(); } using (var context = new EF6RecipesContext()) { var roots = context.Categories.Where(c => c.ParentCategory == null); roots.ToList().ForEach(root => Print(root, 0)); } Console.ReadKey(); } static void Print(Category cat, int level) { StringBuilder sb = new StringBuilder(); Console.WriteLine("{0}{1}", sb.Append(\' \', level).ToString(), cat.Name); cat.Subcategories.ForEach(child => Print(child, level + 1));//递归,直到最后遍历的节点没有子节点集合,则跳出递归循环 }
简单解释下测试代码的逻辑:
(1)、从所有的节点中获取没有父节点的节点,该节点为顶级节点
(2)、然后通过递归将该顶级节点下面的所有的子节点全部遍历出来,每当递归到的节点含有子节点集合,则递归的深度加1.当一个继承链遍历完毕,继续遍历第二个继承链.
以上是关于EF基础知识小记六(使用Code First建模自引用关系,常用于系统菜单文件目录等有层级之分的实体)的主要内容,如果未能解决你的问题,请参考以下文章
EF,Code First - 如何在插入时设置自定义Guid标识值
如何在 EF Core Code First 中自定义迁移生成?