EF CodeFirst系列(6)---配置1对1,1对多,多对多关系

匿名 (未验证) 提交于 2019-12-02 22:06:11

这一节介绍EF CodeFirst模式中的1对0/1,1对多,多对多关系的配置,只有梳理清楚实体间的关系,才能进行愉快的开发,因此这节虽然很简单但是还是记录了一下。

1. 1对0/1关系配置

1. 通过数据注释属性配置1对0/1关系

我们将要实现一个Student和StudentAddress实体的1对0/1关系,1对0/1关系指的是一个Student可有一个或者零个住址StudentAddress,但是一个StudentAddress必须对应一个Student。在数据库中表现形式是StudentId在Student表中是主键,StudentAddressId在数据库中同时是StudentAddress的主键和外键实体类的代码如下:

public class Student {     public int StudentId { get; set; }     public string StudentName { get; set; }      public virtual StudentAddress Address { get; set; } }       public class StudentAddress  {     [ForeignKey("Student")]     public int StudentAddressId { get; set; }              public string Address1 { get; set; }     public string Address2 { get; set; }     public string City { get; set; }     public int Zipcode { get; set; }     public string State { get; set; }     public string Country { get; set; }      public virtual Student Student { get; set; } }

上边代码中会遵循EF CodeFirst默认约定,StudentId作为Students表的主键,StudentAddressId作为StudentAddresses表的主键,我们不需要自己去配置主键了,默认约定不会把StudentAddressId设置为指向Student实体的外键,这就我们需要自己去配置。在StudentAddressId通过[ForeignKey("Student")]修饰即可

在1对0/1关系中,Student在没有StudentAddress时可以保存成功,但是StudentAddress没有分配Student时进行保存就会抛出异常。

2. 通过FluentApi配置1对0/1关系

protected override void OnModelCreating(DbModelBuilder modelBuilder) {     // 配置Student和StudentAddress实体     modelBuilder.Entity<Student>()                 .HasOptional(s => s.Address) // 给Student设置可空的StudentAddress属性                 .WithRequired(ad => ad.Student); //给StudentAddress设置不能为空的Student属性.没有Student时,StudentAddress不能保存 }

生成数据库如下:

2. 1对多关系配置

  这一部分介绍EF中CodeFirst模式下1对多关系的配置,我们要实现Student和Grade的关系配置,一个学生只能有一个班级而一个班级可以有多个学生,实体类如下:

public class Student {     public int StudentId { get; set; }     public string StudentName { get; set; } }         public class Grade {     public int GradeId { get; set; }     public string GradeName { get; set; }     public string Section { get; set; } }

1.通过默认约定配置1对多关系

//Student实体中的Grade引用导航属性和Grade实体中的Students集合导航属性,两者有一个即可,生成的是可空的外键 public class Student {     public int Id { get; set; }     public string Name { get; set; }     public Grade Grade { get; set; } }  public class Grade {     public int GradeID { get; set; }     public string GradeName { get; set; }     public string Section { get; set; }          public ICollection<Student> Students { get; set; } }

运行程序后生成的数据库如下,我们看到生成了可空的Grade_GradeId外键

2.生成不可空的外键(Student必须有班级)

public class Student {     public int Id { get; set; }     public string Name { get; set; }          public int GradeId { get; set; }//如果改成 public int? GradeId则生成可空的外键     public Grade Grade { get; set; } }  public class Grade {      public int GradeId { get; set; }     public string GradeName { get; set; }          public ICollection<Student> Student { get; set; } }

生成的数据库如下:

  通常我们不需要配置1对多的关系,因为EF的默认约定就能帮我们很好地解决这个问题,如果为了让关系更好维护,我们也可以通过FluentApi来配置1对多关系。

FluentApi配置1对多关系代码如下:

    public class SchoolContext : DbContext     {         public SchoolContext() : base()         {         }         public DbSet<Student> Students { get; set; }         public DbSet<Grade> Grade { get; set; }          protected override void OnModelCreating(DbModelBuilder modelBuilder)         {             modelBuilder.Entity<Student>()                 .HasRequired(s => s.Grade)//Student有必需的导航属性Grade,这会创建一个not null的外键                 .WithMany(g => g.Students)//Grade实体有集合导航属性Student                 .HasForeignKey(s=> s.GradeId);//设置外键(如果Student中属性不遵循约定我们自己指定外键,如HasForeignKey(s=>s.GradeKey))         }     }

我们也可以通过Grade实体来实现Student和Grade的1对多关系,代码如下:

        protected override void OnModelCreating(DbModelBuilder modelBuilder)         {             modelBuilder.Entity<Grade>()                 .HasMany(g => g.Students)                 .WithRequired(s => s.Grade)                 .HasForeignKey(s => s.GradeKey);         }

运行程序,生成的数据库如下:

  级联删除指当删除父级记录时会自动删除子级级联,如删除班级时,将在这个班级的所有学生记录一并删除,通过FluentApi很容易配置级联删除。

modelBuilder.Entity<Grade>()     .HasMany<Student>(g => g.Students)     .WithRequired(s => s.CurrentGrade)     .WillCascadeOnDelete();//开启级联删除,删除班级时会一并删除所有这个班级的学生     //.WillCascadeOnDelete(false);不开启级联删除

3.配置多对多关系

1.通过默认约定配置多对多关系

这一部分介绍多对多关系的配置,以Student和Course为例,一个学生可以学多门课,每门课的学生可以是多个。EF6包含了多对多关系的默认约定。在Student实体类添加一个Course的集合导航属性,在Course实体类下添加一个Student集合导航属性,不需额外的配置,EF会帮我们创建Student和Course的多对多关系。代码如下:

    public class Student     {         public int StudentId { get; set; }         public string StudentName { get; set; }         public  ICollection<Course> Courses { get; set; }     }      public class Course     {         public int CourseId { get; set; }         public string CourseName { get; set; }         public ICollection<Student> Students { get; set; }     }      public class SchoolContext : DbContext     {         public SchoolContext() : base()         {         }         public DbSet<Student> Students { get; set; }         public DbSet<Course> Grade { get; set; }      }

运行程序后生成的数据库如下,EF创建了Courses,Students表,同时创建了一个StudentCourses中间表:

2.通过FluentApi配置多对多关系

直接上代码:

    modelBuilder.Entity<Student>()                 .HasMany<Course>(s => s.Courses)//配置一个学生有多个课程                 .WithMany(c => c.Students)      //配置一门课程有多个学生                 .Map(cs =>                         {                             cs.MapLeftKey("StudentRefId");  //因为通过Entity<Student>()开始的,所以左表是Student                             cs.MapRightKey("CourseRefId");  //右表是Course                             cs.ToTable("StudentCourse");    //生成StudentCourse中间表                         });

3.多对多的数据重置

在EF中如果中间表只有两个实体的主键列,那么EF会自动帮我们维护,一个重置学生课程的案例(常用的User-Role-Action权限控制也能这样重置角色和权限):

       static void Main(string[] args)         {             using (SchoolContext context=new SchoolContext())             {                 //必须要把对应的Courses查出来,不然添加时会包空指针异常                 var stu1 = context.Students.Include("Courses").Where(s=>s.StudentId==3).First();                                  var co1 = context.Courses.Find(1);                 var co2= context.Courses.Find(2);                 //先清空再添加                 stu1.Courses.Clear();                 stu1.Courses.Add(co1);                 stu1.Courses.Add(co2);                 context.SaveChanges();             }         }

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!