我在实体框架4.0中遇到了一些意想不到的行为,我希望有人可以帮助我理解这一点。我正在使用northwind数据库来解决这个问题。我也使用默认代码生成器(不是poco或自我跟踪)。我期待随时我查询框架的上下文,如果我还没有获取这些对象,则只进行往返。如果我关闭延迟加载,我会得到这种行为。目前在我的应用程序中,我很简单地打开延迟加载然后将其关闭以便我可以获得所需的行为。这太糟糕了,所以请帮忙。这是一个很好的代码示例,可以演示我的问题。
Public Sub ManyRoundTrips()
context.ContextOptions.LazyLoadingEnabled = True
Dim employees As List(Of Employee) = context.Employees.Execute(System.Data.Objects.MergeOption.AppendOnly).ToList()
'makes unnessesary round trip to the database, I just loaded the employees'
MessageBox.Show(context.Employees.Where(Function(x) x.EmployeeID < 10).ToList().Count)
context.Orders.Execute(System.Data.Objects.MergeOption.AppendOnly)
For Each emp As Employee In employees
'makes unnessesary trip to database every time despite orders being pre loaded.'
Dim i As Integer = emp.Orders.Count
Next
End Sub
Public Sub OneRoundTrip()
context.ContextOptions.LazyLoadingEnabled = True
Dim employees As List(Of Employee) = context.Employees.Include("Orders").Execute(System.Data.Objects.MergeOption.AppendOnly).ToList()
MessageBox.Show(employees.Where(Function(x) x.EmployeeID < 10).ToList().Count)
For Each emp As Employee In employees
Dim i As Integer = emp.Orders.Count
Next
End Sub
为什么第一段代码会进行无关紧要的往返行程?
答案 0 :(得分:3)
您的期望不正确。查询始终查询数据库。总是。那是因为LINQ总是转换为SQL。
如果对象已经被提取,则从上下文加载对象,如果没有,则从数据库加载,使用ObjectContext.GetObjectByKey()。
答案 1 :(得分:2)
第一次“不必要的”旅行是必要的 - 你做了一个新的查询,数据库可能在此期间发生了变化。如果你使用了employees变量(你已经存储了查询的结果),那么就不需要去数据库了。
第二个是必要的,因为您要求它为每个员工获取订单。使用延迟加载并且没有Include(),在您使用emp.Orders.Count()询问它之前,它还没有读取订单。
请记住,在您开始迭代查询(或调用某些需要迭代的方法)之前,LINQ to EF不执行任何操作。如果将该查询保存在变量中,然后在其上调用.Count(),则会进行往返。如果您使用相同的查询并开始枚举它将会有另一次往返。如果该查询中的实体本身具有关系,并且每次访问时都会进行延迟加载,则会有另一次往返。
您的第二个示例显示了如果您事先知道您想要订单,即急切加载,如何正确执行此操作。请注意您如何不回到上下文再次询问员工,重复使用已加载的员工。