在数据库层面,表之间关系,通过主键、外键来实现,基于约束 (constraint) 和数据完整性来制约。 在 EF Core 技术层面,并不是简单地与数据库这些关系和约束对应,EF Core 有它自己的机制。本篇介绍 EF core 在处理表关系方面的典型技术点。为了理解的方便,示例数据库只包含两个表:Articles (文章)和 Comments (文章评论)。很明显,Articles 和 Comments 是一对多关系(一篇文章存在多条评论论)。在数据库中,Comments 表的 Id 字段是外键,指向 Articles 表的 主键 Id 字段。一对多关系是数据表最重要的关系。
数据库中的关系
对象模型中的关系
在 EF Core 中,我们在实体类中定义模型的关系,一般通过导航属性来实现。比如刚才的表模型,在 Articles 实体中, Comments 属性是一个 List,表达 Article 含有多条 Comment。
当然,这只是 EF Core 提供的的一种实现方式,也可以在 Comments 实体中定义导航属性,指向 Articles 实体。在两个类中同时定义也是可以的。
注意,这里并不需要显示定义 ArticleId 字段来标识 Comments 表的外键。
EF Core 关系映射
EF Core 中关系映射,最基本的内容包括:
- 为每一个实体 (entity) 添加主键
- 为实体添加外键 (如果有的话)
- 通过主键和外键定义两个实体的参照关系 (能够清晰地体现出一对多)
(示例为在多的一方定义)
关系表的增删改查
刚才的示例中,在 Articles 实体中和 Comments 实体中,都定义了导航属性。这样我们可以很方便的进行 Create 操作。示例方式一:利用 Articles 实体的导航属性:
[TestMethod]
public void TestInsert1()
{
var a1 = new Article
{
Title = ".net下主要ORM框架",
Content = "xxxxx"
};
a1.Comments.Add(new Comment { CommentContent = "非常赞"});
a1.Comments.Add(new Comment { CommentContent = "强烈支持"});
using var context = new AppDbContext();
context.Articles.Add(a1);
context.SaveChanges();
}
注意到:不需要显式为 Comment 实体的 Article 属性赋值。
示例新增方式二:利用 Comment 实体的导航属性:
[TestMethod]
public void TestInsert2()
{
var a1 = new Article
{
Title = "EFCore 从入门到精通",
Content = "EFCore"
};
var c1 = new Comment {
CommentContent = "这篇文章真不错",
Article = a1
};
var c2 = new Comment
{
CommentContent = "这篇文章写的不错呀",
Article = a1
};
a1.Comments.Add(c1);
a1.Comments.Add(c2);
using var context = new AppDbContext();
context.Articles.Add(a1);
context.SaveChanges();
}
假设我们想对已有的 Article 进行新增操作,我们也能利用导航属性来实现,举例如下:
[TestMethod]
public void TestAddComments()
{
using var context = new AppDbContext();
var article = context.Articles.Single(a => a.Id == 20);
article.Comments.Add(new Comment {CommentContent = "我觉得文章不怎么样" });
context.SaveChanges();
}
查询中获取表关系
刚才我们看到,Article 表和 Comment 表已经定义了主键和外键,在模型层面,定义了导航属性,并且定义了关系。此时我们试着查询出 Id == 20 的文章以及文章相应的 comment,我们先尝试下面的代码:
[TestMethod]
public void TestQueryArticles()
{
using var context = new AppDbContext();
var article = context.Articles.Single(a=>a.Id==20);
Console.WriteLine(article.ToString());
article.Comments.ForEach(Console.WriteLine);
}
我们发现,并没有关联到 comments 实体:
如果在查询的时候,需要关联数据,需要利用 Microsoft.EntityFrameworkCore 的扩展方法 Include() 方法:
[TestMethod]
public void TestQueryArticleAndComments()
{
using var context = new AppDbContext();
var article = context.Articles.Include(a=>a.Comments).Single(a=>a.Id==20);
// print article and related comments
Console.WriteLine(article.ToString());
article.Comments.ForEach(Console.WriteLine);
}
运行的测试结果如下,能够获取到 article 的 comments: