如何将本地上下文中的实体与 EF Core 数据库中的实体进行比较?

How do I compare an entity in my local context with the one in the database in EF Core?

EF 核心 6 和 .NET 6。

假设我所有的实体都有一个 LastUpdateAt 属性,这是一个 DateTime,每次添加或修改实体时都会更新。

我从上下文中获取一个实体并将其显示给用户(网页、WPF window,等等)。在某个时候,用户单击“保存”按钮。

在我保存之前,我想检查在我得到我的副本后是否其他人更新了该实体。但是,我正在努力寻找如何做到这一点。

如果我查询上下文,它只会返回我已有的实体(包括我的用户所做的任何更改)。

如果我刷新实体,它会覆盖我上下文中的实体,从而丢失用户的更改。

如何检查数据库版本的时间戳是否比我上下文中的版本更新?

谢谢

由于我需要粘贴更长的文本,所以将讨论移至此处。在 this article 中说,在 SaveChanges() 期间,如果在此期间修改了数据库版本,它将抛出 DbUpdateConcurrencyException。在该异常中,您拥有所有 3 个值,您可以决定如何解决冲突:

解决并发冲突涉及将当前 DbContext 中的未决更改与数据库中的值合并。合并的值因应用程序而异,可能由用户输入决定。

可以使用三组值来帮助解决并发冲突:

当前值是应用程序试图写入数据库的值。 原始值是在进行任何编辑之前最初从数据库中检索到的值。 数据库值是当前存储在数据库中的值。

如果您正在加载一个实体,保持 DbContext 实例打开,更新该实体,然后保存到同一个 DbContext 实例,那么默认情况下您依赖 EF 来管理并发。这是继“最后一次获胜”之后。您可以通过在 LastUpdateAt 属性 上添加 [ConcurrencyCheck] 或通过 [Timestamp] 使用行版本来让 EF 管理并发性。如果基础数据已更新,这将导致 EF 无法更新。从那里你必须决定你想如何处理它。

如果您想自己执行并发检查,那么有几个选项。

  1. 使用分离实体或投影视图模型构建代码以缩短 DbContext 的生命周期。这通常会对您的代码性能带来 flow-on 好处,因为原始 longer-lived DbContext 可以很容易地找到导致膨胀的方法,或者如果存活时间过长则积累“中毒”实体。 Automapper 是一个很好的辅助工具,您可以使用 ProjectTo 获取视图模型,然后使用 Map(source, destination) 复制值。通过这种方式,您加载数据,包括最后修改的值,进行更改,然后在保存时加载数据,验证修改的值等。然后复制值并保存。

  2. 范围一个 DbContext 实例以在保存前检查数据。

.

private DateTime getFooLastUpdateAt(int fooId)
{
    using(var context = new AppDbContext())
    {
         var lastUpdateAt = context.Foos
             .Where(x => x.FooId == fooId)
             .Select(x => x.LastUpdateAt)
             .Single();
         return lastUpdateAt;
    }
}

这可以使用注入的 DbContext 工厂等来创建 DbContext 实例..