EF Core 实战教程(八)一对多关系配置
小白浏览:11252022-09-18 14:37:42本文累计收益:0我也要赚钱

EF Core中实体之间关系的配置有如下三种:

一对多:HasOne(XXX).WithMany(XXX)

一对一:HasOne(XXX).WithOne (XXX)

多对多:HasMany(XXX).WithMany(XXX)

本文主要说说一对多关系,一对多关系一般分为双向一对多和单向一对多,双向一堆多关系一般两个表之间关系都比较密切,如果一个学生跟他的教育经历和工作经历都是关系密切的;相反单向一对多关系关系就没有那个密切了,比如用户表同用户订单、请假、外勤等。

******实际开发中一般不推荐设置数据库主外键*******。
一、一对多关系建立
1、双向导航关系建立

主要讲解学生跟教育经历的关系,一个学生可以有小学、初中、高中、大学等多个教育经历。首先建立两个表的实体类,学生表中有个List<教育经历>的List,用来指向多个教育经历;教育经历表中有个学生属性用来指向所属那个学生。

两个实体类有了就可以配置一对多关系了,一对多关系的配置可以放在学生配置类也可以放在教育经历配置类中,这里先介绍放在教育经历配置类中,本文后面讲解放到学生类中的方法。

具体代码如下:

实体类代码:

    public class Person
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public int Height { get; set; }
        public List<Education> educations { get; set; }
        = new List<Education>();
    }
    public class Education
    {
        public int Id { get; set; }
        //生成数据库时默认新增一个外键类,列名为本属性名+Id
        public Person Person { get; set; }
        //需要在配置文件告知这一列是外键列,否则数据库将会新增此列
        public string PersonId { get; set; }
        public string SchoolName { get; set; }
    }

教育经历配置类代码:

 

    public class EducationConfig : IEntityTypeConfiguration<Education>
    {
        public void Configure(EntityTypeBuilder<Education> builder)
        {
            builder.ToTable("Educations");
            //设置外键
            builder.HasOne<Person>(e=>e.Person)
                .WithMany(a=>a.educations).HasForeignKey(c=>c.PersonId).IsRequired();
        }
    }

 

程序实例(主要介绍增加数据实例):

至此一对多关系以及配置完成,在进行代码编写之前,需要执行Add-Migration命令将修改更新到数据库才可以,以下是在学生表格增加一个测试学生,然后在教育经历里面增加了初中及高中两条教育经历的代码。

Person person = new Person();
person.Name = "测试学生";
Education education1 = new Education();
education1.SchoolName = "初中";
Education education2 = new Education();
education2.SchoolName = "高中";
person.educations.Add(education1);
person.educations.Add(education2);
db.Persons.Add(person);
db.SaveChanges();
2、单向导航关系建立

本例列举了一个用户表及用户请假信息表,一对多关键建立在请假表中。

实体类代码:

    public class User
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }
    public class Leave
    {
        public int Id { get; set; }
        public User Requester { get; set; }
        public User Approver { get; set; }   
        public string Remark { get; set; }
    }

请假表实体配置代码:

    public class LeaveConfig : IEntityTypeConfiguration<Leave>
    {
        public void Configure(EntityTypeBuilder<Leave> builder)
        {
            builder.ToTable("Leaves");
            builder.HasOne<User>(e => e.Requester).WithMany().OnDelete(DeleteBehavior.NoAction);
            builder.HasOne<User>(e => e.Approver).WithMany().OnDelete(DeleteBehavior.NoAction);
        }
    }

增加及查询测试实例代码:

User user = new User { Name = "测试人员" };
Leave leave = new Leave { Remark = "家中有事", Requester = user,Approver=user };
db.Leaves.Add(leave);
db.SaveChanges();
var list = db.Leaves.FirstOrDefault();
二、双向一对多数据关系相关查询的实现

在一对多关系的查询中比较常见的需求是查询子表时把父表信息带出来,查询父表时把子表信息带出来,EF Core主要使用Include关键字实现。

1、通过父表数据关联查询子表数据

var model = db.Persons.Include(a=>a.educations).Single(a=>a.Id == 1);

以上语句将父表Id为1的学生信息及相对应的教育经历都查询出来了。

2、通过子表数据查询父表数据(结果为一条数据)

var list = db.Educations.Include(a => a.Person).Single(a => a.Id == 2);

三、关系配置在任何一方

双向导航一对多关系配置,也可以配置在父表中,代码如下:

    public class PersonConfig : IEntityTypeConfiguration<Person>
    {
        public void Configure(EntityTypeBuilder<Person> builder)
        {
            builder.ToTable("Persons");
            builder.Property(p=>p.Name).IsRequired();
            //一对多关系
            builder.HasMany<Education>(e => e.educations).WithOne(c=>c.Person).IsRequired();
        }
    }
四、树形结构一对多关系配置

实际开发中有很多树形机构的数据,例如公司组织架构表、导航菜单、地区结构表等等,这里说说树形结构的表一对多关系配置。

如:集团名称-权属企业名称-部门名称-子部门名称

    //实体类   
    public class OrgUnit
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public OrgUnit Parent { get; set; }
        public int? ParentId { get; set; }
        public List<OrgUnit> Children { get; set; }
        = new List<OrgUnit>();
    }
    
    //实体配置类
    public class OrgUnitConfig : IEntityTypeConfiguration<OrgUnit>
    {
        public void Configure(EntityTypeBuilder<OrgUnit> builder)
        {
            builder.ToTable("OrgUnits");
            builder.HasKey(x => x.Id);
            //根节点没有父层Id没有值,非空约束不能要
            builder.HasOne<OrgUnit>(e => e.Parent).WithMany(e => e.Children).IsRequired(false).HasForeignKey(x=>x.ParentId).OnDelete(DeleteBehavior.NoAction); 
        }
    }

//测试增加数据代码
OrgUnit parent1 = new OrgUnit() { Name = "父节点1" };
OrgUnit child11 = new OrgUnit() { Name = "子节点11", Parent = parent1 };
OrgUnit child12 = new OrgUnit() { Name = "子节点12", Parent = parent1 };
parent1.Children.Add(child11);
parent1.Children.Add(child12);
db.OrgUnits.Add(parent1);
db.SaveChanges();

 

评论列表
发表评论
+ 关注