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

CRUD 的最后一部分是 DELETE,这在某些情况很简单,你只需要调用 context.Remove()。在另一些情况它很复杂,例如,当你删除另一个实体类依赖的实体类时会发生什么?

删除映射到数据库的实体类的方法是 Remove。举个例子,我加载一个特定的 Book,然后删除它。

var book = context.Books .Single(p => p.Title == "Quantum Networking"); context.Remove(book); context.SaveChanges();

它的阶段如下:

加载要删除的 Book 实体类。这会获取它的所有属性数据,但对于删除,您实际上只需要实体类的主键。

调用 Remove 方法其实是将 Book 的状态标记为 Deleted。这些信息会有序地存储在跟踪快照中。

最后,SaveChanges 创建一个 SQL DELETE 命令,该命令与任何其他数据库更改一起发送到数据库,并且在一个 SQL 事务中。

这看起来很简单,但这里发生了一些重要的事情,从代码看并不明显。原来书名为“Quantum Networking”的书有其他一些实体类关联到到它——在某个特定的测试用例中,书名为“Quantum Networking”的书关联到以下实体类:

两个 Review

一个 PriceOffer

一个 BookAuthor

现在,Review、PriceOffer 和 BookAuthor 实体类只与这本书相关——我们使用术语叫依赖于 Book 实体类。因此,如果这本书被删除了,那么这些 Review、PriceOffer 和所关联的 BookAuthor 数据行也应该被删除。如果不删除,那么数据库的关联关系就是不正确的,SQL 数据库将抛出异常。那么,为什么做这个删除工作?

这里所发生的都是因为设置了级联删除,级联删除规则设置了 Books 表和三个依赖表之间的数据库关系。
下面是 EF Core 为创建 Review 表而生成的 SQL 命令的一个示例:

CREATE TABLE [Review] ( [ReviewId] int NOT NULL IDENTITY, [VoterName] nvarchar(max) NULL, [NumStars] int NOT NULL, [Comment] nvarchar(max) NULL, [BookId] int NOT NULL, CONSTRAINT [PK_Review] PRIMARY KEY ([ReviewId]), CONSTRAINT [FK_Review_Books_BookId] FOREIGN KEY ([BookId]) REFERENCES [Books] ([BookId]) ON DELETE CASCADE );

CONSTRAINT 语句部分定义了约束规则,该约束表示 Review 通过 BookId 列链接到 Books 表中的一行。在该约束的最后,你将看到关于 DELETE 级联的规则。它告诉数据库,如果它链接的书被删除了,那么这个 Review 也应该被删除。这意味着书的删除是允许的,因为所有相关的行也被删除了。

这是非常有用的,但有时候想要更改删除规则怎么办?比如我决定不允许删除客户订单中存在的书。为了做到这一点,我在 DbContext 中添加了一些 EF Core 配置来改变删除规则,如下:

public class EfCoreContext : DbContext { private readonly Guid _userId; public EfCoreContext(DbContextOptions<EfCoreContext> options) : base(options) public DbSet<Book> Books { get; set; } //… 其它 DbSet<T> protected override void OnModelCreating(ModelBuilder modelBuilder { //… 其它代码 modelBuilder.Entity<LineItem>() .HasOne(p => p.ChosenBook) .WithMany() .OnDelete(DeleteBehavior.Restrict); } }

一旦该配置应用到数据库,就不会生成 SQL 语句的 DELETE CASCADE。这意味着,如果你试图删除客户订单中的一本书,那么数据库将返回一个错误,EF Core 将把这个错误变成一个异常。

这使你对正在发生的事情有一个更深的了解,但是还有相当多的内容我没有介绍(但我在我的书中介绍了)。这里有一些关于删除我还没有提到的事情:

实体类之间可以有 required 关系(依赖关系)和 optional 关系,EF Core 为每种类型使用不同的规则。

EF Core 可以通过设置 DeleteBehavior 来设置级联删除规则,当实体类存在循环关联关系时,可以用它避免一些错误——一些数据库在发现循环删除时会抛出错误。

你可以在调用 Remove 方法时提供一个新的只有主键有值的类来删除实体类。这在处理只返回主键的场景非常有用。

总结

本文我介绍了 CRUD 中的新增、更新和删除部分,前一篇文章介绍了读取部分。

正如您所看到的,使用 EF Core 在数据库中创建记录很容易,但内部很复杂。你通常不需要知道 EF Core 或数据库中发生了什么,但了解一些细节可以让你更好地利用 EF Core 的优势。

更新也很简单——只需在你读入的实体类中更改一个或多个属性,当你调用 SaveChanges 时,EF Core 会找到已更改的数据,并构建 SQL 命令更新数据库。这适用于非关系属性(如图 Book 的 Title 属性)和导航属性(你可以在他们的关系)。

最后,我们看了一个删除案例。同样很容易使用,但很多处理也是在背后执行的。​ 另外,敬请关注我的下一篇文章,我将讨论所谓的“软删除”。如果你设置了一个标志,EF Core 就不会再看到这个实体类了,它仍然在数据库中,但它是隐藏的。

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

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