深入理解 EF Core:EF Core 写入数据时发生了什么?

原文:https://bit.ly/2C67m1C
作者:Jon P Smith
翻译:王亮
声明:我翻译技术文章不是逐句翻译的,而是根据我自己的理解来表述的。其中可能会去除一些本人实在不知道如何组织但又不影响理解的句子。

深入理解 EF Core:EF Core 写入数据时发生了什么?

这是深入理解 EF Core 系列的第二篇文章。第一篇是关于 EF Core 如何从数据库读取数据的;而这一篇是关于 EF Core 如何向数据库写入数据的。这是四种数据库操作 CRUD(新增、读取、更新和删除)中的 CUD 部分。

我假设你对 EF Core 已经有了一定的认识,但在深入学习之前,我们先来了解一下如何使用 EF Core,以确保我们已经掌握了一些基本知识。这是一个“深入研究”的课题,所以我准备大量的技术细节,希望我的描述方式你能理解。

本文是“深入理解 EF Core”系列中的第二篇。以下是本系列文章列表:

深入理解 EF Core:当 EF Core 从数据库读取数据时发生了什么?

深入理解 EF Core:当 EF Core 写入数据到数据库时发生了什么?(本文)

深入理解 EF Core:使用查询过滤器软删除数据(敬请期待)

概要

∮. EF Core 可以通过新的或已存在的关联关系创建一个新的实体。为此,它必须以正确的顺序来组织实体类,以便能够建立各类之间的关联。这使得开发人员很容易写出具有复杂关联关系的类。

∮. 当你调用 EF Core 的 Add 命令来添加一个新条目时,会发生很多事情:

EF Core 查找添加的类和其他类的所有关联。对于每个关联的类,它也会判断是否需要在数据库中创建一个新行,或者仅仅链接到数据库中现有的行。

它使用现有行的主键或伪主键为新添加的条目填充外键信息。

∮. EF Core 可以监测你从数据库读取的类的属性的变化。它通过已读入的类的隐藏副本来实现这一点。当你调用 SaveChanges 时,它会将每个读入的属性值与其原始值进行比较,并且会创建相应的数据更新命令。

∮. EF Core 的 Remove 方法将删除参数提供的实体类的主键所指向的数据行。如果被删除的类有外键关联,那么数据库会自动进行相关的操作(比如级联删除),但你可以更改删除的规则。

数据写入基础

提示:如果你已经对 EF Core 有一定的了解,那么你可以跳过这一部分,这只是一个简单的 EF Core 写入数据的例子。

在这一节的介绍中,我将描述一下本文用到的数据库结构,然后给出一个简单的数据库写入示例。下面是类/表的基本关系图:

深入理解 EF Core:EF Core 写入数据时发生了什么?

这些表被映射到具有类似名称的类,例如 Book、BookAuthor、Author,这些类的属性名称与表的字段名称相同。由于篇幅有限,我不打算展开来讲这些类,但您可以在我的 GitHub 仓库[1]中查看这些类。

和读取数据一样,EF Core 将数据写入数据库也是五部分:

数据库服务器,如 SQL server, Sqlite, PostgreSQL…

映射到数据库的一个类或多个类—我将它们称为实体类

一个继承 EF Core 的 DbContext 的类,该类包含 EF Core 的配置

一个创建数据库的方法

最后,向数据库写入数据的命令

下面的单元测试代码来自我的 GitHub 创库[2],展示了一个简单的示例,它从现有数据库中读取 4 个 Book 实体及其关联的 BookAuthor 和 Authors 实体。

[Fact] public void TestWriteTestDataSqliteInMemoryOk() { //SETUP var options = SqliteInMemory.CreateOptions<EfCoreContext>(); using (var context = new EfCoreContext(options)) { context.Database.EnsureCreated(); //ATTEMPT var book = new Book { Title = "Test", Reviews = new List<Review>() }; book.Reviews.Add(new Review { NumStars = 5 }); context.Add(book); context.SaveChanges(); //VERIFY var bookWithReview = context.Books .Include(x => x.Reviews).Single() bookWithReview.Reviews.Count.ShouldEqual(1); } }

现在,如果我们将单元测试代码对应到上面的 5 部分,结果是这样的:

数据库服务器——第 5 行:我选择了一个 Sqlite 数据库服务器,在本例中是 SqliteInMemory.CreateOptions 方法,它使用我的一个 NuGet 包 EfCore.TestSupport 创建了一个内存数据库(内存中的数据库对于单元测试非常有用,因为你可以为这个测试建立一个新的空数据库)。

实体类——和上一篇结构差不多,但是多了一个与 Book 关联的 Review 实体类。

一个继承 DbContext 的类——第 6 行:EfCoreContext 类继承了 DbContext 类并配置了从类到数据库的映射关系(你可以在我的 GitHub 仓库[3] 中查看该类)。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpxfsf.html