2015-07-10 25 views
1

如何爲此類實體框架創建屬性選擇器?如何在ef6中構建簡單的屬性選擇器表達式

public static List<T> StandardSearchAlgorithm<T>(this IQueryable<T> queryable, Func<T, string> property, string query) 
{ 

    return queryable.Where(e => property(e).ToLower().IndexOf(query) > -1).ToList(); 

} 

我想調用代碼能夠乾淨,簡單的像這樣:

var usernameResults = _db.Users.StandardSearchAlgorithm(u => u.Username, query); 

我得到一個「的LINQ表達式節點類型‘調用’不是在LINQ支持到實體。 「錯誤。我無法弄清楚如何獲得構建的表達式。

UPDATE:

基於由這裏MBoros答案是我最終的代碼。它效果很好。表達樹的關鍵是理解表達式樹都是關於將你通常在代碼中寫入的內容(如「e => e.Username.IndexOf(query)」)分解爲一系列對象:「e」獲取自己的對象,「Username」自己的對象,「IndexOf()」自己的對象,「查詢」常量自己的對象,等等。第二個關鍵是要知道您可以在類Expression上使用一系列靜態方法來創建各種這些對象,如下所示。

PropertyInfo pinfo = (PropertyInfo)((MemberExpression)property.Body).Member; 
    ParameterExpression parameter = Expression.Parameter(typeof(T), "e"); 
    MemberExpression accessor = Expression.Property(parameter, pinfo); 
    ConstantExpression queryString = Expression.Constant(query, typeof(string)); 
    ConstantExpression minusOne = Expression.Constant(-1, typeof(int)); 
    MethodInfo indexOfInfo = typeof(string).GetMethod("IndexOf", new[] { typeof(string) }); // easiest way to do this 
    Expression indexOf = Expression.Call(accessor, indexOfInfo, queryString); 
    Expression expression = Expression.GreaterThan(indexOf, minusOne); 
    Expression<Func<T, bool>> predicate = Expression.Lambda<Func<T, bool>>(expression, parameter); 
    //return predicate.Body.ToString(); // returns "e => e.Username.IndexOf(query) > -1" which is exactly what we want. 

    var results = queryable.Where(predicate).ToList(); 
    return results; 

現在我有一個真正的問題,但我會問一個單獨的問題。我真正的查詢是這樣的:

public static List<T> StandardSearchAlgorithm<T>(this IQueryable<T> queryable, Func<T, string> property, string query) 
{ 

    return queryable.Where(e => property(e).IndexOf(query) > -1).Select(e=> new { Priority = property(e).IndexOf(query), Entity = e }).ToList(); 

} 

所以我需要建立一個表達式,返回一個匿名類型!或者即使我創建一個類來幫助,我需要編寫一個返回一個新對象的表達式。但我會在另外一個問題中加入這個。

+0

你可能想看看[PredicateBuilder(http://www.albahari.com/nutshell/predicatebuilder.aspx)來幫你做這 – DLeh

+0

誰之前發佈答案的人。這工作!謝謝神祕人刪除了你的帖子。 –

+0

我刪除了我的答案,因爲它實際上並沒有編譯到SQL,它只是在內存中。我正在研究如何使其正確編譯爲sql。稍微檢查一下,希望我會有一個工作答案。 – DLeh

回答

2

你不能簡單地在sql中調用CLR委託。但是你可以在屬性選擇器作爲表達式樹傳遞,所以您的簽名是:

public static List<T> StandardSearchAlgorithm<T>(this IQueryable<T> queryable, Expression<Func<T, string>> property, string query) 

呼叫都是相似的。但是現在你已經有了一個表達式,你可以看看這個答案: Pass expression parameter as argument to another expression 它給你提供了簡單的將表達式樹放入另一個表達式的工具。在你的情況下,它看起來像:

Expression<Func<T, bool>> predicate = e => property.AsQuote()(e).Contains(query); 
predicate = predicate.ResolveQuotes(); 
return queryable.Where(predicate).ToList(); 

一旦你在那裏,你仍然有.ToLower()包含()調用(使用。載而不是.IndexOf()> 1)。這實際上很棘手。通常情況下,數據庫使用它的默認排序規則,所以如果它設置爲CI(不區分大小寫),那麼它會以這種方式進行比較。如果你沒有任何限制,並且可以調整數據庫排序規則,那麼我會去做。在這種情況下,您可以省略.ToLower()調用。 否則,看看這個雁:https://stackoverflow.com/a/2433217/280562

+0

謝謝你提到db歸類。事實證明,數據庫設置爲CI,所以在這種情況下,我完全可以忽略ToLower()。這真的很有幫助。使用Contains()的問題是,儘管我在我的問題中顯示的代碼可以根據Contains()來考慮,但實際查詢還有另一個需要IndexOf()的子句,所以即使在查詢中使用Contains()上面我最終需要制定IndexOf()解決方案。 –

+0

對於linq查詢中的MSSql,你可以試試SqlFunctions.CharIndex(https://msdn.microsoft.com/en-us/library/dd487160%28v=vs.110%29.aspx)。請注意,這隻適用於瞭解提供者的情況,例如,您將無法編譯和執行內存中的表達式樹! – MBoros

相關問題