考虑以下实体模型和功能:
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
并检索缓存的Status对象。
我想知道的是,如果父实体具体化(即使未调用DbReferenceEntry.IsLoaded
),如果引用实体位于对象缓存中,则填充DbReferenceEntry.CurrentValue
和.Include()
已经?
因此,在上面的示例中,首次访问Order.OrderStatus
时,会执行数据库查询,即使所有状态都在对象缓存中,因为先前有Status.Load()
调用列举订单?
答案 0 :(得分:0)
EF将在访问导航属性时首先检查对象缓存,因此如果已加载所有Status实体,则访问Order.OrderStatus不应发出数据库查询。 如果引用已经在对象缓存中,则将在实现期间填充DbReferenceEntry。
如果您担心发出的查询数量,请考虑关闭延迟加载,以便在发出自动查询的情况下,您将获得null。
有关更多性能指导,请参阅此文章:http://msdn.microsoft.com/en-us/data/hh949853.aspx
答案 1 :(得分:0)
我没有测试以下语句(通过观察何时以及哪些SQL查询实际在分析器中运行),它们只是猜测:
即使所有状态都在,也会执行数据库查询 对象缓存,因为在枚举之前调用了Status.Load() 订单?
如果数据库查询不已执行,我很确定原因是不 具体 context.Status.Load()
被调用。这只是执行Status
时context.Status.Load()
数据库表的快照。在您枚举订单时,EF无法确定Status
表在此期间没有更改,并且已插入其他Status
行。因此,为了避免错误的数据表示,EF 必须运行新的查询 - 除非EF有其他方法来识别是否需要查询。
还有其他方法,在这种情况下,因为Order
实体只有引用到Status
,而不是Status
集合。加载Order
后 - 通过枚举context.Orders.ToList()
- EF将始终将外键加载到OrderStatus
,无论您对Include
使用Status
或不。如果您没有在模型中公开FK StatusId
作为属性,情况甚至会发生。在实现Order
时,将运行关系修正并检查其主键与与Status
一起加载的FK具有相同值的Order
实体是否已存在于对象上下文。如果是,则Order.OrderStatus
属性将立即设置为该Status
实体 - 我猜,EF会将导航属性标记为IsLoaded
。对于引用,只能有一个匹配的实体,如果该实体已经附加到上下文并分配给导航属性,则运行查询是没有意义的。
所以,我想说:如果右Status
对象附加到上下文,则不运行查询。无论是运行context.Status.Load()
还是仅加载此特定Status
,运行任何其他可加载此Status
的查询,或手动将Status
附加到上下文(context.Status.Attach(...)
)。
如果Order.OrderStatus
是集合,我认为此行为必须更改。现在加载Order
时,Status
没有FK。相反,Status
的FK为Order
。如果您首先加载Status
个实体 - context.Status.Load
或任何其他查询 - 订单-FK将加载Status
个对象。如果稍后加载Order
关系,则会再次运行fixup,这次反过来:EF查看上下文中是否有任何Status
个对象,其中FK引用加载的{{1} }。如果是,则会将Order
添加到Status
集合中。但这一次 - 大猜测 - 它无法将导航属性(Order.OrderStatus
集合)标记为Order.OrderStatus
,因为它无法确定真正全部 {{1之前已加载此IsLoaded
的对象,或者同时未将Status
的{{1}}添加到数据库中。
所以,我想,如果您访问Order
集合,则会运行延迟加载以确保加载Status
的潜在“Order
个对象的剩余部分”。然后它会将集合标记为Order.OrderStatus
。它可能不需要在集合中添加任何额外的Status
,但查询是必要的,至少需要检查集合是否完整。