zoukankan      html  css  js  c++  java
  • 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.当一个继承链遍历完毕,继续遍历第二个继承链.

  • 相关阅读:
    Hierarchy Query (Connect by) and ORA600 ([kkqcbydrv:1])
    The Steps to Create a New Oracle Database
    Change Schema Name (II)
    [转]The differences between V$UNDOSTAT and V$ROLLSTAT
    【Oracle Mgmt】Oracle Character Semantics (NLS_LENGTH_SEMANTICS) and etc...
    [Oracle Mgmt]About Oracle Password File
    Show parameter & Table Not exists
    RMAN Recovery Window and Redundancy Concept
    [PLSQL]Are you sure it will be definitely random? (DBMS_RANDOM.SEED)
    IOT, Secondary Index and Mapping Table
  • 原文地址:https://www.cnblogs.com/GreenLeaves/p/7651407.html
Copyright © 2011-2022 走看看