简体   繁体   English

延迟加载动态代理POCO n + 1选择

[英]Lazy loading dynamic proxy POCOs n+1 select

Consider the following entity model and function: 考虑以下实体模型和功能:

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);
  }
}

I know I can explicitly do: 我知道我可以明确地做:

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

to include Status when querying for orders to prevent n+1 selects. 在查询订单以防止n + 1选择时包括状态。 And I know that if I access Order.OrderStatus navigation property that DbReferenceEntry.IsLoaded is checked and a cached Status object retrieved if possible before database is hit. 而且我知道,如果我访问Order.OrderStatus导航属性, DbReferenceEntry.IsLoaded将在命中数据库之前检查DbReferenceEntry.IsLoaded并在可能的情况下检索缓存的Status对象。

What I'm wondering is if DbReferenceEntry.IsLoaded and DbReferenceEntry.CurrentValue are populated when the parent entity is materialised (even if .Include() is not called) if the reference entity is in the object cache already? 我想知道的是,如果引用实体已经在对象缓存中,则在实现父实体(即使未调用.Include()时是否填充DbReferenceEntry.IsLoadedDbReferenceEntry.CurrentValue

So in this example above when accessing Order.OrderStatus for the first time does a database query get executed even though all Status are in object cache because of the Status.Load() call prior to enumerating orders? 因此,在上面的示例中,由于枚举顺序之前的Status.Load()调用,即使所有Status都在对象缓存中,第一次访问Order.OrderStatus ,数据库查询是否也会执行?

EF will check the object cache first whenever a navigation property is accessed, so if all the Status entities are already loaded accessing Order.OrderStatus shouldn't issue a database query. 每当访问导航属性时,EF都会首先检查对象缓存,因此,如果所有Status实体都已经加载并访问Order.OrderStatus,则不应发出数据库查询。 DbReferenceEntry will be populated during materialisation if the reference is already in the object cache. 如果引用已经在对象缓存中,则在实现过程中将填充DbReferenceEntry。

If you are concerned about the number of queries being issued consider turning off Lazy Loading so in cases where an automatic query would be issued you'll get a null instead. 如果您担心发出的查询数量,请考虑关闭“延迟加载”,这样,在发出自动查询的情况下,您将获得null值。

For more performance guidance see this article: http://msdn.microsoft.com/en-us/data/hh949853.aspx 有关更多性能指南,请参见本文: http : //msdn.microsoft.com/zh-cn/data/hh949853.aspx

I didn't test the following statements (by watching when and which SQL queries are actually run in a profiler), they are only guesses: 我没有测试以下语句(通过观察在探查器中何时实际运行哪个SQL查询),它们只是猜测:

does a database query get executed even though all Status are in object cache because of the Status.Load() call prior to enumerating orders? 即使在枚举顺序之前调用Status.Load(),即使所有Status都在对象高速缓存中,数据库查询也会执行吗?

If a database query is not executed I'm pretty sure that the reason is not that specifically context.Status.Load() is called. 如果没有执行数据库查询,我很确定原因不是 特定的 context.Status.Load()被调用。 This is only a snapshot of the Status database table at the time when context.Status.Load() is executed. 这只是执行context.Status.Load()Status数据库表的快照。 At the point where you enumerate the orders EF can't be sure that the Status table hasn't change in the meantime and additional Status rows have been inserted. 在您枚举订单时,EF不能确定“ Status表在此期间没有更改,并且已插入其他“ Status行。 So, to avoid a wrong data representation EF must run a new query - unless EF has other means to recognize if a query is required or not. 因此,为避免错误的数据表示,EF 必须运行新的查询-除非EF有其他方法可以识别是否需要查询。

And there are other means, in this case because an Order entity has only a reference to Status , not a Status collection. 还有其他方法,在这种情况下,因为Order实体仅引用 Status ,而没有Status集合。 When the Order s get loaded - by enumerating context.Orders.ToList() - EF will always load the foreign key to OrderStatus , no matter if you use Include for the Status or not. 加载Order -通过枚举context.Orders.ToList() -EF始终将外键加载到OrderStatus ,无论是否对Status使用Include It would even be the case if you hadn't exposed the FK StatusId as property in your model. 如果您没有在模型中公开FK StatusId作为属性,甚至会是这种情况。 At the moment when an Order is materialized, relationship fixup will run and check if a Status entity whose primary key has the same value as the FK loaded together with the Order already exists in the object context. 在实现Order时,将运行关系修正,并检查其主键与与Order一起加载的FK具有相同值的Status实体是否已存在于对象上下文中。 If yes, the Order.OrderStatus property will immediately be set to that Status entity - and I guess, EF will mark the navigation property as IsLoaded . 如果是,则Order.OrderStatus属性将立即设置为该Status实体-我想,EF会将导航属性标记为IsLoaded For a reference there can be only one matching entity and it wouldn't make sense to run a query if that entity is already attached to the context and assigned to the navigation property. 作为参考,只能有一个匹配的实体,并且如果该实体已附加到上下文并已分配给导航属性,则运行查询是没有意义的。

So, I would say: No query is run if the right Status object is attached to the context. 因此,我会说:如果将正确的Status对象附加到上下文,则不会运行查询。 It doesn't matter if by running context.Status.Load() or by loading only this specific Status , running any other query that would load this Status or by manually attaching the Status to the context ( context.Status.Attach(...) ). 通过运行context.Status.Load()或仅加载此特定Status ,运行将加载此Status任何其他查询,或通过手动将Status附加到上下文( context.Status.Attach(...) )。

This behaviour must change in my opinion if Order.OrderStatus would be a collection . 如果Order.OrderStatus是一个collection,这种行为必须改变。 When an Order gets loaded now there is no FK to Status . 现在加载Order时,没有FK到Status Instead the Status had an FK to Order . 相反, Status具有要Order的FK。 If you load Status entities first - by context.Status.Load or any other query - the Order-FK would be loaded with the Status objects. 如果首先通过context.Status.Load或任何其他查询加载Status实体,则Order-FK将与Status对象一起加载。 If you later load an Order relationship fixup would run again, this time the other way around: EF looks if there are any Status objects in the context that have an FK refering to the loaded Order . 如果您稍后加载了Order关系修正程序,那么它将再次运行,这一次是相反的:EF查找上下文中是否有任何Status对象具有FK引用已加载的Order If yes, it will add the Status to the Order.OrderStatus collection. 如果是,它将Status添加到Order.OrderStatus集合。 But this time - big guess - it can't mark the navigation property (the Order.OrderStatus collection) as IsLoaded because it can't be sure that really all Status objects for this Order have been loaded previously or that a new Status for that Order hasn't been added to the database in the meantime. 但是这一次-很大的猜测-它不能将导航属性( Order.OrderStatus集合)标记为IsLoaded因为它不能确保此Order 所有 Status对象实际上都已预先加载,或者无法确定该Status的新Status同时,尚未将Order添加到数据库中。

So, I guess, if you access the Order.OrderStatus collection lazy loading will run to ensure that the potential "rest of Status objects" for that Order gets loaded as well. 因此,我想,如果您访问Order.OrderStatus集合,则会进行延迟加载,以确保也可以加载该Order的潜在“ Status对象”。 Then it will mark the collection as IsLoaded . 然后它将集合标记为IsLoaded It might not need to add any additional Status to the collection, but the query is necessary at least to check if the collection was complete or not. 它可能不需要向集合添加任何其他Status ,但是至少必须检查该查询才能检查集合是否完整。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM