【问题标题】:EF Where(x => x.ColumnVal == 1) vs FirstOrDefault(x => x.Column == 1)EF Where(x => x.ColumnVal == 1) vs FirstOrDefault(x => x.Column == 1)
【发布时间】:2015-03-11 17:15:16
【问题描述】:

我有一个 LINQ 查询,它加载如下对象的层次结构。

查询 #1

var result = db.Orders
               .Include("Customer")
               // many other .Include() here
               .FirstOrDefault(x => x.Customer.CustomerId == 1 &&
                                    x.OrderId == orderId);

我遇到了严重的性能问题
CPU 使用率接近 100%,内存使用率非常高。

然后我将其调整为以下,性能问题得到了修复。

查询 #2

var result = db.Orders
               .Include("Customer")
               // many other .Include() here
               .Where(x => x.Customer.CustomerId == 1 &&
                           x.OrderId == orderId)
               .FirstOrDefault();



我只是想证实我的怀疑。
查询 #1 可能正在遍历我的所有记录内存中寻找匹配记录
vs
查询 #2 过滤数据库中的记录,然后仅获取第一条记录。

这就是 Query #1 存在性能问题的原因吗?

为了安全起见,我需要在.FirstOrDefault() 之前使用.Select(x => x) 吗?

查询 #3

var result = db.Orders
               .Include("Customer")
               // many other .Include() here
               .Where(x => x.Customer.CustomerId == 1 &&
                           x.OrderId == orderId)
               .Select(x => x)
               .FirstOrDefault();

【问题讨论】:

  • 我同意你最初的想法。观察生成的查询并让您休息一下:-)

标签: entity-framework linq-to-entities


【解决方案1】:

不,它们在执行时都应该产生相同的 SQL 查询。您可以通过查看 SQL Profiler 来证明这一点,并查看在这两种情况下从 EF 提交的确切 SQL 是什么。您的性能优化应该是由其他一些因素引起的。原因如下:

Include 方法返回 ObjectQuery<T>

public class ObjectQuery<T> : ObjectQuery, IOrderedQueryable<T>, 
                              IQueryable<T>, IEnumerable<T>, 
                              IOrderedQueryable, IQueryable, 
                              IEnumerable, IListSource

这意味着它的 FirstOrDefault 方法带有 2 个重载:

// Defined by Enumerable:
FirstOrDefault(Func<T, Boolean>)

// Defined by Queryable:
FirstOrDefault(Expression<Func<T, Boolean>>)

当您编写.FirstOrDefault(x =&gt; x.Customer.CustomerId == 1 代码时,编译器将进入一个名为重载解析 的过程,以推断 lambda 表达式 x =&gt; x.Customer.CustomerId == 1 的类型,因为它可以转换为类型两种重载的参数类型。
编译器将使用一种算法(我仍在尝试在 C# 语言规范中找到该算法!),找出将 lambda 转换为 Expression&lt;Func&lt;T, Boolean&gt;更好的转换 Func&lt;T, Boolean&gt; 所以选择 IQueryable 重载。
因此,当您在 SQL Profiler 中观察它时,您会在生成的 SQL 中看到谓词。

【讨论】:

  • 嗨 Morteza,您回答的类似问题略有不同,但告诉他包括“使用 System.Linq.Expressions;”。我没有那个 using 声明。这就是我遇到性能问题的原因吗? stackoverflow.com/questions/3540410/…
  • 感谢您的提醒。我同意,这是不同的,但我仍然坚持我的新答案,我更新我的答案以说明原因。请同时运行它们并在 Profiler 中查看结果,您会看到它们都产生完全相同的 SQL,如果不是,请告诉我。
【解决方案2】:

我找到了罪魁祸首。它是 Entity Framework 生成的 SQL 查询
我有一个包含很多多对多关系的复杂架构。

Entity Framework 正在生成 32,000 行长 SQL 字符串:'(
现在,我正在更改我的代码以手动加载某些部分的层次结构。

如果有人知道一些关于 Eager Loading 和多对多关系的好文章,请告诉我。

【讨论】:

    【解决方案3】:

    我认为最好使用 ...Where(condition).Take(1).FirstOrDefault() 因为 Take(1) 可以很容易地转换为 SQL 作为 TOP 子句。有人陪我吗?

    【讨论】:

    • FirstOrDefault() 会自动生成带有 SQL 的 TOP (1) 的查询
    猜你喜欢
    • 2023-03-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多