Cha*_*dSC 7 c# entity-framework-core
我之前编写的一些代码使用该Find()方法通过主键检索单个实体:
return myContext.Products.Find(id)
Run Code Online (Sandbox Code Playgroud)
这很有效,因为我将这段代码放入一个通用类中,并且每个实体都有一个不同的字段名称作为其主键。
但是我不得不替换代码,因为我注意到它正在返回缓存的数据,并且每次调用时我都需要它从数据库中返回数据。微软的文档证实这是Find().
所以我把我的代码改成使用SingleOrDefaultor FirstOrDefault。我没有在文档中找到任何说明这些方法返回缓存数据的内容。
现在我正在执行这些步骤:
SingleOrDefault
或将实体检索到新的实体变量中FirstOrDefault。返回的实体在该Description字段中仍然具有旧值。
我已经运行了 SQL 跟踪,并验证了在第 3 步期间正在查询数据。这让我感到困惑 - 如果 EF 正在往返数据库,为什么它返回缓存数据?
我在网上搜索过,大多数答案都适用于该Find()方法。此外,他们提出了一些解决方案,这些解决方案仅仅是解决方法(处理DbContext并实例化一个新的)或对我不起作用的解决方案(使用该AsNoTracking()方法)。
如何从数据库中检索我的实体并绕过 EF 缓存?
您所看到的行为在 Microsoft 的How Queries Work文章中的第 3 点下进行了描述:
- 对于结果集中的每个项目
一种。如果这是一个跟踪查询,EF 检查数据是否表示上下文实例的更改跟踪器中已有的实体
- 如果是,则返回现有实体
在这篇博文中对它的描述要好一些:
事实证明,实体框架使用身份映射模式。这意味着一旦具有给定键的实体被加载到上下文的缓存中,只要该上下文存在,它就永远不会再次加载。因此,当我们第二次访问数据库以获取客户时,它会
851从数据库中检索更新的记录,但由于客户851已经加载到上下文中,因此它忽略了数据库中的更新记录(更多 详细信息)。
所有这一切都是说,如果您进行查询,它会首先检查主键以查看它是否已经在缓存中。如果是这样,它将使用缓存中的内容。
你如何避免它?首先是确保您的DbContext对象不会存活太久。DbContext对象仅设计用于一个工作单元。如果您将其保留太久,则会发生不好的事情,例如过多的内存消耗。
DbContext以获取数据并丢弃该DbContext。DbContext,更新记录并丢弃它DbContext。这就是为什么当您在 ASP.NET Core 中使用带有依赖项注入的EF Core时,它创建时具有作用域生命周期,因此任何DbContext对象仅在一个 HTTP 请求的生命周期内有效。
在极少数情况下,您确实需要为已有对象的记录获取新数据,您可以像这样使用EntityEntry.Reload() / EntityEntry.ReloadAsync:
myContext.Entry(myProduct).Reload();
Run Code Online (Sandbox Code Playgroud)
如果您只知道 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;
}
}
Run Code Online (Sandbox Code Playgroud)
但同样,这应该只在有限的情况下使用,当您已经解决了任何长寿命DbContext对象的情况时,因为不需要的缓存不是唯一的后果。