繁体   English   中英

SingleOrDefault 和 FirstOrDefault 返回缓存数据

[英]SingleOrDefault and FirstOrDefault returning cached data

我之前编写的一些代码使用Find()方法通过主键检索单个实体:

return myContext.Products.Find(id)

这很有效,因为我将这段代码放入一个通用类中,并且每个实体都有一个不同的字段名称作为其主键。

但是我不得不替换代码,因为我注意到它正在返回缓存数据,并且每次调用时我都需要它从数据库中返回数据。 微软的文档证实这是Find()的行为。

所以我将代码更改为使用SingleOrDefaultFirstOrDefault 我没有在文档中找到任何说明这些方法返回缓存数据的内容。

现在我正在执行这些步骤:

  1. 通过 EF 保存实体。
  2. 在 SSMS 中执行 UPDATE 语句以更新最近保存的记录的描述字段。
  3. 使用SingleOrDefaultFirstOrDefault将实体检索到新的实体变量中。

返回的实体在“ Description字段中仍具有旧值。

我已经运行了 SQL 跟踪,并验证了在第 3 步期间正在查询数据。这让我感到困惑 - 如果 EF 往返于数据库,为什么它返回缓存数据?

我在网上搜索过,大多数答案都适用于Find()方法。 此外,他们提出了一些解决方案,这些解决方案仅仅是解决方法(处理DbContext并实例化一个新的)或对我不起作用的解决方案(使用AsNoTracking()方法)。

如何从数据库中检索我的实体并绕过 EF 缓存?

您所看到的行为在 Microsoft 的How Queries Work文章中的第 3 点下进行了描述:

  1. 对于结果集中的每个项目

一种。 如果这是一个跟踪查询,EF 检查数据是否表示上下文实例的更改跟踪器中已有的实体

  • 如果是,则返回现有实体

这篇博文中对它的描述要好一些:

事实证明,实体框架使用身份映射模式。 这意味着一旦具有给定键的实体被加载到上下文的缓存中,只要该上下文存在,它就永远不会再次加载。 因此,当我们第二次访问数据库以获取客户时,它从数据库中检索了更新的851记录,但由于客户851已经加载到上下文中,因此它忽略了数据库中更新的记录( 更多详细信息)。

所有这一切都是说,如果您进行查询,它会首先检查主键以查看它是否已经在缓存中。 如果是这样,它将使用缓存中的内容。

你如何避免它? 第一个是确保您的DbContext对象不会存活太久。 DbContext对象仅设计用于一个工作单元。 如果您将其保留太久,则会发生不好的事情,例如过多的内存消耗。

  • 您是否需要检索数据以显示给用户? 创建一个DbContext以获取数据并丢弃该DbContext
  • 您需要更新记录吗? 创建一个新的DbContext ,更新记录并丢弃该DbContext

这就是为什么当您在 ASP.NET Core 中使用具有依赖项注入的EF Core ,它创建时具有作用域生命周期,因此任何DbContext对象仅在一个 HTTP 请求的生命周期内有效。

在极少数情况下,您确实需要为已有对象的记录获取新数据,您可以像这样使用EntityEntry.Reload() / EntityEntry.ReloadAsync

myContext.Entry(myProduct).Reload();

但是,如果您只知道 ID,那对您没有帮助。

如果你真的需要重新加载的实体,您只对,你可以做这样一些奇怪的ID:

private Product GetProductById(int id) {
    //check if it's in the cache already
    var cachedEntity = myContext.ChangeTracker.Entries<Product>()
                           .FirstOrDefault(p => p.Entity.Id == id);
    if (cachedEntity == null) {
        //not in cache - get it from the database
        return myContext.Products.Find(id);
    } else {
        //we already have it - reload it
        cachedEntity.Reload();
        return cachedEntity.Entity;
    }
}

但同样,这应该只在有限的情况下使用,当您已经解决了任何长期存在的DbContext对象的情况时,因为不需要的缓存不是唯一的后果。

好吧,我也遇到了同样的问题,终于找到了答案,
你做的一切都是正确的,这就是 EF 的工作方式。 您可以将.AsNoTracking()用于您的目的:

return myContext.Products.AsNoTracking().Find(id)

确保您using Microsoft.EntityFrameworkCore;添加using Microsoft.EntityFrameworkCore; 在顶部。

它像魔术一样工作

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM