1

默认情况下使用延迟加载,我知道您应该在您的实体框架实体上调用 .Include() 以在您的查询中拉入您想要的关联实体,以减少对数据库的调用次数,如果您在您的上调用 LINQ 方法实体。如果不这样做,您将面临每行重复数据库调用的风险(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% 准确。

4

2 回答 2

1

如果你真正要问的是

这个查询会访问数据库一次吗?

那么答案是肯定的。LINQ to EF 将您的表达式转换为原始 SQL,并且只有当您评估查询时,它才会将任何内容发送到数据库,例如ToList()/ foreach/for等。一旦发送了该查询,除非您明确告知它,否则不会有其他任何内容。

您的 LINQ 语句可以使用 Lambda 表达式进行简化,例如

_context.tblOrder.Include("Customer").ToList();

这将在一次数据库行程中为您提供所有订单详细信息,包括所有相关的客户详细信息。

于 2013-07-15T12:25:09.760 回答
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讨论了一些可用的分析器。

于 2013-07-15T13:25:34.413 回答