EF 4.1でのエンティティの挿入がObjectContextと比較して非常に遅いのはなぜですか?


81

基本的に、1つのトランザクション内に35000個のオブジェクトを挿入します。

using(var uow = new MyContext()){
  for(int i = 1; i < 35000; i++) {
     var o = new MyObject()...;
     uow.MySet.Add(o);
  }
  uow.SaveChanges();
}

これは永遠にかかります!基になるObjectContextを(を使用してIObjectAdapter)使用すると、それでも低速ですが、約20秒かかります。DbSet<>線形検索を行っているようですが、これには2乗の時間がかかります...

この問題を見ている人は他にいますか?


3
私は何とか答えはこのようになりますことを信じて:stackoverflow.com/questions/5917478/...
ラディスラフMrnka

回答:


128

コメントでLadislavがすでに示しているように、パフォーマンスを向上させるには、自動変更検出を無効にする必要があります。

context.Configuration.AutoDetectChangesEnabled = false;

この変更検出は、DbContextAPIでデフォルトで有効になっています。

APIとのDbContext動作が大きく異なる理由は、自動変更検出が有効になっている場合、APIの関数よりもObjectContext多くのDbContextAPIのDetectChanges関数が内部で呼び出すObjectContextためです。

ここではDetectChanges、デフォルトで呼び出される関数のリストを見つけることができます。彼らです:

  • AddAttachFindLocal、またはRemove上のメンバーDbSet
  • GetValidationErrorsEntryまたはSaveChanges上のメンバーDbContext
  • 上のEntries方法DbChangeTracker

特に、あなたが経験したパフォーマンスの低下の原因AddDetectChangesなる呼び出し。

これとは対照的に、ObjectContextAPIは、上記の他の対応するメソッドではなく、でDetectChanges自動的に呼び出されます。これが、のデフォルトのパフォーマンスが速い理由ですSaveChangesAddObjectObjectContext

なぜ彼らはこのデフォルトの自動変更検出DbContextを非常に多くの機能に導入したのですか?よくわかりませんが、無効にDetectChangesして適切な場所で手動で呼び出すことは高度であると見なされ、アプリケーションに微妙なバグを簡単に導入する可能性があるため、注意して使用してください


@Ladislav:挿入の問題だけを探していたので、これは見つかりませんでした:-(
Hartmut

説明してくれてありがとう。私は実際にcontext.Configuration.AutoDetectChangesEnabled = falseを呼び出していましたが、Seed()メソッドでデータベースを構築しているときに呼び出しました。これでデフォルトが設定されると思いました。インスタンスごとに呼び出す必要があることに気づいていませんでした。ありがとう!
Hartmut

3
@Hartmut:派生したDbContextのコンストラクター内で変更検出を無効にすると、常に無効になります。しかし、個人的には、無効にすると「微妙なバグが発生する可能性がある」というこの発言は、私を緊張させます。私はデフォルトで変更検出をオンにしており、パフォーマンスの向上が明らかで、問題を引き起こさないと確信しているあなたのようなコードブロックでのみ無効にします。
スラウマ2011年

私は、アプリケーションのパフォーマンスが重要な部分のみをテストしていたことに同意します。生産コードでは、バルク挿入などのような場合に、それを制限するのがベストです
ハルトムート

この答えをありがとう。そこにはたくさんの情報がありますが、これは追いかけっこになります!
フレッドウィルソン

12

EF 4.3 CodeFirstを使用した小さな経験的テスト:

AutoDetectChanges = true:23秒で1000個のオブジェクトを削除しました

AutoDetectChanges = falseで1000個のオブジェクトを削除:11秒

AutoDetectChanges = trueで1000個のオブジェクトを挿入:21秒

AutoDetectChanges = falseで1000個のオブジェクトを挿入:13秒


1
Zaxに感謝します。質問によると、35,000での結果はどうですか?元の質問では、パフォーマンスが2次関数的に低下することがわかります
Daniel Dyson

9

.netcore 2.0では、これは次の場所に移動されました。

context.ChangeTracker.AutoDetectChangesEnabled = false;


1

あなたがここで見つけた答えに加えて。データベースレベルでは、追加するよりも挿入する方が手間がかかることを知っておくことが重要です。データベースは、新しいスペースを拡張/割り当てる必要があります。次に、少なくとも主キーインデックスを更新する必要があります。インデックスは更新時にも更新される可能性がありますが、それほど一般的ではありません。外部キーがある場合は、それらのインデックスも読み取って、参照整合性が維持されていることを確認する必要があります。トリガーも同じように更新に影響を与える可能性がありますが、役割を果たすことができます。

そのデータベース作業はすべて、ユーザーエントリによって開始される毎日の挿入アクティビティで意味があります。ただし、既存のデータベースをアップロードするだけの場合、または多数の挿入を生成するプロセスがある場合。それを最後まで延期することによって、それをスピードアップする方法を検討したいかもしれません。通常、挿入中にインデックスを無効にするのが一般的な方法です。場合によっては実行できる非常に複雑な最適化があり、それらは少し圧倒される可能性があります。

一般に、挿入は更新よりも時間がかかることを知っておいてください。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.