次のコードがあります(データベースは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)
ここで何が起こっているのですか?
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/