2009-12-30 111 views
10

我有這樣的LINQ查詢:爲什麼這個LINQ連接語句不起作用?

// types... 
    LinkedList<WeightedItem> itemScores = new LinkedList<WeightedItem>(); 

    var result = from i in _ctx.Items 
       join s in itemScores on i.Id equals s._id 
       orderby s._score descending 
       select new ItemSearchResult(i, s._score); 

    // this fails: 
    return result.ToList(); 

這是產生這個錯誤:

Unable to create a constant value of type 'System.Collections.Generic.IEnumerable`1'.
Only primitive types ('such as Int32, String, and Guid') are supported in this context.

[編輯]這裏是WeightedItem代碼:

public class WeightedItem 
{ 
    public int _id; 
    public decimal? _score; 

    public WeightedItem(int id, decimal? score) 
    { 
     _id = id; 
     _score = score; 
    } 
} 

你能看到我做錯了什麼?代碼編譯完美,_ctx.Items和itemScores都包含適當的值。

+0

你可以發佈WeightedItem的代碼 – Lazarus 2009-12-30 19:50:40

+0

顯然WeightedItem不是原始類型。 – DOK 2009-12-30 19:50:50

+0

拉撒路,它完成了。 DOK,這意味着什麼? – Mickel 2009-12-30 19:53:14

回答

21

是的,它會編譯得很好 - 問題是它不能將它轉換成SQL。當您引用「本地」值時,實體框架必須在需要創建SQL查詢時解決如何處理它們。它基本上無法應付在內存中收集和數據庫表之間進行連接。

可能工作的一件事是使用Contains來代替。我不知道LinkedList<T>是否會爲這方面的工作,但我相信List<T>確實,至少在LINQ to SQL:

List<int> requiredScoreIds = itemScores.Select(x => x._id).ToList(); 

var tmp = (from i in _ctx.Items 
      where requiredScoreIds.Contains(i.Id) 
      orderby s._score descending 
      select i).AsEnumerable(); 

// Now do the join in memory to get the score 
var result = from i in tmp 
      join s in itemScores on i.Id equals s._id 
      select new ItemSearchResult(i, s._score); 

現在正在做的內存查詢,這是有點不必要的聯接。您可以改爲使用字典:

List<int> requiredScoreIds = itemScores.Select(x => x._id).ToList(); 

var tmp = (from i in _ctx.Items 
      where requiredScoreIds.Contains(i.Id) 
      orderby s._score descending 
      select i).AsEnumerable(); 

// Create a map from score ID to actual score 
Dictionary<int, decimal?> map = itemScores.ToDictionary(x => x._id, 
                 x => x._score); 

var result = tmp.Select(i => new ItemSearchResult(i, map[i.Id])); 
+0

是有意義的,所以.AsEnumerable()執行查詢並將結果保存在內存中?如果不是,代碼的哪一部分呢? – Mickel 2009-12-30 19:59:50

+2

@Mickel:'AsEnumerable'不會立即執行查詢 - 但它會返回'IEnumerable '而不是'IQueryable ',因此查詢的其餘部分將使用'Enumerable.xxx'而不是'Queryable'完成。 xxx'。當該查詢最終需要執行時,它將執行數據庫中的第一部分,並執行內存中的第二部分。 – 2009-12-30 20:11:36

3

您不能在內存列表和可查詢對象之間進行連接。你需要做這樣的事情:

var criteria = itemScores.Select(x => x._id).ToList(); 
var result_tag = (from i in _ctx.Items 
       where criteria.Contains(i.ID) 
       select i).ToList(); 
var result = from i in result_tag 
      join s in itemScores on i.ID equals s._id 
      orderby s._score descending 
      select new ItemSearchResult(i, s._score); 
+5

Ah shucks - Jon Skeet擊敗了我:) – 2009-12-30 19:56:54

+1

他總是贏... – Ragepotato 2009-12-30 20:02:45

+0

Jon Skeet是Chuck Norris的StackOverflow – 2013-01-24 21:35:20

1

萬一被_ctx.Items所代表的表是不是一個大的,你不內存關心裝載 所有的表,然後在內存中過濾它,你可以簡單地交換項目的順序在 的連接語句,如下面的代碼片段:

LinkedList<WeightedItem> itemScores = new LinkedList<WeightedItem>(); 

var result = from s in itemScores 
      join i in _ctx.Items on s._id equals i.Id 
      orderby s._score descending 
      select new ItemSearchResult(i, s._score); 

return result.ToList(); 

在最初的聲明中被調用的可查詢擴展方法:

IQueryable<TResult> Queryable.Join<TOuter, TInner, TKey, TResult>(
     this IQueryable<TOuter> outer, 
     IEnumerable<TInner> inner, 
     Expression<Func<TOuter, TKey>> outerKeySelector, 
     Expression<Func<TInner, TKey>> innerKeySelector, 
     Expression<Func<TOuter, TInner, TResult>> resultSelector 
) 

而在交換一個可枚​​舉擴展方法被調用:

IEnumerable<TResult> Enumerable.Join<TOuter, TInner, TKey, TResult>(
     this IEnumerable<TOuter> outer, 
     IEnumerable<TInner> inner, 
     Func<TOuter, TKey> outerKeySelector, 
     Func<TInner, TKey> innerKeySelector, 
     Func<TOuter, TInner, TResult> resultSelector 
) 

所以在全_ctx.Items表被加載到內存中,然後加入, 通過LINQ到對象,到itemScores列表中的最後一條語句(我不知道LinkedList,我試着用List)。

我說這個答案,主要是因爲有人可以鍵入以相反的順序的加入,讓它 工作,甚至沒有意識到是怎麼回事在數據庫中發生。

我不建議用這種方式來加入,雖然它可以每當 所涉及的表由很少的記錄,應用程序不會受到相關的性能惡化後臺應用。 畢竟,這個解決方案保持代碼更清潔。