繁体   English   中英

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

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

考虑以下实体模型和功能:

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选择时包括状态。 而且我知道,如果我访问Order.OrderStatus导航属性, DbReferenceEntry.IsLoaded将在命中数据库之前检查DbReferenceEntry.IsLoaded并在可能的情况下检索缓存的Status对象。

我想知道的是,如果引用实体已经在对象缓存中,则在实现父实体(即使未调用.Include()时是否填充DbReferenceEntry.IsLoadedDbReferenceEntry.CurrentValue

因此,在上面的示例中,由于枚举顺序之前的Status.Load()调用,即使所有Status都在对象缓存中,第一次访问Order.OrderStatus ,数据库查询是否也会执行?

每当访问导航属性时,EF都会首先检查对象缓存,因此,如果所有Status实体都已经加载并访问Order.OrderStatus,则不应发出数据库查询。 如果引用已经在对象缓存中,则在实现过程中将填充DbReferenceEntry。

如果您担心发出的查询数量,请考虑关闭“延迟加载”,这样,在发出自动查询的情况下,您将获得null值。

有关更多性能指南,请参见本文: http : //msdn.microsoft.com/zh-cn/data/hh949853.aspx

我没有测试以下语句(通过观察在探查器中何时实际运行哪个SQL查询),它们只是猜测:

即使在枚举顺序之前调用Status.Load(),即使所有Status都在对象高速缓存中,数据库查询也会执行吗?

如果没有执行数据库查询,我很确定原因不是 特定的 context.Status.Load()被调用。 这只是执行context.Status.Load()Status数据库表的快照。 在您枚举订单时,EF不能确定“ Status表在此期间没有更改,并且已插入其他“ Status行。 因此,为避免错误的数据表示,EF 必须运行新的查询-除非EF有其他方法可以识别是否需要查询。

还有其他方法,在这种情况下,因为Order实体仅引用 Status ,而没有Status集合。 加载Order -通过枚举context.Orders.ToList() -EF始终将外键加载到OrderStatus ,无论是否对Status使用Include 如果您没有在模型中公开FK StatusId作为属性,甚至会是这种情况。 在实现Order时,将运行关系修正,并检查其主键与与Order一起加载的FK具有相同值的Status实体是否已存在于对象上下文中。 如果是,则Order.OrderStatus属性将立即设置为该Status实体-我想,EF会将导航属性标记为IsLoaded 作为参考,只能有一个匹配的实体,并且如果该实体已附加到上下文并已分配给导航属性,则运行查询是没有意义的。

因此,我会说:如果将正确的Status对象附加到上下文,则不会运行查询。 通过运行context.Status.Load()或仅加载此特定Status ,运行将加载此Status任何其他查询,或通过手动将Status附加到上下文( context.Status.Attach(...) )。

如果Order.OrderStatus是一个collection,这种行为必须改变。 现在加载Order时,没有FK到Status 相反, Status具有要Order的FK。 如果首先通过context.Status.Load或任何其他查询加载Status实体,则Order-FK将与Status对象一起加载。 如果您稍后加载了Order关系修正程序,那么它将再次运行,这一次是相反的:EF查找上下文中是否有任何Status对象具有FK引用已加载的Order 如果是,它将Status添加到Order.OrderStatus集合。 但是这一次-很大的猜测-它不能将导航属性( Order.OrderStatus集合)标记为IsLoaded因为它不能确保此Order 所有 Status对象实际上都已预先加载,或者无法确定该Status的新Status同时,尚未将Order添加到数据库中。

因此,我想,如果您访问Order.OrderStatus集合,则会进行延迟加载,以确保也可以加载该Order的潜在“ Status对象”。 然后它将集合标记为IsLoaded 它可能不需要向集合添加任何其他Status ,但是至少必须检查该查询才能检查集合是否完整。

暂无
暂无

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

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