EntityFrameWork Core从零开始,(三)一对多/多对一关系的映射

前言:

根据关系的方向性,及老外说的是否能导航到关联属性.
我们可以将一对多/多对一划分成三种情况
1.单向的一对多
2.单向的多对一
3.双向一对多或多对一
在这里插入图片描述
无论一对多的关系如何,但在数据库的表结构都是一样的.
在EFCore中,它默认支持以上的所有的方式,且不需要使用那个FluentAPI进行配置.
所以问题变得十分的简单了,这就很大的方便了开发工作

使用方式:

1.单向多对一:

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; }
}
class SGDbContext:DbContext
    {
        public DbSet<Student> students { get; set; }

        public DbSet<Grade> grades { get; set; }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            var Connection = "server=.;database=XXXX;uid=XXXX;pwd=XXXXX";
            optionsBuilder.UseSqlServer(Connection);
        }
    }

再看一看表生成的代码

protected override void Up(MigrationBuilder migrationBuilder)
        {
            //生成grades表(主表),将属性映射成字段
            migrationBuilder.CreateTable(
                name: "grades",
                columns: table => new
                {
                    GradeId = table.Column<int>(type: "int", nullable: false)
                        .Annotation("SqlServer:Identity", "1, 1"),
                    GradeName = table.Column<string>(type: "nvarchar(max)", nullable: true),
                    Section = table.Column<string>(type: "nvarchar(max)", nullable: true)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_grades", x => x.GradeId);
                });

            //生成students表(从表),并设置了外键关联
            migrationBuilder.CreateTable(
                name: "students",
                columns: table => new
                {
                    Id = table.Column<int>(type: "int", nullable: false)
                        .Annotation("SqlServer:Identity", "1, 1"),
                    Name = table.Column<string>(type: "nvarchar(max)", nullable: true),
                    GradeId = table.Column<int>(type: "int", nullable: true)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_students", x => x.Id);
                    //将Grade属性映射成外键参照grades表的主键Gradeid
                    table.ForeignKey(
                        name: "FK_students_grades_GradeId",
                        column: x => x.GradeId,
                        principalTable: "grades",
                        principalColumn: "GradeId",
                        onDelete: ReferentialAction.Restrict);
                });

            migrationBuilder.CreateIndex(
                name: "IX_students_GradeId",
                table: "students",
                column: "GradeId");
        }

在这里插入图片描述

2.单向一对多

class StudentSec
{
     public int StudentId { get; set; }
     public string StudentName { get; set; }
}
class GradeSec
{
    public int GradeId { get; set; }
    public string GradeName { get; set; }
    public string Section { get; set; }
     //多方不能导航到一方,是为单向一对多关联
    public ICollection<StudentSec> Students { get; set; }
}
class SGDbContext:DbContext
 {
      public DbSet<StudentSec> student2 { get; set; }

      public DbSet<GradeSec> grade2 { get; set; }

      protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
      {
          var Connection = "server=.;Database=xxxxxx;uid=xxx;pwd=xxxxx";
          optionsBuilder.UseSqlServer(Connection);
      }
  }

回到建表代码,只能说和单向多对一一模一样,所以不必过多赘述

protected override void Up(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.CreateTable(
                name: "grade2",
                columns: table => new
                {
                    GradeSecId = table.Column<int>(type: "int", nullable: false)
                        .Annotation("SqlServer:Identity", "1, 1"),
                    GradeSecName = table.Column<string>(type: "nvarchar(max)", nullable: true),
                    Section = table.Column<string>(type: "nvarchar(max)", nullable: true)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_grade2", x => x.GradeSecId);
                });

            migrationBuilder.CreateTable(
                name: "student2",
                columns: table => new
                {
                    StudentSecId = table.Column<int>(type: "int", nullable: false)
                        .Annotation("SqlServer:Identity", "1, 1"),
                    StudentSecName = table.Column<string>(type: "nvarchar(max)", nullable: true),
                    GradeSecId = table.Column<int>(type: "int", nullable: true)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_student2", x => x.StudentSecId);
                    table.ForeignKey(
                        name: "FK_student2_grade2_GradeSecId",
                        column: x => x.GradeSecId,
                        principalTable: "grade2",
                        principalColumn: "GradeSecId",
                        onDelete: ReferentialAction.Restrict);
                });

            migrationBuilder.CreateIndex(
                name: "IX_student2_GradeSecId",
                table: "student2",
                column: "GradeSecId");
        }

