我正在与EF core 6.0合作一个.NET6项目.该项目是使用Clean架构和CQRS模式构建的.

我的问题更笼统,我更多地是在寻求意见,而不是直接的答案.

因此,假设我有一个从数据库中删除用户的命令.我们就叫它DeleteUserByIdCommand吧.该命令获取用户ID并判断该用户是否存在,如果该用户存在,则将其删除.

在命令结束时,我调用context.SaveChanges(),因此删除操作实际上是在数据库中进行的.当我添加另一个命令时,问题就来了,让我们将其命名为DeleteUsersByIdBulkCommand.在这个命令中,为了不重复我自己,我希望使用我现有的DeleteUserByIdCommand.新命令接受一组用户ID.但是,通过这种方式,如果我有context.SaveChanges()个用户要删除,SaveChanges()和GET请求将被执行context.SaveChanges()次,这对性能非常糟糕.

对我来说,只有两个简单的 Select ,我都试过了:

  1. 只是不要重复使用DeleteUserByIdCommand,并在新代码中编写新代码.这样做的问题是,例如,如果我想要扩展命令,比如从其他数据库或第三方API中删除用户,我将不得不在两个地方更改代码.
  2. 另一种解决方案是在DeleteUserByIdCommand之间有一个属性,称为ShouldCallSaveChanges,它是布尔型的.缺省情况下它是真的,但是当我从DeleteUsersByIdBulkCommand调用这个命令时,它将被设置为假,并且我将在Foreach之后执行SaveChanges().

我的问题是,对于这种情况,是否有一些严格的规定?在这里重复代码是不是有问题,或者对于这种情况可能有一些特定的解决方案.我想听取您的意见或您可能有的其他建议:)

推荐答案

这是一个常见问题的例子,"为什么我的批量数据操作语句如此慢得离谱?"对于典型的RDBMS服务器,通常的答案是"因为数据操作中的大部分工作都发生在提交阶段".

在纯SQL应用程序中,这意味着您应该执行如下操作来高效地删除多行:

BEGIN TRANSACTION;
DELETE FROM mytable WHERE whatever;
DELETE FROM mytable WHERE whatever;
DELETE FROM mytable WHERE whatever;
COMMIT;

当您处于事务中时,RDBMS会对操作进行批量处理,当您提交时,它会完成应用这些操作的所有工作.(这是一种过于简单化,但对于几乎所有涉及不到数万次操作的应用程序来说,这是一个非常有用的心理模型.)

这如何转化为EF Core?这.SaveChanges()份文件是这样写的:

对于大多数数据库提供程序来说,SaveChanges是事务性的.

您提到的代码通过在每个操作后调用.SaveChanges(),隐式地 for each 操作执行一个事务.这使得代码的调用者很容易使用.但这让它对像这样的大规模行动来说并不是很好

foreach (var id in arrayOfIds) {
   ctx.DeleteUserById(id);
}

构建代码 struct 的最简单方法--不必构建某种神奇的批量模式属性--总是显式调用.SaveChanges(),而不是隐式调用.

foreach (var id in arrayOfIds) {
   ctx.DeleteUserById(id);
}
ctx.SaveChanges();

这将与重写代码的速度一样快(除非您有成千上万的用户要同时删除).但您必须记住调用.SaveChanges().

当我这样做时,我实现了一个实现IDisposable的更新程序类,并在它的Dispose方法中调用了.SaveChanges.

然后我会做这样的事情.

using (var upd = new MyUpdater()) {
  upd.DeleteUserById (whatever);
  upd.AddUser (whatever);
  upd.ChangeUser (whatever);
 }

当我的MyUpdater实例在使用中超出范围时,它的.Dispose方法将被自动调用.这还有一个额外的好处,那就是使用单个数据库事务来执行一系列不同的操作,如果您想这样做的话.

对.Dispose()的超出范围调用总是会发生,即使在某个地方出现了异常.

Sql相关问答推荐

如何实现一个广泛的多级自连接PostgreSQL查询?

JSON列之间的Postgr聚合

用于匹配红旗和绿旗的SQL查询

Select 起始参数和截止参数之间的间隔,包括与期间重叠的参数

LEFT JOIN不显示计数0我期望的方式

在Oracle中,如何删除具有特定值的行,仅当它是重复的行?

SQL:如何将相应位置的两个数组中的元素组合在一起

使用列表作为参数进行 Select ,如果为空,则在PostgreSQL中不使用参数进行 Select

从重复值中获取最新值

在 SQL Server 中合并两个 XML 列

DbUp for sqlserver 在 dbo 授权下为非 dbo 用户创建架构

具有分组条件的不同计数 (DAX)

我们可以使用连接改进包含多个子查询的查询吗

基于变量的条件 WHERE 子句

根据不同日期标准分配组的逻辑

如何根据共同列值从两个表中包含列,但只包含左表中的行

如何获取每个组中最近的n条记录并将它们聚合成数组

PostgreSQL 如何在一组项目及其数量上找到完全相同的订单?

SQL:获取连接表的第一个项目

使用 json_agg 从 SQL 查询获取 [null] 响应