1

次のエンティティ モデルと関数を検討してください。

public class Order
{
  public int OrderId {get; set;}
  public int StatusId {get; set;}

  public virtual Status OrderStatus {get; set;}
}

public class Status
{
  public int StatusId {get; set;}
  public String Name { get; set;}
}

public void ShowOrders()
{
  //load all status entities.
  //Will EF check for these in object cache first when I access order.Status for
  //the first time?
  //Or perhaps even auto include them in materialised orders?
  context.Status.Load(); 

  //enumerate orders without explicit status include
  foreach(Order o in context.Orders.ToList())
  {
    //Get Status navigation property for each order
    //Will database be hit?
    Console.WriteLine("Order: {0:N}, Status: {1}", o.OrderId, o.OrderStatus.Name);
  }
}

私は明示的にできることを知っています:

context.Orders.Include(o=>o.OrderStatus).ToList();

n+1 の選択を防ぐために、注文のクエリ時に Status を含めます。また、チェックされOrder.OrderStatusているナビゲーション プロパティにアクセスし、可能であればデータベースにヒットする前に取得されたキャッシュされた Status オブジェクトを取得することを知っています。DbReferenceEntry.IsLoaded

私が疑問に思っているのは、参照エンティティが既にオブジェクトキャッシュにある場合、親エンティティが具体化されたときに (呼び出されていなくても) データが取り込まれているかどうかDbReferenceEntry.IsLoadedですDbReferenceEntry.CurrentValue.Include()

上記の例では、注文を列挙する前の呼び出しにより、すべての Status がオブジェクト キャッシュにあるにもかかわらずOrder.OrderStatus 、初めてアクセスしたときにデータベース クエリが実行されるのでしょうか?Status.Load()

4

2 に答える 2

0

EFは、ナビゲーションプロパティにアクセスするたびに最初にオブジェクトキャッシュをチェックするため、すべてのStatusエンティティがすでにロードされている場合は、Order.OrderStatusにアクセスしてデータベースクエリを発行しないでください。参照がすでにオブジェクトキャッシュにある場合、DbReferenceEntryはマテリアライゼーション中に入力されます。

発行されるクエリの数が心配な場合は、遅延読み込みをオフにすることを検討してください。自動クエリが発行される場合は、代わりにnullが返されます。

パフォーマンスガイダンスの詳細については、次の記事を参照してください:http: //msdn.microsoft.com/en-us/data/hh949853.aspx

于 2013-03-06T01:18:56.680 に答える
0

次のステートメントはテストしませんでした (いつ、どの SQL クエリがプロファイラーで実際に実行されるかを監視することによって)。これらは単なる推測です。

注文を列挙する前に Status.Load() を呼び出すため、すべての Status がオブジェクト キャッシュにあるにもかかわらず、データベース クエリが実行されますか?

データベースクエリが実行されない場合、その理由は具体的に呼び出されたということではないと確信しています。これは、実行時のデータベース テーブルのスナップショットにすぎません。注文を列挙した時点で、EF はその間にテーブルが変更されておらず、追加の行が挿入されていることを確認できません。したがって、間違ったデータ表現を避けるために、EFは新しいクエリを実行する必要があります。ただし、クエリが必要かどうかを認識する他の手段が EF にない場合は除きます。 context.Status.Load()Statuscontext.Status.Load()StatusStatus

この場合、Orderエンティティにはコレクションではなくへの参照しかないため、他の手段もあります。s がロードされると (列挙によって) 、を使用するかどうかに関係なく、EF は常に に外部キーをロードします。モデルで FK をプロパティとして公開していない場合も同様です。が実体化された瞬間に、リレーションシップ フィックスアップが実行され、主キーが一緒にロードされた FK と同じ値を持つエンティティがオブジェクト コンテキストに既に存在するかどうかがチェックされます。はいの場合、プロパティはすぐにそのエンティティに設定されます-おそらく、EFはナビゲーションプロパティを次のようにマークしますStatusStatusOrdercontext.Orders.ToList()OrderStatusIncludeStatusStatusIdOrderStatusOrderOrder.OrderStatusStatusIsLoaded. 参照の場合、一致するエンティティは 1 つだけであり、そのエンティティが既にコンテキストに関連付けられており、ナビゲーション プロパティに割り当てられている場合、クエリを実行しても意味がありません。

Statusつまり、正しいオブジェクトがコンテキストにアタッチされている場合、クエリは実行されません。context.Status.Load()この特定Statusの のみを実行またはロードするか、これをロードする他のクエリを実行するか、コンテキスト ( ) にStatus手動でアタッチするかは問題ではありません。Statuscontext.Status.Attach(...)

私の意見では、コレクションOrder.OrderStatusになる場合、この動作は変更する必要があります。がロードされると、 への FK はありません。代わりに にFK がありました。エンティティを最初にロードすると(または他のクエリによって)、Order-FK がオブジェクトと共にロードされます。後でリレーションシップを読み込むと、今度は逆に修正が再度実行されます。EF は、読み込まれた を参照する FK を持つオブジェクトがコンテキスト内にあるかどうかを調べます。はいの場合、コレクションに追加されます。しかし、今回は - 大推測 - ナビゲーション プロパティ (コレクション) をマークすることはできません。OrderStatusStatusOrderStatuscontext.Status.LoadStatusOrderStatusOrderStatusOrder.OrderStatusOrder.OrderStatusIsLoaded StatusこのオブジェクトはOrder以前にロードされているか、その間データベースに追加されていないStatus新しいオブジェクトです。Order

したがって、Order.OrderStatusコレクションにアクセスすると、遅延読み込みが実行され、そのための潜在的な「残りのStatusオブジェクト」Orderも読み込まれるようになります。次に、コレクションを としてマークしIsLoadedます。コレクションに何も追加する必要はないかもしれませんがStatus、少なくともコレクションが完成したかどうかを確認するにはクエリが必要です。

于 2013-03-06T22:40:52.763 に答える