I have created a predicate that looks like:
p.Name.Contains("Saw")
and I have the following code that works:
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;
}
But I want to change the predicate to:
p.Name.ToLower().Contains("Saw")
and I'm coming up blank. I know that I have to add something somewhere where the MethodInfo is defined.
Does anyone have a suggestion?
Rather than constructing the entire expression manually just because one tiny little piece is dynamic, you can use a regular lambda to define all of the contents that are in fact static, and then just replace the little bit that isn't.
Specifically, the general strategy you can use is to have a lambda with a parameter representing your little dynamic bit, and then using it in a regular lambda, and then replace all instances of that parameter with your dynamically constructed expression:
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);
}
This not only simplifies your original code, but makes other changes to this static code as easy as editing any regular old C# code, rather than requiring all of the expression manipulation, and the complexity (and loss of static typing) that comes along with it.
This solution uses the following method to replace all instances of one expression with another:
public static Expression Replace(this Expression expression,
Expression searchEx, Expression replaceEx)
{
return new ReplaceVisitor(searchEx, replaceEx).Visit(expression);
}
internal class ReplaceVisitor : ExpressionVisitor
{
private readonly Expression from, to;
public ReplaceVisitor(Expression from, Expression to)
{
this.from = from;
this.to = to;
}
public override Expression Visit(Expression node)
{
return node == from ? to : base.Visit(node);
}
}
Another approach, allowing things to be even more high level, is to write a Compose method that lets you compose expressions easily. Conceptually we'll have two lambdas, and we want to create a lambda that represents invoking one and passing its result to the other, and then it returning the result:
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);
}
This uses more or less the same strategy that we used above, but it generalizes it instead of special casing it to your specific expressions.
We then have one remaining helper method to make before putting the pieces together; creating a method that represents accessing a property as defined by the string name of the property:
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);
}
Using these two helper methods (that aren't dependent on any particular situation), we can now construct the lambda that we want without any custom built expression manipulation:
private static Expression<Func<T, bool>> BuildContainsPredicate<T>(
string propertyName, string propertyValue)
{
return MemberSelector<T>(propertyName)
.Compose(prop => prop.ToLower().Contains(propertyValue));
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With