2011-12-04 36 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。 @阿尼的回答是正確的。