2011-12-04 38 views
4
var articles = context.Articles.Where(a => a.Id != articleId) 
.OrderBy(p => p.Categories.OrderBy(q => q.Name).FirstOrDefault().Name).ToList(); 

我收到了可能的NullReferenceException消息,它是正确的。实体框架和lambda表达式树(深空合并)

所以我让

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表达式与语句正文不能转换为表达式树 。

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

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

+0

对于你使用'lambda'到这个程度是否至关重要?通过重新分解一些'lambda'表达式,解决你的解决方案可能会更容易,并使代码更具可读性。 –

回答

4

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

目前尚不清楚哪个系统正在生成此消息。 ReSharper的?

无论如何,在这种情况下,如果这确实是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查询优化器通常比您想象的更聪明。 :)

+2

是的,这是正确的答案。 Linq-to-entities将正确解释第一个查询。 –

+0

是resharper给我这个消息。所以消息是无关紧要的。为什么然后resharper给这个消息。 – senzacionale

+0

@senzacionale:Resharper可能(目前)不够聪明,无法将LINQ提供者的特质考虑在内。在一般情况下,警告是正确的 - 在这种情况下不会发生。如果需要,您可以通过评论禁用警告(它为您提供了选项)。 – Ani

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(); 

在您的查询的问题是选择Name你叫FirstOrDefault后,所以如果你的项目之前的结果调用FirstOrDefault它不应该产生警告,但在结果SQL中会有额外的子选择。

Btw。 @阿尼的回答是正确的。