【问题标题】:Entity Framework and lambda expression tree (deep null coalescing)实体框架和 lambda 表达式树(深空合并)
【发布时间】:2011-12-04 11:10:27
【问题描述】:
var articles = context.Articles.Where(a => a.Id != articleId)
.OrderBy(p => p.Categories.OrderBy(q => q.Name).FirstOrDefault().Name).ToList();

我收到可能的 NullReferenceException 消息,这是正确的。

所以我做

var  articles = context.Articles.Where(a => a.Id != articleId)
                               .OrderBy(p =>
                                   (p.Categories.OrderBy(q => q.Name).FirstOrDefault() != null
                                    ? p.Categories.OrderBy(q => q.Name).FirstOrDefault().Name
                                    : null))
                               .Skip(page * pageSize)
                                  .Take(pageSize)
                                  .ToList();

这行得通,但语句调用了两次并且可能很慢,所以我尝试制作

var articles = context.Articles.Where(a => a.Id != articleId)
             .OrderBy(p =>
             {
                 var firstOrDefault = p.Categories.OrderBy(q => q.Name).FirstOrDefault();
                 return firstOrDefault != null ? firstOrDefault.Name : null;
             }).ToList();

但我明白了

带有语句体的 lambda 表达式不能转换为 表达式树。

我能做什么? Ss 第一个例子正确,即使我打电话两次p.Categories.OrderBy(q => q.Name).FirstOrDefault().

我认为这可能会很慢。我在数据库中有 200k 行。

【问题讨论】:

  • 你必须使用lambda 到那种程度吗?通过重构一些lambda 表达式,可能更容易解决您的解决方案并使代码更具可读性。

标签: c# entity-framework


【解决方案1】:

我收到可能的 NullReferenceException 消息,这是正确的。

目前尚不清楚哪个系统正在生成此消息。锐化器?

无论如何,在这种情况下,如果这确实是 LINQ to Entities,则警告是虚假的。 LINQ to Entities 在许多情况下会执行自动“深度空值合并”,这就是一种情况。

在您的原始查询中:

var articles = context.Articles
                      .Where(a => a.Id != articleId)
                      .OrderBy(p => p.Categories
                                     .OrderBy(q => q.Name)
                                     .FirstOrDefault()
                                     .Name)
                       .ToList();

...如果文章没有与之关联的类别,则不会有NullReferenceException。相反,此类文章的排序值将被视为null(这意味着根本没有类别的文章将首先出现),这正是您想要的。因此,您无需付出额外的努力!

请注意,对于其他 LINQ 提供程序(例如 LINQ to Objects),行为可能完全不同,.FirstOrDefault().XXX 确实是一个有风险的表达方式。

另一方面,不要过早优化。如果您已经有一个可行的解决方案,请对其进行基准测试。如果太慢,请调查为什么 - 在这种情况下,线索在生成的 SQL 中。 LINQ to Entities 查询优化器通常比您想象的更智能。 :)

【讨论】:

  • 是的,这是正确的答案。 Linq-to-entities 将正确解释第一个查询。
  • 是的 resharper 给我这个消息。所以消息是无关紧要的。那么为什么要重新发送该消息。
  • @senzacionale:Resharper 可能(目前)不够聪明,无法将 LINQ 提供程序的特性考虑在内。在一般情况下,警告是正确的 - 在这种情况下不会发生。如果需要,您可以通过注释禁用警告(它为您提供了执行此操作的选项)。
【解决方案2】:

您的第二个示例确实会产生更复杂的 SQL 查询,但如果此查询真的更慢,则取决于数据库。无论如何,你可以简单地改变你的第一个查询,它应该可以按预期工作而没有那个警告:

var articles = context.Articles
                      .Where(a => a.Id != articleId)
                      .OrderBy(p => p.Categories
                                     .OrderBy(q => q.Name)
                                     .Select(q => q.Name)
                                     .FirstOrDefault())
                      .ToList();

查询中的问题是在调用FirstOrDefault 之后选择Name,因此如果您在调用FirstOrDefault 之前预测结果,它不应该产生警告,但在生成的SQL 中会有额外的子选择。

顺便说一句。 @Ani 的回答是正确的。

【讨论】:

    【解决方案3】:

    这似乎是一个重复的答案:"A lambda expression with a statement body cannot be converted to an expression tree" 看来您不能使用花括号表示的代码块。

    【讨论】:

    • LINQ to Entities 无法将自定义方法调用转换为等效的 SQL。
    • 很公平 - 我没有意识到它使用的是实体框架。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-10-04
    • 2014-10-09
    • 2012-10-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多