欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2023-04-06 20:57:13
这一节介绍EF CodeFirst模式中的1对0/1,1对多,多对多关系的配置,只有梳理清楚实体间的关系,才能进行愉快的开发,因此这节虽然很简单但是还是记录了一下。 1. 1对0/1关系配置 1. 通过数据注释属性配置1对0/1关系 我们将要实现一个Student和StudentAddress实体的 ......

这一节介绍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不能保存
}

生成数据库如下:

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

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对多关系

 1.生成可空的外键(student可以没有班级)

//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外键

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

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; }
}

生成的数据库如下:

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

2. 通过fluentapi配置1对多关系

  通常我们不需要配置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);
        }

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

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

3. 配置1对多的级联删除

  级联删除指当删除父级记录时会自动删除子级记录,如删除班级时,将在这个班级的所有学生记录一并删除(ef中是将这个班级的student中的gradeid列都设成null),通过fluentapi很容易配置级联删除。

modelbuilder.entity<grade>()
    .hasmany<student>(g => g.students)
    .withrequired(s => s.currentgrade)
    .willcascadeondelete();//开启级联删除,删除班级时会一并删除所有这个班级的学生
    //.willcascadeondelete(false);不开启级联删除

一点补充:ef中的级联删除默认是打开的,ef中级联删除执行策略

  1对1:如student和studentaddress,删除student时会把studentaddress一并删除。

  1对多:如student和grade,删除grade时,会把该年级下的学生的gradid设成为null。

  多对多:见下边的student和course,删除一门课程时,会删除中间表中该门课程的记录。

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中间表:

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

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();
            }
        }