实体框架依赖于跟踪机制(请参阅this and this)。它本身不是缓存,但它可以产生类似的效果,尽管主要目标之一是跟踪更改,以便将它们传播到数据库。
更改跟踪“绑定”到上下文的具体实例(即不在不同实例/全局之间共享)。变更跟踪的重要功能之一是身份解析:
由于跟踪查询使用更改跟踪器,因此 EF Core 将在跟踪查询中进行身份解析。当具体化实体时,EF Core 将从更改跟踪器返回相同的实体实例(如果已被跟踪)。如果结果多次包含相同的实体,则每次出现都会返回相同的实例。无跟踪查询不使用更改跟踪器,也不进行身份解析。因此,即使结果中多次包含同一实体,您也会返回实体的新实例。
如果数据库在同一上下文中的请求之间“外部”更新,这可能会导致数据过时:
using (var ctx1 = new AppContext())
{
var entity = await ctx1.SomeEntity
.Where(e => e.Id == 1)
.FirstOrDefaultAsync();
using (var ctx2 = new AppContext())
{
var theSameEntity = await ctx2.SomeEntity
.Where(e => e.Id == 1)
.FirstOrDefaultAsync();
theSameEntity.SomeTextField = "Updated"; // assuming it had another value
await ctx2.SaveChangesAsync();
}
var entity1 = await ctx1.SomeEntity
.Where(e => e.Id == 1)
.FirstOrDefaultAsync()
var referenceEquals = object.ReferenceEquals(entity, entity1); // True
var field = entity1.SomeTextField; // field will have the original value, not "Updated"
}
第二次调用要快得多,我相信这是因为 EF 缓存了我的查询和元数据
其潜在原因有多种:
- 如果第一个查询是在此应用程序运行中第一次执行此查询,则 EF Core 可以缓存查询翻译结果,以便在后续查询(跨上下文实例)中重用。此外,如果实体已被跟踪,则 EF 的当前迭代将不会重新创建和重新映射获取的数据。不确定我是否记得正确,但如果已请求已跟踪的实体,EF/EF Core 的先前迭代可能会完全跳过查询数据库,但我在这里可能是错的。
还有几个因素影响“启动时间”,即在计算机上执行第一个操作的时间DbContext
当那个时候DbContext
type 在应用程序中首次使用。
- Model initialization - see the compiled models section of the docs:
创建一个DbContext
实例不会导致 EF 模型初始化,导致模型初始化的典型第一个操作包括调用DbContext.Add
或执行第一个查询。
- 连接管理 - EF Core 将需要建立与数据库服务器的连接,然后将其汇集并重用(更多一点 -here),在某些情况下可能会花费相当长的时间,这将不适用于后续查询(如果底层物理连接池有足够的可用连接)。
-
即时编译- 第一次调用某个方法时,运行时需要编译它,在使用 EF Core 的上下文中它应该可以忽略不计(尽管理论上与EF Core 编译模型)但仍然值得一提(显然适用于第一次调用某些特定方法)。
但它似乎没有缓存查询的实际结果。
它同时是部分正确和部分错误的。如前所示 - 如果数据实际上已更改,EF 将不会更新跟踪的实体。但如果数据被删除,EF 会发现:
using (var ctx1 = new AppContext())
{
var entity = await ctx1.SomeEntity
.Where(e => e.Id == 1)
.FirstOrDefaultAsync();
using (var ctx2 = new AppContext())
{
var theSameEntity = await ctx2.SomeEntity
.Where(e => e.Id == 1)
.FirstOrDefaultAsync();
ctx2.DeliveryComment.Remove(theSameEntity); // remove
await ctx2.SaveChangesAsync();
}
var entity1 = await ctx1.SomeEntity
.Where(e => e.Id == 1)
.FirstOrDefaultAsync()
var isNull = entity1 == null; // True
}