C# 带有链式字符串方法的表达式树

C# 带有链式字符串方法的表达式树,c#,lambda,C#,Lambda,我创建了一个谓词,如下所示: p.Name.Contains("Saw") 我的代码如下: private static Expression<Func<T, bool>> BuildContainsPredicate<T>(string propertyName, string propertyValue) { PropertyInfo propertyInfo = typeof (T).GetProperty(proper

我创建了一个谓词,如下所示:

p.Name.Contains("Saw")
我的代码如下:

    private static Expression<Func<T, bool>> BuildContainsPredicate<T>(string propertyName, string propertyValue)
    {
        PropertyInfo propertyInfo = typeof (T).GetProperty(propertyName);

        // ListOfProducts.Where(p => p.Contains(propertyValue))
        ParameterExpression pe = Expression.Parameter(typeof(T), "p");

        MemberExpression memberExpression = Expression.MakeMemberAccess(pe, propertyInfo);
        MethodInfo methodInfo = typeof (string).GetMethod("Contains", new Type[] {typeof (string)});
        ConstantExpression constantExpression = Expression.Constant(propertyValue, typeof(string));

        // Predicate Body - p.Name.Contains("Saw")
        Expression call = Expression.Call(memberExpression, methodInfo, constantExpression);

        Expression<Func<T, bool>> lambda = Expression.Lambda<Func<T, bool>>(call, pe);
        return lambda;
    }
而我一片空白。我知道我必须在定义MethodInfo的地方添加一些东西


有人有什么建议吗?

您必须获得
ToLower
方法的表达式,然后在
Contains
表达式中使用它

private static Expression<Func<T, bool>> BuildContainsPredicate<T>(string propertyName, string propertyValue)
{
    PropertyInfo propertyInfo = typeof (T).GetProperty(propertyName);

    ParameterExpression pe = Expression.Parameter(typeof(T), "p");

    MemberExpression memberExpression = Expression.MakeMemberAccess(pe, propertyInfo);

    //ToLower expression
    MethodInfo toLowerMethodInfo = typeof (string).GetMethod("ToLower", new Type[]{});
    Expression toLowerCall = Expression.Call(memberExpression, toLowerMethodInfo);


    MethodInfo containsMethodInfo = typeof (string).GetMethod("Contains", new Type[] {typeof (string)});
    ConstantExpression constantExpression = Expression.Constant(propertyValue, typeof(string));

    // Pass ToLowerCall to  
    Expression call = Expression.Call(toLowerCall, containsMethodInfo, constantExpression);

    Expression<Func<T, bool>> lambda = Expression.Lambda<Func<T, bool>>(call, pe);
    return lambda;
}
私有静态表达式BuildContainsPredicate(string propertyName,string propertyValue)
{
PropertyInfo PropertyInfo=typeof(T).GetProperty(propertyName);
ParameterExpression pe=表达式参数(类型为(T),“p”);
MemberExpression MemberExpression=Expression.MakeMemberAccess(pe,propertyInfo);
//托洛尔表达式
MethodInfo toLowerMethodInfo=typeof(string).GetMethod(“ToLower”,新类型[]{});
表达式toLowerCall=Expression.Call(memberExpression,toLowerMethodInfo);
MethodInfo containsMethodInfo=typeof(string).GetMethod(“Contains”,新类型[]{typeof(string)});
ConstantExpression ConstantExpression=表达式.常量(propertyValue,typeof(string));
//传给某人
Expression call=Expression.call(toLowerCall,containsMethodInfo,constantExpression);
表达式lambda=Expression.lambda(call,pe);
返回lambda;
}

关于安德烈的回答,这基本上是我在Servy最初评论后得出的,我想发布我得出的结论:

    private static Expression<Func<T, bool>> BuildContainsPredicate<T>(string propertyName, string propertyValue)
    {
        PropertyInfo propertyInfo = typeof (T).GetProperty(propertyName);

        // ListOfProducts.Where(p => p.Contains(propertyValue))
        ParameterExpression pe = Expression.Parameter(typeof(T), "p");

        MemberExpression memberExpression = Expression.MakeMemberAccess(pe, propertyInfo);
        // Thanks to Servy's suggestion
        Expression toLowerExpression = Expression.Call(memberExpression, typeof(string).GetMethod("ToLower", Type.EmptyTypes));

        MethodInfo methodInfo = typeof (string).GetMethod("Contains", new Type[] {typeof (string)});
        ConstantExpression constantExpression = Expression.Constant(propertyValue, typeof(string));

        // Predicate Body - p.Name.Contains("Saw")
        Expression call = Expression.Call(toLowerExpression, methodInfo, constantExpression);

        Expression<Func<T, bool>> lambda = Expression.Lambda<Func<T, bool>>(call, pe);
        return lambda;
    }
私有静态表达式BuildContainsPredicate(string propertyName,string propertyValue)
{
PropertyInfo PropertyInfo=typeof(T).GetProperty(propertyName);
//产品列表,其中(p=>p.Contains(propertyValue))
ParameterExpression pe=表达式参数(类型为(T),“p”);
MemberExpression MemberExpression=Expression.MakeMemberAccess(pe,propertyInfo);
//多亏了Servy的建议
表达式toLowerPression=Expression.Call(memberExpression,typeof(string).GetMethod(“ToLower”,Type.EmptyTypes));
MethodInfo MethodInfo=typeof(string).GetMethod(“包含”,新类型[]{typeof(string)});
ConstantExpression ConstantExpression=表达式.常量(propertyValue,typeof(string));
//谓词体-p.Name.Contains(“Saw”)
Expression call=Expression.call(toLowerExpression、methodInfo、constantExpression);
表达式lambda=Expression.lambda(call,pe);
返回lambda;
}

