EFCore的关联查询

官方文档

前言:本来这种通过设置外键来关联表的方式,我只记得在大学数据库课程当中实践过,后面的实习(java)、工作当中都没有使用到通过这种设计外键来关联表,因为当存在外键时,新增或删除的时候都要去检查关联表的记录是否存在,影响IO效率;那通过什么方式解决的呢?比如当存在A表和B表存在一对多的关系是,一般会在B表设置A表id字段,插入的时候,插入A表一条记录同时插入B表多条记录。

后来工作的时候使用了微软开发的C#语言及ORM框架EFCore,同时在其他项目中看到了这种设计,尤其是通过EFCore的CodeFirst设计这种外键是非常方便的,同时查询的时候也避免了很多代码。

背景:公司里的商城项目我接手的时候,都没有使用CodeFirst,没有历史迁移记录。同时我也是第一次负责这么大的项目,也有点不敢”轻举妄动“。所以就出现了一个奇怪的现象,在后面建表过程当中,有些表存在一对多或一对一的关系,数据库中表没有设置外键,但是在代码配置了映射关系,如下第一段代码,那么在实际查询的时候可以直接使用Include()以及ThenInclude()的,但是有一天发现一个问题,如下第二段代码,当我使用了.ThenInclude(p => p.QualityService)之后,OrderItems表中的记录也不见了。

c#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
//一、
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Post>()
.HasOne(p => p.Blog)
.WithMany(b => b.Posts);
}
//二、
var query = _orderRepository.Query()
.Include(c => c.OrderItems)//.ThenInclude(p => p.QualityService)
.Include(c => c.Customer)
.Include(c => c.OrderShipping).ThenInclude(OrderShipping => OrderShipping.WoodenBoxType)
.Include(c => c.OrderShipping).ThenInclude(OrderShipping => OrderShipping.TransportationType)
.Where(c => c.CustomerId == user.Id && c.Id == id);

解决办法是:在配置的时候显示指定了允许外键为null

一个 OrderItem 可以没有 关联的质检服务(QualityService),这是可选的

plaintext
1
2
3
4
5
6
modelBuilder.Entity<OrderItem>(u =>
u.HasOne(x => x.QualityService)
.WithMany()
.HasForeignKey(x => x.QualityId)
.IsRequired(false);
});

下面是使用EFCore表之间一对多、一对一、多对一、多对多的关系的实体配置

c#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public class Blog
{
public int Id { get; set; } // Primary key
public string Name { get; set; }

public IList<Post> Posts { get; } = new List<Post>(); // Collection navigation
public BlogAssets Assets { get; set; } // Reference navigation
}

public class BlogAssets
{
public int Id { get; set; } // Primary key
public byte[] Banner { get; set; }

public int? BlogId { get; set; } // Foreign key
public Blog Blog { get; set; } // Reference navigation
}

public class Post
{
public int Id { get; set; } // Primary key
public string Title { get; set; }
public string Content { get; set; }

public int? BlogId { get; set; } // Foreign key
public Blog Blog { get; set; } // Reference navigation

public IList<Tag> Tags { get; } = new List<Tag>(); // Skip collection navigation
}

public class Tag
{
public int Id { get; set; } // Primary key
public string Text { get; set; }

public IList<Post> Posts { get; } = new List<Post>(); // Skip collection navigation
}