当我将.AsNoTracking()埋在存储库中时,为什么不启用它?

时间:2018-08-02 19:48:03

标签: c# entity-framework entity-framework-6 repository-pattern

我工作的代码库使用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与我们现有的当前存储库代码一起使用?

1 个答案:

答案 0 :(得分:1)

查询1调用FirstOrDefault并从数据库返回第一个结果。因此,即使数据库包含5000个匹配项,也只会从数据库中返回第一个匹配项。

您的存储库包装器调用调用ToList并从数据库返回所有结果,然后您的代码才从已返回的所有内容中获取第一个结果。因此,如果ToList返回5000个匹配项,则所有匹配项都将被加载到内存中,然后您选择第一个匹配项。


注释1

  

(我猜它正在再次缓存)

别猜。使用性能分析工具(例如 Sql Server Profiler ,如果您的数据库是Sql Server),查看实际执行的查询是什么,并将其与期望的查询进行比较(基于第一个代码示例) “出色地工作”)。在这种情况下,您可以捕获查询,并注意到第一个可能返回单个结果,而另一个返回许多其他记录。


注释2

我想指出,将实体框架功能包装在存储库或UoW模式中几乎总是一个坏主意。

类型DbContext是UoW模式的实现,类型DbSet<T>是存储库模式的实现。为什么要在自己的相同模式实现中重新包装这些类型?您没有添加任何价值,仅添加了更多代码和较差的抽象性,从而导致难以阅读,调试和正确使用代码。