您可以使用一个规则的lambda定义所有实际上是静态的内容,然后替换不是静态的内容,而不是仅仅因为一个小片段是动态的而手动构建整个表达式

具体地说,您可以使用的一般策略是使用一个lambda和一个表示小动态位的参数,然后在常规lambda中使用它,然后用动态构造的表达式替换该参数的所有实例:

private static Expression<Func<T, bool>> BuildContainsPredicate<T>(
    string propertyName, string propertyValue)
{
    Expression<Func<string, bool>> e = s => s.ToLower().Contains(propertyValue);
    var parameter = Expression.Parameter(typeof(T));
    var property = Expression.PropertyOrField(parameter, propertyName);
    var body = e.Body.Replace(e.Parameters[0], property);
    return Expression.Lambda<Func<T, bool>>(body, parameter);
}

另一种方法是编写一个
Compose
方法,让您可以轻松地编写表达式,从而使事情变得更高级。从概念上讲,我们将有两个lambda,我们希望创建一个lambda,表示调用其中一个并将其结果传递给另一个,然后返回结果:

public static Expression<Func<TFirstParam, TResult>>
    Compose<TFirstParam, TIntermediate, TResult>(
    this Expression<Func<TFirstParam, TIntermediate>> first,
    Expression<Func<TIntermediate, TResult>> second)
{
    var param = Expression.Parameter(typeof(TFirstParam), "param");

    var newFirst = first.Body.Replace(first.Parameters[0], param);
    var newSecond = second.Body.Replace(second.Parameters[0], newFirst);

    return Expression.Lambda<Func<TFirstParam, TResult>>(newSecond, param);
}
公共静态表达式
谱写(
这句话首先,,
表达(第二)
{
var param=表达式参数(typeof(TFirstParam),“param”);
var newFirst=first.Body.Replace(first.Parameters[0],param);
var newSecond=second.Body.Replace(second.Parameters[0],newFirst);
返回表达式.Lambda(newSecond,param);
}
这或多或少地使用了我们上面使用的相同策略,但它将其概括,而不是将其特殊地装箱到特定的表达式中

然后,我们还有一个助手方法要在将这些片段组合在一起之前制作;创建表示访问由属性的字符串名称定义的属性的方法:

public static Expression<Func<T, string>> MemberSelector<T>(string propertyName)
{
    var param = Expression.Parameter(typeof(T));
    var body = Expression.PropertyOrField(param, propertyName);
    return Expression.Lambda<Func<T, string>>(body, param);
}
publicstaticexpressionmemberselector(stringpropertyname)
{
var param=表达式参数(typeof(T));
var body=Expression.PropertyOrField(param,propertyName);
返回表达式.Lambda(body,param);
}
使用这两种辅助方法(不依赖于任何特定情况),我们现在可以构造所需的lambda,而无需任何自定义构建表达式操作:

私有静态表达式BuildContainsPredicate(
字符串propertyName,字符串propertyValue)
{
返回成员选择器(propertyName)
.Compose(prop=>prop.ToLower().Contains(propertyValue));
}

将方法调用添加到
ToLower
方法,就像对
Contains
方法进行方法调用一样。这基本上是相同的过程,只需获取
MethodInfo
并调用
表达式。调用
。顺便说一句,
p.Name.ToLower().Contains(“Saw”)
将始终返回false。@StriplingWarrior好吧,除非当前的文化是一种相当不敏感的东西,实际上并没有做您认为它应该做的事情(例如,将
“S”
视为小写字母).@Servy,谢谢。这比我想象的要容易得多。我只需要再看一眼。@coson的表情非常可怕,令人生畏,但当你克服了这一点,花时间真正尝试看看他们在做什么时,他们并没有那么糟糕。如果你找到了解决方案,请随意将其作为答案发布,而不仅仅是编辑qu我想说你找到了。
public static Expression<Func<TFirstParam, TResult>>
    Compose<TFirstParam, TIntermediate, TResult>(
    this Expression<Func<TFirstParam, TIntermediate>> first,
    Expression<Func<TIntermediate, TResult>> second)
{
    var param = Expression.Parameter(typeof(TFirstParam), "param");

    var newFirst = first.Body.Replace(first.Parameters[0], param);
    var newSecond = second.Body.Replace(second.Parameters[0], newFirst);

    return Expression.Lambda<Func<TFirstParam, TResult>>(newSecond, param);
}
public static Expression<Func<T, string>> MemberSelector<T>(string propertyName)
{
    var param = Expression.Parameter(typeof(T));
    var body = Expression.PropertyOrField(param, propertyName);
    return Expression.Lambda<Func<T, string>>(body, param);
}
private static Expression<Func<T, bool>> BuildContainsPredicate<T>(
    string propertyName, string propertyValue)
{
    return MemberSelector<T>(propertyName)
        .Compose(prop => prop.ToLower().Contains(propertyValue));
}