EF实现可扩展性菜单
EF搭建可扩展菜单
由于要做一个三级菜单存贮菜单和文章,由于菜单在很多地方用的到,于是想做一个可扩展性的菜单以便以后使用。
由于以前从来没有做过动态的菜单,所以走了很多弯路,尤其搭配EF Code First更是坑了我一把,我想把我碰到坑给大家分享一下。
1.类库的实现
首先我选择树这个数据结构来存贮我的菜单,我定义菜单Menus来作为一个最小单元,定义一个bool
类型IsFoot来定义是否为根菜单,每个Menus有一个父级菜单Menus,有一群子菜单,下面是我定义的Menu库。
[Description("菜单")]
public class Menus
{
[Key]
[Display(Name="菜单ID")]
public int MenusID { get; set; }
[Required]
[Display(Name="是否为根节点")]
public bool IsFoot { get; set; }
[Required]
[StringLength(25)]
[Display(Name="目录名字")]
public string Name { get; set; }
[Display(Name="是否删除")]
public bool IsDelete { get; set; }
[Display(Name="包含的文章")]
public virtual List<Article> articles { get; set; }
[Display(Name = "父级菜单")]
public virtual Menus fatherMenus { get; set; }
[Display(Name = "子菜单")]
public virtual List<Menus> sonList { get; set; }
}
每个Menus都包含了一个文章集合,虽然有些菜单不一定有文章但是EF可以允许我们0对多,或1对多。
2. 生成数据库
EF比较人性化的是,当我们数据库里面没有我们想要生成的表时,我们不需要多余的代码,只要当成数据库有表,像平时一样添加数据然后EF会帮我们自动在数据库里面建好表,当然你如果有相同名字的表话它报错,会提醒你数据库里面有如果想保存数据要做好数据迁移工作,数据迁移不是我们的重点,如果想了解的话,点击这里
生成的数据库包含两个表,一个Menus表,一个是Article表(PS:上面没有给出Article的类型定义,想要的可以自己写),对于这个来说,我们并没有在表里面定义外键属性,只是用来一个引用属性,引用属性是一种“虚属性”,我们通过这个属性来建立起两个对象的虚拟联系,比如说父与子,这种关系是虚拟的对于两者之间的联系是通过血缘来联系的,这个血缘是存在的,相对应就是数据库里面的外键联系,外键也可以看做是表中的一个字段,它记录了一种关系。
由于EF的智能关系,当我们Code First时,他会帮我们自动建好外键如果我们不定义的话,当我们使用EF的时候是不需要考虑外键的值的初始化,如果我们没有给他赋值EF会自动给他赋值。
讲完了EF的建立,现在就谈谈使用Code First在项目中遇到的问题。
这个问题主要出在给创建子菜单上,当我们创建子菜单时,我们用的是我们自己的类库代码进行初始化数据库,我们先得到菜单的ID然后在EF里面查询这个菜单,我们查询到这个实体,然后在菜单实体里面添加子菜单,在SaveChange()时候就报错了,EF称检测到有循环赋值的可能,让我们添加外键以避免冲突,我不记得看到的那篇博客看到有人也遇到相同的问题,如果只是普通的一对多(假如是A对1,2,3,4···),当我们给A那个新建一个5时,这个外键的位置是知道的,我们只要在5的外键位置存贮A的主键,然而当我们建立这种父级菜单时,每个菜单里面的外键可以是存贮父级的主键,也可以是子集的主键,所以EF并不能解决冲突,解决这个问题的方法有两种一种是在表中添加外键 如:
[ForeignKey("sonList")]
public int sonListMenusID{get;set;}
或者用Fluent API 在继承的方法 onModelCreate中添加
modelBuilder.Entity<Menus>().HasRequired(p=>p.sonList).WithMany(l=>l.Menus).HasForeignKey(p=>p.sonListMenusID)
通过这种创建方式当我们创建子集菜单时我们就可以成功利用EF特性帮我们自动添加上外键,以及建立好实体关系。
关于更详细的外键知识可以点击这里