2017-04-11 81 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