3.双向多对一/一对多

双向的多对一和一对多是同一回事,
因为可以双向导航,对于一方是一对多,对于多方是多对一,
所以不必区分开来

同样按照他的约定,我们根本不用写FluentAPI

class Person
{
     public int PersonId { get; set; }

     public int Name { get; set; }

     public ICollection<Pet> pets { get; set; }
 }
 class Pet
 {
     public int PetId { get; set; }

     public string name { get; set; }

     public Person owner { get; set; }
 }
 class PPDbContext:DbContext
 {
     public DbSet<Person> persons { get; set; }

     public DbSet<Pet> pets { get; set; }

     protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
     {
         var Connection = "server=.;Database=XXXXX;uid=XXXX;pwd=XXXX";
         optionsBuilder.UseSqlServer(Connection);
     }
 }

再大致看一下建表的代码,发现建表的代码结构都是一样的

protected override void Up(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.CreateTable(
                name: "persons",
                columns: table => new
                {
                    PersonId = table.Column<int>(type: "int", nullable: false)
                        .Annotation("SqlServer:Identity", "1, 1"),
                    Name = table.Column<int>(type: "int", nullable: false)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_persons", x => x.PersonId);
                });

            migrationBuilder.CreateTable(
                name: "pets",
                columns: table => new
                {
                    PetId = table.Column<int>(type: "int", nullable: false)
                        .Annotation("SqlServer:Identity", "1, 1"),
                    name = table.Column<string>(type: "nvarchar(max)", nullable: true),
                    ownerPersonId = table.Column<int>(type: "int", nullable: true)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_pets", x => x.PetId);
                    table.ForeignKey(
                        name: "FK_pets_persons_ownerPersonId",
                        column: x => x.ownerPersonId,
                        principalTable: "persons",
                        principalColumn: "PersonId",
                        onDelete: ReferentialAction.Restrict);
                });

            migrationBuilder.CreateIndex(
                name: "IX_pets_ownerPersonId",
                table: "pets",
                column: "ownerPersonId");
        }

这里就测试一下吧

static void Main(string[] args)
        {
            PPDbContext context = new PPDbContext();

            Person p1 = new Person();
            p1.Name = 999;

            Person p2 = new Person();
            p2.Name = 999;

            //这里不让Person去维护关系,(其实是不让一方去维护关系,多方维护关系比较清晰一点)
            Pet dog = new Pet();
            dog.name = "旺財";
            dog.owner = p1;

            Pet cat = new Pet();
            cat.name = "阿喵";
            cat.owner = p1;

            context.persons.Add(p1);
            context.persons.Add(p1);
            context.pets.Add(dog);
            context.pets.Add(cat);

            context.SaveChanges();

            List<Person> list = context.persons
                                    .Include(person =>person.pets)
                                        .Where(person => person.pets!=null)
                                            .ToList();
            Console.WriteLine("Person的ID為:"+list.First<Person>().PersonId+"Person的名字為"+list.First<Person>().Name);
            Person one = list.First<Person>();
            
            Console.WriteLine(one.pets.ElementAt<Pet>(0).name+ one.pets.ElementAt<Pet>(1).name);
        }

结果:

Person的ID為:1Person的名字為999
旺財阿喵

(PS:双向关系的话不需要双方同时维护关系,在Hibertnate中可以设置inverse属性来迫使一方放弃维护关系,这样能少发一条SQL语句也能减少数据库的压力,由于我们并不知道EFCore为我们生成了什么sql语句,也就无从得知EFCore维护实体关系的规则,或许之后可以研究一下)
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

罗马苏丹默罕默德

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值