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

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

所以在上面的这个例子中,当Order.OrderStatus 第一次访问时,即使所有状态都在对象缓存中,因为Status.Load()枚举订单之前的调用,数据库查询是否会被执行?

4

2 回答 2

0

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

由于枚举订单之前的 Status.Load() 调用,即使所有状态都在对象缓存中,是否会执行数据库查询?

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

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

所以,我想说:如果正确的Status对象附加到上下文,则不会运行任何查询。不管是通过运行context.Status.Load()还是只加载这个特定Status的,运行任何其他会加载这个的查询,Status或者通过手动将 附加Status到上下文 ( context.Status.Attach(...)) 都没有关系。

Order.OrderStatus如果是一个集合,我认为这种行为必须改变。现在加载anOrder时,没有 FK 到Status. 相反,Status他们有一个 FK 到Order. 如果您Status首先加载实体 - 通过context.Status.Load或任何其他查询 - Order-FK 将与Status对象一起加载。如果您稍后加载Order关系修复将再次运行,这次相反:EF 会查看Status上下文中是否有任何对象具有 FK 引用已加载的Order. 如果是,它将添加StatusOrder.OrderStatus集合中。但是这一次 - 很大的猜测 - 它不能将导航属性(Order.OrderStatus集合)标记为,IsLoaded因为它不能确定真的所有 StatusOrder之前已加载此对象或同时尚未将新Status对象Order添加到数据库中。

所以,我想,如果你访问Order.OrderStatus集合,延迟加载将运行以确保潜在的“其余Status对象”也Order被加载。然后它将集合标记为IsLoaded. 它可能不需要向Status集合添加任何其他内容,但至少需要查询以检查集合是否完整。

于 2013-03-06T22:40:52.763 回答
0

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

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

有关更多性能指导,请参阅本文:http: //msdn.microsoft.com/en-us/data/hh949853.aspx

于 2013-03-06T01:18:56.680 回答