当前位置: 移动技术网 > IT编程>开发语言>.net > EF CodeFirst系列(6)---配置1对1,1对多,多对多关系

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

2018年09月21日  | 移动技术网IT编程  | 我要评论

beyonce好听的歌,张槐乾,vr1.5中文版下载

这一节介绍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对多关系

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

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

生成的数据库如下:

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

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

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

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

 

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网