我工作的代码库使用Entity Framework和存储库模式。为了获得良好的性能(即150 ms的数据库调用而不是1500 ms的调用),我发现在选择数据以避免缓存时需要使用AsNoTracking。
以下代码非常出色:
using (var context = new DeviceCloudModel())
{
var model = context.Devices
.AsNoTracking()
.Include(d => d.DeviceSettings)
.Where(d => d.SerialNumber == serialnumber && o.IsActive).FirstOrDefault();
}
但是,这并没有,而且也很慢(我想它正在再次缓存):
var predicate = (filter ?? (x => true)).Compile(); // assume filter is always null
var model = _repository
.Get(o => o.SerialNumber == serialnumber && o.IsActive)
.Where(predicate)
.FirstOrDefault();
这是我的存储库Get方法:
public override List<Device> Get(Expression<Func<Device, bool>> filter = null)
{
var predicate = (filter ?? (x => true)).Compile();
var ret = _context.Devices
.AsNoTracking()
.Include(d => d.DeviceSettings)
.Where(predicate)
.ToList();
return ret;
}
我们想提高性能,但我们不想摆脱存储库模式或在其之上施加难看的骇客。有什么方法可以将AsNoTracking与我们现有的当前存储库代码一起使用?
答案 0 :(得分:1)
查询1调用FirstOrDefault
并从数据库返回第一个结果。因此,即使数据库包含5000个匹配项,也只会从数据库中返回第一个匹配项。
您的存储库包装器调用调用ToList
并从数据库返回所有结果,然后您的代码才从已返回的所有内容中获取第一个结果。因此,如果ToList
返回5000个匹配项,则所有匹配项都将被加载到内存中,然后您选择第一个匹配项。
(我猜它正在再次缓存)
别猜。使用性能分析工具(例如 Sql Server Profiler ,如果您的数据库是Sql Server),查看实际执行的查询是什么,并将其与期望的查询进行比较(基于第一个代码示例) “出色地工作”)。在这种情况下,您可以捕获查询,并注意到第一个可能返回单个结果,而另一个返回许多其他记录。
我想指出,将实体框架功能包装在存储库或UoW模式中几乎总是一个坏主意。
类型DbContext
是UoW模式的实现,类型DbSet<T>
是存储库模式的实现。为什么要在自己的相同模式实现中重新包装这些类型?您没有添加任何价值,仅添加了更多代码和较差的抽象性,从而导致难以阅读,调试和正确使用代码。