简体   繁体   English

Linq扩展表达式

[英]Linq extending Expressions

I'm trying to write a generic wildcard Search for the ServiceStack.OrmLite.SqlExpressionVisitor that has the following signature: 我正在尝试编写具有以下签名的ServiceStack.OrmLite.SqlExpressionVisitor的通用通配符搜索:

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

where ev is the rest of the filter, field is the getter for the field to search by and search is the entered term. 其中ev是过滤器的其余部分,field是要搜索的字段的getter,搜索是输入的术语。

Normally (non-generic) I would write the following: 通常(非泛型)我会写以下内容:

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

and of course also variants for x.foo.StartsWith or EndsWith. 当然还有x.foo.StartsWith或EndsWith的变种。

Now I am searching for something like (pseudocode:) 现在我正在寻找类似的东西(伪代码:)

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

Of course I can't compile and call the expression directly, as this should be translated to Sql using Linq2Sql. 当然我无法直接编译和调用表达式,因为这应该使用Linq2Sql转换为Sql。

This is my code so far: 到目前为止这是我的代码:

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);

Please don't tell me that I should directly write SQL with $"LIKE %search%" or something - I know that there are other ways, but solving this would help my understanding of Linq and Expressions in general and it bugs me when I can't solve it. 请不要告诉我,我应该用$"LIKE %search%"或者其他东西直接编写SQL - 我知道还有其他方法,但解决这个问题将有助于我理解Linq和Expressions一般而且当我无法解决它。

Here is how it can be done (I think it will be clear for you without much additional explanations what you did wrong, but if not - feel free to request a clarification): 这是如何做到的(我认为没有太多额外的解释,你做错了什么,但如果没有 - 请随时要求澄清):

// 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);

In response to comment. 回应评论。 You have two expression trees: one is being passed to you and another one which you are building ( exp ). 你有两个表达式树:一个是传递给你的,另一个是你正在构建的( exp )。 In this simple case they both use the same number of parameters and those parameters are of the same type ( T ). 在这种简单的情况下,它们都使用相同数量的参数,并且这些参数具有相同的类型( T )。 In this case you can reuse parameter from field expression tree, like this: 在这种情况下,您可以重用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);

In more complicated cases you might need to use ExpressionVisitor to replace parameters in one expression tree to reference to parameters from another (final) expression tree. 在更复杂的情况下,您可能需要使用ExpressionVisitor替换一个表达式树中的参数,以引用另一个(最终)表达式树中的参数。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM