2017-04-11 79 views
3

我試圖寫具有以下特徵的ServiceStack.OrmLite.SqlExpressionVisitor通用通配符搜索:LINQ的擴展表達式

public static SqlExpressionVisitor<T> WhereWildcardSearch<T> (this SqlExpressionVisitor<T> ev, Expression<Func<T,string>> field, string search) 

其中EV是過濾器的其餘部分,字段是吸氣搜索和搜索的字段是輸入的字詞。

通常情況下(非通用),我會寫:

if(search.StartsWith('*') && search.EndsWith('*')) 
    ev = ev.Where(x => x.foo.Contains(search.Trim('*'))); 

當然,也可用於x.foo.StartsWith或變種的endsWith。

現在我正在尋找類似的信息(僞代碼:)

ev = ev.Where(x => field(x).Contains(search.Trim('*'))); 

當然,我不能編譯並直接調用的表達,因爲這應該使用LINQ2SQL轉換爲SQL。

這是到目前爲止我的代碼:

var getFieldExpression = Expression.Invoke (field, Expression.Parameter (typeof (T), "getFieldParam")); 
var searchConstant = Expression.Constant (search.Trim('*')); 

var inExp = Expression.Call (getFieldExpression, typeof(String).GetMethod("Contains"), searchConstant); 
var param = Expression.Parameter (typeof (T), "object"); 
var exp = Expression.Lambda<Func<T, bool>> (inExp, param); 

ev = ev.Where (exp); 

請不要告訴我,我應該直接寫SQL與$"LIKE %search%"什麼的 - 我知道還有其他方法,但解決這將有助於我的理解一般的Linq和Expressions,當我解決不了它時,它會讓我感到不適。

回答

3

這裏是如何可以做到(我認爲這將是明確的,你沒有太多的附加說明你做錯了什麼,但如果沒有 - 隨時申請澄清):

// extract property name from passed expression 
var propertyName = ((MemberExpression)field.Body).Member.Name;    
var param = Expression.Parameter(typeof(T), "object");    
var searchConstant = Expression.Constant(search.Trim('*')); 
var contains = typeof(String).GetMethod("Contains"); 
// object.FieldName.Contains(searchConstant) 
var inExp = Expression.Call(Expression.PropertyOrField(param, propertyName), contains, searchConstant);    
// object => object.FieldName.Contains(searchConstant) 
var exp = Expression.Lambda<Func<T, bool>>(inExp, param); 

響應評論。你有兩個表情樹:一個傳遞給你,另一個傳給你(exp)。在這種簡單情況下,它們都使用相同數量的參數,並且這些參數的類型相同(T)。在這種情況下,你可以從field表達式樹重用參數,如:

// use the same parameter 
var param = field.Parameters[0]; 
var searchConstant = Expression.Constant(search.Trim('*')); 
var contains = typeof(String).GetMethod("Contains");    
// note field.Body here. Your `field` expression is "parameter => parameter.Something" 
// but we need just "parameter.Something" expression here 
var inExp = Expression.Call(field.Body, contains, searchConstant); 
// pass the same parameter to new tree 
var exp = Expression.Lambda<Func<T, bool>>(inExp, param); 

在更復雜的情況下,您可能需要使用ExpressionVisitor在一個表達式樹來代替參數從另一個(最終)表達式樹的參考參數。

+0

這工作良好,正是我所要求的。謝謝! 只是進一步的問題:這隻會在Field-Getter表達式通過時才起作用(就像我問的那樣)。是否有一個易於修改,可以用於任何表達式>? (假設給定的表達式在linq2sql中是有效的) – KillPinguin

+1

@KillPinguin我已經更新了答案,以澄清這一點 – Evk

+0

現在明白了。謝謝! – KillPinguin