一、什么是实体间关系
- 数据库表(实体)之间的关系:一对一(学生-成绩)、一对多(学生-科目)、多对多(教师-班级)。数据库中,每一个实体可以由主键唯一标识,而每一条“关系”则由外键进行标识。
(1) 外键分为逻辑外键与物理外键。逻辑外键在数据库中不存在任何体现与约束,所有约束由业务代码进行控制,方便管理与迁移;物理外键在数据库中直接表现为外键列,需要严格按照数据库约束进行增删操作。 - EF Core不仅支持单实体操作,更支持多实体之间的关系操作。通过关系操作实体主要由两大途径:一是通过逻辑/物理外键手动编写关联查询;二就是通过导航属性进行操作。
- 操作步骤三部曲:实体类中的关系属性;FluentAPI关系配置;使用关系操作
- 一些有关术语:
(1)依赖实体:包含外键属性的实体,有时指的是关系的子级。
(2)主体实体:包含主键/备选键属性的实体,有时是指的关系的“父级”
(3)主体键:唯一标识主体实体的属性或属性集合,它可能是主键,也有可能是备选键
(4)外键:依赖实体中用于存储主体实体的主键值属性
(5)导航属性:在引用相关实体的主体实体和/或依赖实体上定义的属性。
①集合导航属性:包含对多个依赖实体引用的导航属性
②引用导航属性:对单个依赖实体引用的导航属性
③反向导航属性:对于特定导航属性关系上一端的导航属性与另一端的导航属性互为反向导航属性。
(例如A中的导航属性b指向实体B,而B中又有属性a指向实体A,则a与b互为反向导航属性)
二、默认的外键约定与导航属性
- 导航属性:在EntityFramework中实体之间的关系由“导航属性(Navgation Properity)”来定义与体现。注意,导航属性将无法映射为数据库中的单个字段(原子属性),只能映射为外键关系。
- 默认外键约定:
在EF Core中,约定大于配置,在以下情况下会默认生成外键(依赖实体的导航属性就将会被映射外键)
已知主体实体Blog与依赖实体Post
3.第一种情况:引用导航属性,导航属性名+主体主键名:此时主体实体与依赖实体构成一对一关系
主体实体:
```csharppublic class Blog{public int Id { get; set; }public string Title { get; set; } public string Content{ get; set; }//导航属性名public Post Post { get; set; }}
依赖实体:
public class Post
{public int Id { get; set; }public string? Title { get; set; }public string? Content { get; set; }public string? AuthorName { get; set; }public DateTime? UpdateTime { get; set; }//主体主键名public int BlogId { get; set; }
}
在主体实体Blog中,属性Post充当了依赖实体的导航属性,每一个Blog对象都拥有一个对应的Post对象。此时对应Post对象中的BlogId指向了Post所属的Blog对象,二者构成一对一关系,生成对应的外键。执行数据迁移进行测试:
可以看到此时已经生成了对应的外键。
4.第二种情况,集合导航属性,集合导航属性名+主体主键名:此时构成一对多关系,一个主体实体通过List集合来含有多个依赖实体。
主体实体:
```csharppublic class Blog{public int Id { get; set; }public string Name { get; set; }//导航属性集合 一对多 一个Blog含有多个Postpublic List<Post> Posts { get; set; }}
依赖实体:
```csharp
public class Post
{public int Id { get; set; }//主体主键名 每一个Post都需要对应唯一一个Blogpublic int BlogId { get; set; }public string? Content { get; set; }public DateTime? UpdateTime { get; set; }
}
迁移代码:
需要注意的是,此处作为导航属性的List不能使用new进行初始化,进行初始化后就不能作为导航属性来使用了。
5.第三种情况:主体属性名+(主体属性+主体主键名)。此时构成一对一关系
主体实体:
public class Blog{public int Id { get; set; }public string Title{ get; set; }
public string? Content{ get; set; }}
依赖实体:
```csharp
public class Post
{public int Id { get; set; }public string? Content { get; set; }public DateTime? UpdateTime { get; set; }//主体属性+主体主键名public int BlogId { get; set; }//主体名public Blog? Blog { get; set; }
}
执行数据迁移:
成功生成外键