导航属性[翻译不下去了]
本章中, 我会介绍通过导航属性, 在 EFCore5 中如何映射 一对多 和 多对多 的关系. 在本书前面, 在我们映射 Person 记录中一对多的地址关系时, 我们已经完成为创建这种关系所需的一部分代码了. 现在, 我会展示如何在 Address 与 Person 实体之间构建多对多关系. 我仍会介绍如何使用导航属性, 在父实体和子实体之间构建多对多关系.
本书存在一些术语需要解释补充
- 关系怎么建立
- 父子实体的多对多关系
在一对多关系中映射父实体
在第10章的案例中, 将多个 Address 实体关联到一个 person 记录上, 我们在 Person 类中创建一个集合属性来存储我们的地址, 如下:
public List<Address> Addresses { get; Set; } = new List<Address>();
引用的 Person 类完整代码如下:
严重怀疑是在凑字数, 有一次快整理不下去了... 囧.
好吧, 至少可以看到会不会冒出什么新的没交代的代码.
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace EFCore5WebApp.Core.Entities
{
[Table("Persons", Schema = "dbo")]
public class Person
{
public int Id { get; set; }
[Required]
[MaxLength(255)]
public string FirstName { get; set; }
[Required]
[MaxLength(255)]
public string LastName { get; set; }
[Required]
public string EmailAddress { get; set; }
public List<Address> Addresses { get; set; } = new List<Address>();
[NotMapped]
public string FullName => $"{FirstName} {LastName}";
public DateTime CreatedOn { get; set; }
}
}
Addresses 导航属性支持查询一对多地址记录, 从检索出的 person 记录中.
我快不想组织语言了, 赶快整理完球.
将相关实体映射到父实体 (Mapping a Related Entity to Parent)
要完成一对多关系在Person和Address实体之间, 我们将单个Person类型的属性 Person, 加到 Address 实体类中, 看起来是这样:
Fuck, Fuck, Fuck
namespace EFCore5WebApp.Core.Entities
{
public class Address
{
public int Id { get; set; }
public string AddressLine1 { get; set; }
public string AddressLine2 { get; set; }
public string City { get; set; }
public string State { get; set; }
public string Country { get; set; }
public string ZipCode { get; set; }
public int? PersonId { get; set; }
public Person Person { get; set; }
}
}
这段代码的结果是我们可以检索数据库中的任意地址, 并通过 PersonId 值来确定地址属于谁. 每一个地址都含有一个 Person.
一对多集成测试
不组织语言了, Fuck
这一节给可一个测试代码
- 创建一个person有两个地址
- 测试通过查询person, 地址记录长度为2.
- 测试去person中的地址, 地址的person属性不为空.
- 测试结束 [TearDown], 删除这个 person
这个单元测试是存在问题的.
多对多关系
我们的多对多关系是关系到父与子实体的. 一个子实体可以有多个父实体, 同时一个父实体允许有多个子实体. 在 EFCore5 以前, 我们需要显式的创建映射, 在 DbContext 中的 OnModelCreating 方法中. 在 EFCore5 中, 我们只需要简单的为他们添加集合来描述关系, 而 EFCore5 会创建交叉引用表 (cross-reference table) 为我们, 在我们创建迁移的时候. 在前期的 EFCore 中, 我们需要自己创建交叉引用实体来存储多对多关系中两个实体的外键. 下面我们一步一步的演示.
通过导航属性映射关系
作者更新 Person 类, 让该类具有 Person 集合类型的 Parents 属性, 以及 Children 属性.
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace EFCore5WebApp.Core.Entities
{
[Table("Persons", Schema = "dbo")]
public class Person
{
public int Id { get; set; }
[Required]
[MaxLength(255)]
public string FirstName { get; set; }
[Required]
[MaxLength(255)]
public string LastName { get; set; }
[Required]
public string EmailAddress { get; set; }
public List<Address> Addresses { get; set; } = new List<Address>();
public DateTime CreatedOn { get; set; }
public List<Person> Persons { get; set; } = new List<Person>();
public List<Person> Children { get; set; } = new List<Person>();
}
}
FullName 属性又没了, 囧
然后简单说明了一些无用的信息. 可通过这两个属性向上与向下访问.
创建并运行迁移
Add-Migration PersonParentChildren
然后又看到 Migrations 生成了代码, 贴代码 ...
...
你可以看到 EFCore5 添加了 PersonPerson 交叉引用表来存储多对多映射, 在 parents 和 children 之间. personperson 表中含有 ChildrenId 和 ParentsId 列. 它通过父关联到子, 反之亦然.
然后运行 Update-Database
, 通过 VS 看到数据库中创建了新表.
多对多集成测试
贴一段代码, 解释一遍, 再逐步贴一块块前面已经贴过的代码. 然后再告诉你代码应该是这样, 再贴一次代码. Fuck
创建一个复杂的 有父有子的 Person
测试时在测试通过person, 其 parents 的长度, 和 children 的长度符合要求.
居然又把代码贴了一边