与ID上的.SingleOrDefault相比,DbSet.Find方法非常慢

时间:2012-07-27 10:42:35

标签: entity-framework ef-code-first dbcontext sql-server-ce-4 dbset

我有以下代码(数据库是SQL Server Compact 4.0):

Dim competitor=context.Competitors.Find(id)

当我对此进行分析时,Find方法需要300 + ms才能从仅有60条记录的表中检索竞争对手。

当我将代码更改为:

Dim competitor=context.Competitors.SingleOrDefault(function(c) c.ID=id)

然后在3毫秒内找到竞争对手。

竞争者类:

Public Class Competitor
    Implements IEquatable(Of Competitor)

    Public Sub New()
        CompetitionSubscriptions = New List(Of CompetitionSubscription)
        OpponentMeetings = New List(Of Meeting)
        GUID = GUID.NewGuid
    End Sub

    Public Sub New(name As String)
        Me.New()
        Me.Name = name
    End Sub

    'ID'
    Public Property ID As Long
    Public Property GUID As Guid

    'NATIVE PROPERTIES'
    Public Property Name As String

    'NAVIGATION PROPERTIES'
    Public Overridable Property CompetitionSubscriptions As ICollection(Of CompetitionSubscription)
    Public Overridable Property OpponentMeetings As ICollection(Of Meeting)
End Class

我使用流畅的API为CompetitionSubscriptionsOpponentMeetings定义了多对多的关系。

Competitor类的ID属性是Long,它由Code First转换为带有数据表中主键的Identity列(SQL Server Compact 4.0)

这里发生了什么?

1 个答案:

答案 0 :(得分:55)

Find在内部调用DetectChangesSingleOrDefault(或通常是任何查询)都不会。 DetectChanges是一项昂贵的操作,因此这就是Find速度较慢的原因(但如果实体已经加载到上下文中,它可能会变得更快,因为Find不会运行查询而只是返回加载的实体。)

如果你想对很多实体使用Find - 例如循环 - 你可以像这样禁用自动更改检测(不能用VB编写它,所以是一个C#例子):

try
{
    context.Configuration.AutoDetectChangesEnabled = false;
    foreach (var id in someIdCollection)
    {
        var competitor = context.Competitors.Find(id);
        // ...
    }
}
finally
{
    context.Configuration.AutoDetectChangesEnabled = true;
}

现在,Find不会在每次调用时调用DetectChanges,它应该与SingleOrDefault一样快(如果实体已经附加到上下文,则更快)。

自动变化检测是一个复杂且有些神秘的主题。在这个由四部分组成的系列中可以找到一个非常详细的讨论:

(链接到第1部分,第2,3和4部分的链接位于该文章的开头)

http://blog.oneunicorn.com/2012/03/10/secrets-of-detectchanges-part-1-what-does-detectchanges-do/