我正在使用 EF Core 7.0,并通过重写 SaveChangesAsync() 使用以下代码实现了审核跟踪。
目前,要更新实体,我们首先需要通过Id查找,这需要先往返数据库加载实体,然后更改字段并再次往返数据库保存。
var parent = await context.Users.FindAsync(10);
parent.EmailAddress = "[email protected]";
await context.SaveChangesAsync();
审计结果
问题:是否可以在不先查找实体的情况下更新实体,并且仍然生成审核跟踪?
我尝试了 context.Update、context.Attach 和 context.ExecuteUpdate。
使用 context.Update(Parent),它将整个 Parent 标记为 EntityState.Modified,导致大量审计跟踪,因为它认为所有字段都已修改。与附加相同。 ExecuteUpdate 根本不使用 SaveChangesAsync,这是我实现审核跟踪的地方。
var parent = await context.Users.AsNoTracking().FirstOrDefaultAsync(c => c.Id == 9);
parent.EmailAddress = "[email protected]";
context.Update(parent);
await context.SaveChangesAsync();
审计结果
如果 EF 不首先获取实体来更新它,您希望 EF 如何生成审核跟踪(旧 -> 新)?第一种方法是我推荐使用的方法,尽管我通常使用
Single
/SingleOrDefault
/First
/FirstOrDefault
方法而不是 Find
。 Find
的问题在于你无法急切加载相关实体,而使用Single
/First
...你可以。
读取一行进行编辑然后更新它的模式的额外考虑是断言您正在执行有效的更新,特别是在 Web 应用程序和面向公众的 Web 应用程序中。例如,在许多情况下,用户只会更新属于他们或他们被授权更新的特定行。如果用户可能执行诸如更新送货地址之类的操作,则应确保正在更新的订单是他们的,并且处于可更新的状态,这意味着读取该数据。接受包括订单 ID 在内的参数并仅尝试执行
UPDATE Orders SET DeliveryAddress = @p0 WHERE OrderId = @p1
的等效操作很容易被篡改,其中恶意用户或错误代码可能会发送该用户没有业务编辑的 OrderID 参数,或者客户端允许基于以下内容进行更新陈旧状态。 (例如,如果订单处于特定状态,则仅在客户端启用编辑/保存,但在提交之前可能已更改)
将读取作为更新的一部分通常是验证操作、检查并发字段的好主意,并且由于它是通过 PK 获取的,因此相对于它提供的值来说,它不应该造成任何显着的性能成本。