2010-08-05 29 views
0

我真的很喜歡PredicateBuilder。它允許我非常動態地構建各種查詢。謂詞變量可以傳遞給不同的對象,並且可以使用他們知道的值添加它,等等。除非我需要在散列集合上使用.Contains。 Bzzt!崩潰和燒傷。LINQ Predicates中使用的收藏範圍

例如(例如/僞代碼,這可能會或可能不會編譯/運行):

protected Expression<Func<MyClass, bool>> GetWherePredicate() 
{ 
    string[] selectedValues = Request.Form.GetValues("checkbox1") ?? new string[0]; 
    HashSet<int> selectedIDs = new HashSet<int>(selectedValues.Cast<int>()); 

    Expression<Func<MyClass, bool>> predicate = PredicateBuilder.True<MyClass>(); 
    predicate = predicate.And(s => selectedIDs.Contains(s.ID)); 

    return predicate; 
} 

protected void Retrieve() 
{ 
    Expression<Func<MyClass, bool>> predicate = GetWherePredicate(); 
    IEnumerable<MyClass> retrievedValues = MyDataContext.GetTable<MyClass>.Where(predicate); 
} 

當我嘗試這樣做,我得到一個NotSupportedException異常:方法「布爾包含(Int32)已」由於所選ID的HashSet不在範圍內,因此沒有支持轉換到SQL。如果我用同樣的方法完成這一切,那麼它工作正常。

我需要知道正確的方法來讓我的謂詞在那裏解決或編譯或者其他任何東西,以便它可以在聲明HashSet的不同範圍內使用。任何幫助?

更新:我有這個錯誤。下面的代碼工作正常,所以沒有範圍衝突。謝謝Jay。

string[] selectedValues = Request.Form.GetValues("checkbox1") ?? new string[0]; 
Expression<Func<MyClass, bool>> predicate = PredicateBuilder.True<MyClass>(); 
predicate = predicate.And(s => selectedValues.Contains(s.ID.ToString())); 

回答

2

從你引用的例外來看,範圍似乎不太可能是一個因素。

我要回答的是,你需要聲明selectedIDs作爲IEnumerable<int>,而不是HashSet<int>(或調用Contains()之前只投它,但不考慮它的工作時,都在同一個方法,所以我不能確定。

沒有看到所表現出這種行爲任何實際的代碼,這將是困難的任何進一步解決。

+0

好的,我會工作我的代碼了一下,看看我是否在這裏走錯了路。謝謝。 – sliderhouserules 2010-08-05 04:49:49

0

不要球藝,眼花繚亂的名稱爲Contains ...有被命名的許多方法,並且很多人都支持翻譯爲SQL。

  • Enumerable.Contains<T>List<T>.Contains是支持轉換的方法。
  • HashSet<T>.Contains是一種沒有支持翻譯的方法。

有許多決議,但我建議:

IEnumerable<string> selectedValueQuery = 
    Request.Form.GetValues("checkbox1") ?? Enumerable.Empty<string>(); 
List<string> selectedIds = selectedValueQuery 
    .Cast<int>() 
    .Distinct() 
    .ToList(); 

雖然簡單的解決方案可能是:

IEnumerable<int> selectedIDs = new HashSet<int>(selectedValues.Cast<int>()); 

編輯:這不是關於具體實施包含在內。這是關於linqtosql查詢提供程序是否可以識別該方法並將其轉換爲IN(list)sql表達式。識別碼查看錶達式中使用的參數的類型。識別碼不使用多態性/實現,也不走繼承樹尋找其他可能性。

List<int> myList = new List<int>(){1, 2, 3}; 
IList<int> myIList = myList; 
IEnumerable<int> myIEnumerable = myList; 

    //works by List<T>.Contains() 
db.Customers.Where(c => myList.Contains(c.CustomerID)); 

    //doesn't work, no translation for IList<T>.Contains 
db.Customers.Where(c => myIList.Contains(c.CustomerID)); 

    //works by Enumerable.Contains<T>() 
db.Customers.Where(c => myIEnumerable.Contains(c.CustomerID)); 

    //works by Enumerable.Contains<T>() 
db.Customers.Where(c => Enumerable.Contains(myIEnumerable, c.CustomerID)); 

即使這些參數引用同一實例,發生不同的翻譯的行爲,因爲該參數的類型是不同的。

.Contains()不被調用,因爲它被翻譯,因此它的實現是不相關的。它可以throw NotImplementedExceptionreturn true;

+0

甜,謝謝。我花了一些時間閱讀IEnumerable <>。Contains和IList <>之間的區別。包含和我認爲這可能首先是我的問題的核心。所以這裏推薦的類型是很好的指南。 – sliderhouserules 2010-08-05 18:36:49

+0

IList .Contains沒有支持翻譯。列表。包含。有沒有這樣的事情IEnumerable 。包含 – 2010-08-05 19:40:20

+0

這是一個偶然的評論,我用記憶寫了名字。我並沒有試圖爲人們記錄事情。有時候這個網站會讓我感到懊惱...... /嘆氣 http://msdn.microsoft.com/en-us/library/bb352880.aspx – sliderhouserules 2010-08-06 18:34:56