默认情况下使用延迟加载,我知道你应该在你的Entity Framework实体上调用.Include(),以便在你的查询中引入你想要的关联实体,以减少调用数据库的数量您实体上的方法。如果不这样做,则存在每行重复数据库调用的风险(N + 1问题)
有人可以确认,如果我编写规范的LINQ查询,并明确定义了连接,那么我们是否可以防范N + 1?
from x in _context.tblOrder
join y in _context.tblCustomer equals y.id = x.customerId
select x
当我们加载所有需要连接的实体时,N + 1有什么方法可以进入吗?
修改的
作为背景,有人问初级开发者如何防范N + 1。我提到最简单的方法是写出你的查询并定义你的联接,我希望我所说的确认是100%准确的。
答案 0 :(得分:1)
如果您真正要求的是
此查询是否会在数据库中命中一次?
然后答案是肯定的。 LINQ to EF将您的表达式转换为原始SQL,并且只有在您评估查询时,它才会向数据库发送任何内容,例如ToList()
/ foreach
/ for
等。除非您明确告知其他情况,否则一旦发出该查询,则不会发出其他信息。
您的LINQ语句可以使用Lambda表达式进行简化,例如
_context.tblOrder.Include("Customer").ToList();
这将在一次数据库旅行中为您提供所有订单详细信息,包括所有相关的客户详细信息。
答案 1 :(得分:1)
仅仅因为在连接中指定表并不意味着在迭代值时不会遇到n + 1问题。请考虑以下对您的查询的扩展名:
var query = from o in Orders
join c in Customers on o.CustomerID equals c.CustomerID
select o;
foreach (var o in query)
{
Console.WriteLine(String.Format("{0}: {1}", o.OrderDate, o.Employee.FirstName));
}
在这种情况下,每次浏览订单的Employee对象时,都会从数据库中获取该订单的员工。如果您想避免此问题,可以在select子句中投影所需的值:
var query = from o in Orders
join c in Customers on o.CustomerID equals c.CustomerID
select new {o.OrderDate, o.Employee.FirstName};
foreach (var o in query)
{
Console.WriteLine(String.Format("{0}: {1}", o.OrderDate, o.FirstName));
}
注意,在这种情况下,您甚至不需要连接,因为您只需使用导航属性即可。当然,如果您不允许实体中的导航属性并且仅依赖于连接,则可以避免n + 1情况,但这不是解决问题的非常OOP方式。
如果您只从查询中返回匿名类型,我认为您可以安全地保证不会遇到n + 1,但这也是相当严格的。
最好的选择是确保对应用程序生成的SQL进行概要分析,并准确了解何时以及为何要访问数据库。我在http://www.thinqlinq.com/Post.aspx/Title/LINQ-to-Database-Performance-hints讨论了一些可用的分析器。