# 模块二 基础巩固 EF Core 关系

## 2.4.4 EF Core -- 关系 <a href="#id-244efcore-guan-xi" id="id-244efcore-guan-xi"></a>

* 一对多
* 一对一
* 多对多
* 示例

关系：<https://docs.microsoft.com/zh-cn/ef/core/modeling/relationships?tabs=fluent-api%2Cfluent-api-simple-key%2Csimple-key>

### 一对多 <a href="#yi-dui-duo" id="yi-dui-duo"></a>

```
// Dependent Entity 主表
public class Blog
{
    // Principal Key 标识键/可能是主键或者备用键（唯一性约束）
    public int BlogId { get; set; }
    
    public string Url { get; set; }

    // Collection navigation property 关联多个从表的属性集合（集合属性）
    public List<Post> Posts { get; set; }
}

// Principal Entity 从表
public class Post
{
    public int PostId { get; set; }
    
    public string Title { get; set; }
    
    public string Content { get; set; }

    // Foreign Key 外键（指向主表中的 Principal Key）
    // Inverse navigation property 反向导航属性
    public int BlogId { get; set; }

    // Inverse navigation property 反向导航属性
    public Blog Blog { get; set; }
}
```

### 一对一 <a href="#yi-dui-yi" id="yi-dui-yi"></a>

```
// Principal Entity 从表
public class Blog
{
    public int BlogId { get; set; }
    
    public string Url { get; set; }

    public List<Post> Posts { get; set; }
}

// Dependent Entity 主表
public class Post
{
    public int PostId { get; set; }
    
    public string Title { get; set; }
    
    public string Content { get; set; }

    // Reference navigation property 一对一时指向另外一张表（引用属性）
    public Blog Blog { get; set; }
}
```

### 多对多 <a href="#duo-dui-duo" id="duo-dui-duo"></a>

```
public class Post
{
    public int PostId { get; set; }
    
    public string Title { get; set; }
    
    public string Content { get; set; }

    public ICollection<Tag> Tags { get; set; }
}

public class Tag
{
    public string TagId { get; set; }

    public ICollection<Post> Posts { get; set; }
}
```

### 示例 <a href="#shi-li" id="shi-li"></a>

#### 一对多 <a href="#yi-dui-duo-1" id="yi-dui-duo-1"></a>

![](https://3083743005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8gwpNo3eyzHkX0O40HRA%2Fuploads%2FbyO1pbBQiBGX4T4VRDOM%2F202.jpg?alt=media\&token=1ea696ce-8a13-494b-a4fc-622950a41c93)

一个 Project 对应多个 ProjectGroup

在 Project 实体中添加 ProjectGroup 列表

```
public List<ProjectGroup> Groups { get; set; }
```

迁移

```
dotnet ef migrations add ProjectGroupCollectionProperty
```

生成集合属性 ProjectGroupCollectionProperty

```
protected override void Up(MigrationBuilder migrationBuilder)
{
    migrationBuilder.AlterColumn<string>(
        name: "ProjectId",
        table: "ProjectGroups",
        nullable: true,
        oldClrType: typeof(string),
        oldType: "longtext CHARACTER SET utf8mb4",
        oldNullable: true);

    migrationBuilder.CreateIndex(
        name: "IX_ProjectGroups_ProjectId",
        table: "ProjectGroups",
        column: "ProjectId");

    migrationBuilder.AddForeignKey(
        name: "FK_ProjectGroups_Projects_ProjectId",
        table: "ProjectGroups",
        column: "ProjectId",
        principalTable: "Projects",
        principalColumn: "Id",
        onDelete: ReferentialAction.Restrict);
}
```

**手动配置**

```
class MyContext : DbContext
{
    public DbSet<Blog> Blogs { get; set; }
    public DbSet<Post> Posts { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        // 先在从表上建立一对一的关系，再从主表上建立一对多的关系
        modelBuilder.Entity<Post>()
            .HasOne(p => p.Blog)
            .WithMany(b => b.Posts);
    }
}

public class Blog
{
    public int BlogId { get; set; }
    public string Url { get; set; }

    public List<Post> Posts { get; set; }
}

public class Post
{
    public int PostId { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }

    public Blog Blog { get; set; }
}
```

LighterDbContext

```
// 一对一
modelBuilder.Entity<Project.ProjectGroup>().HasOne<Project.Project>(g => g.Project);
// 一对多
modelBuilder.Entity<Project.ProjectGroup>().HasOne<Project.Project>(g => g.Project).WithMany(p => p.Groups);
```

#### 多对多 <a href="#duo-dui-duo-1" id="duo-dui-duo-1"></a>

![](https://3083743005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8gwpNo3eyzHkX0O40HRA%2Fuploads%2FbyTpf9T6lyk9cAcCs4MC%2F203.jpg?alt=media\&token=3d4836a2-d5c9-48e1-a33b-5a183b270d46)

为 Project 和 Subject 建立中间表 SubjectProject

```
public class Project : Entity
{
    public string Title { get; set; }

    public DateTime StartDate { get; set; }

    public DateTime EndDate { get; set; }

    public string SupervisorId { get; set; }

    public string PlanId { get; set; }

    public List<ProjectGroup> Groups { get; set; }
    
    public List<SubjectProject> SubjectProjects { get; set; }
}

public class Subject : Entity
{
    public string Title { get; set; }

    public string Content { get; set; }
    
    public List<SubjectProject> SubjectProjects { get; set; }
}

public class SubjectProject : Entity
{
    public string ProjcetId { get; set; }

    public Project Project { get; set; }

    public string SubjectId { get; set; }

    public Subject Subject { get; set; }
}
```

配置多对多关系

LighterDbContext

```
// 多对多（两组一对多）
modelBuilder.Entity<Project.SubjectProject>()
    .HasOne<Project.Project>(s => s.Project)
    .WithMany(p => p.SubjectProjects)
    .HasForeignKey(s => s.ProjcetId);
    
modelBuilder.Entity<Project.SubjectProject>()
    .HasOne<Project.Subject>(s => s.Subject)
    .WithMany(p => p.SubjectProjects)
    .HasForeignKey(s => s.SubjectId);
```

迁移

```
dotnet ef migrations add SubjectProjectManyToManyRelation
```

SubjectProjectManyToManyRelation

```
table.ForeignKey(
    name: "FK_SubjectProject_Projects_ProjcetId",
    column: x => x.ProjcetId,
    principalTable: "Projects",
    principalColumn: "Id",
    onDelete: ReferentialAction.Restrict);
table.ForeignKey(
    name: "FK_SubjectProject_Subject_SubjectId",
    column: x => x.SubjectId,
    principalTable: "Subject",
    principalColumn: "Id",
    onDelete: ReferentialAction.Restrict);
```

中间表创建了两个外键，形成多对多

**EF Core 5.0 多对多实现**

```
public class Post
{
    public int PostId { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }

    public ICollection<Tag> Tags { get; set; }
}

public class Tag
{
    public string TagId { get; set; }

    public ICollection<Post> Posts { get; set; }
}
```

迁移的时候会自动生成中间表

联接实体类型配置 HasMany

```
modelBuilder
    .Entity<Post>()
    .HasMany(p => p.Tags)
    .WithMany(p => p.Posts)
    .UsingEntity(j => j.ToTable("PostTags"));
```

### GitHub源码链接： <a href="#github-yuan-ma-lian-jie" id="github-yuan-ma-lian-jie"></a>

<https://github.com/MingsonZheng/ArchitectTrainingCamp/tree/main/LighterApi>
