1

親オブジェクトに子オブジェクトを追加するのが非常に遅いという問題があります。何万もの子オブジェクト(この場合は33kレコード)がありますが、それらのどれも問題の親オブジェクトの子ではありません。速度が必要な場合のエンティティフレームワークの遅さ

私はそれが完了するまでに一分以上かかり、親への最初の子を追加します。

public class ParentEntity // POCO generated by EF TT4 templates 
{ 
    public virtual int Id { get; set; } 
    public virtual ICollection<ChildEntity> ChildEntities {} 
} 

public class ChildEntity // POCO generated by EF TT4 templates 
{ 
    public virtual int Id { get; set; } 
    public virtual int ParentEntityId { get; set; } 
    public virtual ParentEntity ParentEntity { get; set; } 
    public virtual Warehouse Warehouse { get; set; } 
    public virtual WarehouseLocation WarehouseLocation { get; set; } 
} 

public class Warehouse { // etc } // another POCO class 
public class WarehouseLocation { // etc } // another POCO class 

// somewhere in a controller action method... 
var parent = _parentEntityService.GetBy(id); 
var child = new ChildEntity{ ParentEntityId = id, 
          WarehouseId = id2, WarehouseLocationId = id3 }; 

// ChildEntities.Add() takes more than one minute to add the 
// first and only child to this parent 
// why would this be so incredibly slow? 

parent.ChildEntities.Add(child); 

EntityFrameworkでの速度の問題を発見アプローチする最良の方法は何ですか?

更新:EFProfが、それは3つのSQLクエリを発行することを示しています

SELECT * FROM ChildEntities where ParentId = id 
SELECT * FROM ChildEntities where WarehouseId = id2 
SELECT * FROM ChildEntities where WarehouseLocation = id3 

それだけで唯一の現在の子のためにそれらをロードする必要があるときに、なぜそれは、すべての単一のChildEntityのためにこれらをロードしていますか?

編集2:@LadislavMrnkaによると、追加のクエリはテンプレートのFixupメソッドによって発生します。しかし、私がこれらのメソッドをコメントアウトし、Fixupへの呼び出しをコメントアウトすると、それはまだ遅いです。これは、(それが私に削除されるように見える)フィックスアップを除去するための正しい方法ではありません:

public class ChildEntity { 
public virtual Warehouse Warehouse 
{ 
    get { return _warehouse; } 
    set 
    { 
     if (!ReferenceEquals(_warehouse, value)) 
     { 
      var previousValue = _warehouse; 
      _warehouse = value; 
      //FixupWarehouse(previousValue); // commented out 
     } 
    } 
} 
+0

このように変更してみてください。 'var parent = _parentEntityService.GetBy(id); var child = new ChildEntity {Foo = ""、Bar = ""、Parent = parent}; _ entityService.Save(子) ' –

+0

EFプロファイラ(http://efprof.com/)を使用してエンティティの内部を確認しますフレームワーク。 –

+0

この 'parent'エンティティには' ChildEntities'が多数ありますか? – Eranga

答えて

0

は、これは非常に私を助け:

context.Configuration.AutoDetectChangesEnabled = false; 

を私はこれがあなたのために行くかどうかわからないんだけどあなたもあなたの質問を正しく理解すれば、あなたはちょうどAddをやってみるとあなたにとっては遅いのですか?

これでもオブジェクトを挿入できますが、EFはオブジェクト階層をナビゲートする際にはるかに少ない労力しか要しません。私はエンティティを更新するときにこれが問題になると思いますが、わかりません。

第二の方法(あなたがDbContextを使用すると仮定)は、次のとおりです。

using (var dbCtx = new MyDataContext()) 
{ 
    var ctx = ((IObjectContextAdapter)dbCtx).ObjectContext; 

    var customers = ctx.CreateObjectSet<Customer>(); 

    customers.AddObject(customer); 
} 

ObjectContextが変化に関して、異なる内部で物事を処理しているため。私はその時点でソースを見つけることができません、私はそれを後で見つけようとします。

5

これはあなたの問題です:

var child = new ChildEntity 
       { 
        Foo = "", 
        Bar = "", 
        ParentEntityId = id, 
        WarehouseId = 1, 
        WarehouseLocationId = 1 
       }; 

parent.ChildEntities.Add(child); 

、それはすべてのPOCOテンプレートによって生成されたコードに隠されたフィックスアップコレクションについてです私見。フィックスアップ+遅延ロード=パフォーマンスの問題フィックスアップは、モデル内のすべてを同期させようとします。つまり、ナビゲーションプロパティまたはFKプロパティの片側を設定すると、関係の反対側のナビゲーションプロパティにも変更が反映されるようになります。問題は、ナビゲーションプロパティがロードされていない場合、遅延ロードをトリガーすることです。あなたのケースでは、倉庫を設定することは、第1 ChildEntityにナビゲーションプロパティを固定し、その後Warehouseインスタンス上でナビゲーションプロパティをに修復しするが、その子エンティティのコレクションは

SELECT * FROM ChildEntities where WarehouseId = some id 

同じ原因=>遅延ロードをロードされていませんでした試みたように見えますWarehouseLocationの場合に発生しました。最初のクエリは、親エンティティ上のロードされていないコレクションに子を追加した結果です。

解決方法は、テンプレートを修正してすべてのフィックスアップを取り除くことです(たとえば、EFv4のDbContext POCOテンプレート)。好き

public class DisableLazyLoadingScope : IDisposable 
{ 
    private readonly ObjectContext context; 

    public DisableLazyLoadingScope(ObjectContext context) 
    { 
     this.context = context; 
     context.ContextOptions.LazyLoadingEnabled = false; 
    } 

    public void Dispose() 
    { 
     context.ContextOptions.LazyLoadingEnabled = true; 
    } 
} 

そして、それを使用する:1+もうフィックスアップを使用)、または呼び出すことによって、この操作の遅延ロードをオフにしないでください:

context.ContextOptions.LazyLoadingEnabled = false; 

// Your insert logic here 

context.ContextOptions.LazyLoadingEnalbed = true; 

あなたも同じようにカスタムIDisposableをでコードをラップすることができます:

using (new DisableLazyLoadingScope(context) 
{ 
    // Your insert logic here 
} 
+0

ああ私は、ありがとう、理解する。フィックスアップはどのようなメリットをもたらしますか?私はそれがTT4テンプレートに入っていたので、それを持っています。必要がない場合は削除してもよかったです。私は今、EF4.1を使用していますが、テンプレートが最初に追加されたとき、以前のEFバージョンを使用していました。 –

+0

フィックスアップは、読み込まれたデータを同期して保持します。一方の側で 'WarehouseId'を設定した場合、' Warehouse'ナビゲーションプロパティは正しい 'Warehouse'エンティティを持ち、' ​​Warehouse'エンティティは関連エンティティのコレクションにあなたの子供を持っています。副作用は、遅延ロードを使用している場合、それらを使用したくなくてもそれらのエンティティをロードするということです。 –

+0

ChildEntityからフィックスアップを削除しましたが、まだ遅いです。私はすべてのエンティティからフィックスアップを削除しようとしています。私は4.1の自己追跡エンティティテンプレートを見ましたが、それはまだフィックスアップ(異なって実装されています): 'var previousValue = _warehouse; _warehouse = value; FixupWarehouse(previousValue); OnNavigationPropertyChanged( "Warehouse"); ' –

関連する問題