web-dev-qa-db-ja.com

DbSet.Findメソッドは、IDの.SingleOrDefaultに比べて途方もなく遅い

次のコードがあります(データベースはSQL Server Compact 4.0です)。

Dim competitor=context.Competitors.Find(id)

これをプロファイリングすると、Findメソッドは、わずか60レコードのテーブルから競合他社を取得するのに300 + msかかります。

コードを次のように変更すると:

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

Fluent APIを使用して、CompetitionSubscriptionsおよびOpponentMeetingsの多対多の関係を定義しました。

CompetitorクラスのIDプロパティはLongで、Code Firstによってデータテーブルの主キーを持つIdentity列に変換されます(SQL Server Compact 4.0)

ここで何が起こっているのですか?

34
Dabblernl

Findは内部でDetectChangesを呼び出しますが、SingleOrDefault(または通常は任意のクエリ)は呼び出しません。 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と同じくらい高速になります(エンティティが既にコンテキストにアタッチされている場合は高速です)。

自動変更検出は、複雑でやや謎めいたテーマです。この4部構成のシリーズでは、非常に詳細な説明が見つかります。

(パート1へのリンク、パート2、3、および4へのリンクはその記事の冒頭にあります)

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

55
Slauma