20

我有一个包含大约 16,500 个城市的表的数据库,以及该数据库的 EF 数据模型(数据库优先)。我使用代码将它们预加载到内存中:

Db.Cities.Load()

...然后在使用它们时,我尝试了以下每个查询:

Dim cities() As String = Db.Cities.Select(Function(c) c.CityName).ToArray

Dim cities() As String = Db.Cities.Local.Select(Function(c) c.CityName).ToArray

第一个查询很快(约 10 毫秒),但第二个查询第一次运行大约需要 2.3 秒(尽管在此之后调用它比第一个查询快)。

这没有任何意义,因为 SQL Server Profiler 会验证第一个查询是否正在访问另一台计算机上的数据库,但第二个不是!

我试过关闭Db.Configuration.AutoDetectChangesEnabled,我试过预先生成视图。

我该怎么做才能.Local更快?(并非所有运行此应用程序的客户端都将在快速 LAN 上。)

4

3 回答 3

9

我使用Resharper的便捷功能走过了该Local物业的源头。如果您正在运行的只是上述三行,您将首先看到一个可能不是您的问题的调用。但是随后 EF 为它创建了一个新的 ObservableCollection并逐项填充它。在第一次通话时,其中任何一个都可能代价高昂。DetectChangesLocal

直接针对 EF 的查询DbSet将路由到我确定直接访问内部本地缓存的 EF 数据库提供程序。

于 2012-09-05T07:54:18.000 回答
6

以下扩展方法将返回一个包含 DbSet 的本地缓存实体,而不会因检测上下文更改和创建对象IEnumerable<T>的方法而产生启动开销。DbSet.Local()ObservableCollection<T>

<Extension()>
Public Function QuickLocal(Of T As Class)(ByRef DbCollection As DbSet(Of T)) As IEnumerable(Of T)
    Dim baseType = DbCollection.[GetType]().GetGenericArguments(0)
    Dim internalSet = DbCollection.GetType().GetField("_internalSet", Reflection.BindingFlags.NonPublic Or Reflection.BindingFlags.Instance).GetValue(DbCollection)
    Dim internalContext = internalSet.GetType().GetProperty("InternalContext").GetValue(internalSet, Nothing)
    Return DirectCast(internalContext.GetType.GetMethod("GetLocalEntities").MakeGenericMethod(baseType).Invoke(internalContext, Nothing), IEnumerable(Of T))
End Function

调用.QuickLocal包含 19,679 个实体的 DbSet 需要 9 毫秒,而.Local第一次调用需要 2121 毫秒。

于 2012-09-05T17:50:24.867 回答
0

为什么不简单地从第一个查询中保存字符串列表并改用它。

List<string> cities = db.Cities.Select( x=>x.CityName).ToList();

由于 Select 可能会执行一些一致性检查,因此本地可能会更慢。

于 2012-09-03T05:37:28.677 回答