Expression Tree with chained string methods - c#

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

You have to get the expression for the ToLowermethod, then use it in the Contains expression
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;
}

With respect to Andre's answer, which is essentially what I came up after Servy's initial comment, I wanted to post what I came up with:
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;
}

Related

Create lambda predicate expression to access object sub properties

I have a extension method which creates a lambda predicate expression, which I use to filter data when the value is not null.
It works fine for cases like
query.FilterBy(obj => obj.MyProp, value);
but it fails in cases like
query.FilterBy(obj => obj.MyObject.MyProp, value);
In this case the expression for MyProp property is accessed on obj, which of course doesn't have the property.
I'm not familiar with the expression tree and could not figure out how to modify the extension method to access properties of sub objects. Does anyone have an idea how I could do it?
The extension method:
public static IQueryable<TSource> FilterBy<TSource, TProp>(this IQueryable<TSource> source,
Expression<Func<TSource, TProp>> property,
TProp value)
{
if (value != null)
{
var parameter = Expression.Parameter(typeof(TSource));
var memberExpression = property.Body as MemberExpression ??
((UnaryExpression)property.Body).Operand as MemberExpression;
if (memberExpression is null)
{
throw new InvalidOperationException("Please provide a valid property expression.");
}
var propertyName = memberExpression.Member.Name;
var body = Expression.Equal(
Expression.Property(parameter, propertyName),
Expression.Constant(value));
var predicate = Expression.Lambda<Func<TSource, bool>>(body, parameter);
source = source.Where(predicate);
}
return source;
}
You could simply use epxression passed like parameter property of FilterBy() function for creating new filtering expression instead of trying to create expression from scratch. It will works with "sub-properties".
public static IQueryable<TSource> FilterBy<TSource, TProp>(this IQueryable<TSource> source,
Expression<Func<TSource, TProp>> property,
TProp value)
{
if (value != null)
{
var expression = Expression.Equal(property.Body, Expression.Constant(value));
var predicate = Expression.Lambda<Func<TSource, bool>>(expression, property.Parameters[0]);
source = source.Where(predicate);
return source;
}
return source;
}

Entity Framework's navigation properties are null after sort by nullable

I use the following code to convert order by expressions so nullable columns can be ordered by as well.
protected virtual Expression<Func<T, object>> GetSorting(string ordering)
{
Expression<Func<T, object>> expression = default(Expression<Func<T, object>>);
IEnumerable<Order> sortObjects = string.IsNullOrEmpty(ordering) ? null : JsonConvert.DeserializeObject<IEnumerable<Order>>(ordering);
if (sortObjects != null)
{
foreach (Order sortObject in sortObjects)
{
Expression<Func<T, object>> currentExpression = this.GetExpression(sortObject.Property);
expression = this.CombineExpressions(expression, currentExpression);
}
}
return expression;
}
private Expression<Func<T, object>> GetExpression(string propertyName)
{
Type type = typeof(T);
ParameterExpression parameter = Expression.Parameter(type, "x");
MemberExpression propertyReference = Expression.Property(parameter, propertyName);
Expression conversion = Expression.Convert(propertyReference, typeof(object));
Expression<Func<T, object>> currentExpression = Expression.Lambda<Func<T, object>>(conversion, new[] { parameter });
return currentExpression;
}
private Expression<Func<T, object>> CombineExpressions(Expression<Func<T, object>> expression, Expression<Func<T, object>> currentExpression)
{
if (expression == default(Expression<Func<T, object>>))
{
expression = currentExpression;
}
else
{
// Combine the two expressions' body together
BinaryExpression body = Expression.AndAlso(expression.Body, currentExpression.Body);
ParameterExpression[] parameters = new ParameterExpression[1] { Expression.Parameter(typeof(T), expression.Parameters.First().Name) };
// Convert the BinaryExpression to the requested type
Expression<Func<T, object>> lambda = Expression.Lambda<Func<T, object>>(body, parameters);
expression = lambda;
}
return expression;
}
This code works perfectly for all non-navigation properties but it seems as if the navigation properties aren't queried anymore. I use a Select expression to load the navigation properties, like so:
protected override Expression<Func<Resource, ResourceViewModel>> Selector
{
get
{
return (x) => new ResourceViewModel()
{
ResourceId = x.ResourceId,
DisplayName = x.DisplayName,
ResourceType = x.ResourceType != null ? x.ResourceType.Name : string.Empty,
}
}
}
If I don't have anything to order by, the navigation properties are loaded. But as soon as there is anything to order by, the navigation property is null. If I would skip the ternary operation and straightly go to the ResourceType.Name property, I'd get an exception telling me the lambda_method has thrown a NullReference exception.
I know ordering navigation properties doesn't work as such but that's not the problem. Ordering by 'regular' properties causes the problem.
Any thoughts on this?
Turns out the issue wasn't as complex as I thought. The problem was that I created the wrong expression tree. You can nest the expression so you can navigate to the property of a property.
The solution below should explain this (this isn't how I actually solved the issue but it should make it clear):
private Expression<Func<T, object>> GetExpression(string parentClass, string propertyName)
{
Type type = typeof(T);
ParameterExpression parameter = Expression.Parameter(type, "x");
// Get parent class expression
// Will result in (x) => x.MyNavigationPropertyWhichIsAClass
MemberExpression propertyReference1 = Expression.Property(parameter, parentclass);
// Navigate to the property of the navigation property class
// Will result in (x) => x.MyNavigationPropertyWhichIsAClass.MyPropertyIWantToSort
MemberExpression propertyReference2 = Expression.Property(propertyRefernce1, propertyName);
Expression conversion = Expression.Convert(propertyReference2, typeof(object));
Expression<Func<T, object>> currentExpression = Expression.Lambda<Func<T, object>>(conversion, new[] { parameter });
return currentExpression;
}

Convert a LambdaExpression to typed Expression<Func<T, object>> with boxing return values [duplicate]

This question already has an answer here:
Create Func to return both reference- and value-types
(1 answer)
Closed 7 years ago.
I want to build the following Expression:
Expression<Func<T, object>>
I have currently the following code:
public class Strategy<T>
{
private static Expression<Func<T, object>> GetIt(PropertyInfo propertyInfo)
{
ParameterExpression parameter = Expression.Parameter(typeof(T));
MemberExpression property = Expression.Property(parameter, propertyInfo);
Type funcType = typeof(Func<,>).MakeGenericType(typeof(T), typeof(object));
//next line fails: can't convert int to object
LambdaExpression lambda = Expression.Lambda(funcType, property, parameter);
Expression<Func<T, object>> retval = (Expression<Func<T, object>>)lambda;
return retval;
}
}
My PropertyInfo object has a returntype of "int".
How can i use boxing in my Expression?
public class ExpressionGetter<T>
{
public static Expression<Func<T, Object>> Get(PropertyInfo pInfo)
{
ParameterExpression parameter = Expression.Parameter(typeof(T));
MemberExpression property = Expression.Property(parameter, pInfo);
Type funcType = typeof(Func<,>).MakeGenericType(typeof(T), typeof(object));
//next line fails: can't convert int to object
LambdaExpression lambda;
if (typeof (T).IsClass == false && typeof (T).IsInterface == false)
lambda = Expression.Lambda(funcType, Expression.Convert(property, typeof (Object)), parameter);
else
lambda = Expression.Lambda(funcType, property, parameter);
return (Expression<Func<T, object>>)lambda;
}
}

Compound expression with variable comparator?

I'm writing a query framework, and trying to make it as generic as possible.
Let's say I have a query based on person, and I want the ability to filter on both the first and last names, and in both cases I want to be able to use filter conditions like StartsWith, 'EndsWith, Contains, Equals.
So now I have a method:
private Expression<Func<Person, bool>> FirstNameFilter(Comparator comparator, string compareValue) {
switch (comparator) {
case Comparator.Equal:
return p => p.FirstName == compareValue;
case Comparator.Contains:
return p => p.FirstName.Contains(compareValue);
case Comparator.StartsWith:
return p => p.FirstName.StartsWith(compareValue);
// etc.
}
}
Now, I also want to be able to build the same filter for LastName. Seems silly and wasteful to copy and paste the whole thing over again, just replacing p.FirstName with p.LastName. I also have a bunch of other string fields that I want to filter on, and I really don't want to have to rewrite this whole method for each one!
Is there some way to abstract this, maybe using LinqKit, so that I can come out with a more generic method with the following approximate signature:
Expression<Func<Person, bool>> GetFilter(Expression<Func<Person, string>> stringExpression, Comparator comparator, string compareValue) {}
such that I could, inside FirstNameFilter, invoke it like so:
return GetFilter(p => p.FirstName, comparator, compareValue);
Something like that (untested, but you have the idea) should help you to build the needed expressions :
public static class LinqQueries
{
private static MethodInfo toLowerMethod = typeof(String).GetMethod("ToLower", Type.EmptyTypes);
private static MethodInfo startsWithMethod= typeof(String).GetMethod("StartsWith", new Type[] { typeof(String) });
private static MethodInfo containsMethod = typeof(String).GetMethod("Contains", new Type[] { typeof(String) });
private static MethodInfo endsWithMethod= typeof(String).GetMethod("EndsWith", new Type[] { typeof(String) });
public static Expression<Func<T, bool>> GetFilter(Expression<Func<T, string>> expression, Comparator comparator, string compareValue) {
ParameterExpression parameterExpression = null;
var memberExpression = GetMemberExpression(expression.Body, out parameterExpression);
Expression constExp = Expression.Constant(compareValue);
switch (comparator) {
case Comparator.Contains:
memberExpression = Expression.Call(memberExpression, containsMethod,constExp);
break;
case Comparator.StartsWith:
memberExpression = Expression.Call(memberExpression, startsWithMethod, constExp);
break;
//etc.
default :
memberExpression = Expression.Equal(memberExpression, constExp);
break;
}
return Expression.Lambda<Func<T, bool>>(memberExpression, new[]{parameterExpression});
}
private static Expression GetMemberExpression(Expression expression, out ParameterExpression parameterExpression)
{
parameterExpression = null;
if (expression is MemberExpression)
{
var memberExpression = expression as MemberExpression;
while (!(memberExpression.Expression is ParameterExpression))
memberExpression = memberExpression.Expression as MemberExpression;
parameterExpression = memberExpression.Expression as ParameterExpression;
return expression as MemberExpression;
}
if (expression is MethodCallExpression)
{
var methodCallExpression = expression as MethodCallExpression;
parameterExpression = methodCallExpression.Object as ParameterExpression;
return methodCallExpression;
}
return null;
}
}

Returning a nested generic Expression<Func<T, bool>>

The error message is "The type or namespace name 'T' could not be found."
???
public static Expression<Func<T, bool>> MakeFilter(string prop, object val)
{
ParameterExpression pe = Expression.Parameter(typeof(T), "p");
PropertyInfo pi = typeof(T).GetProperty(prop);
MemberExpression me = Expression.MakeMemberAccess(pe, pi);
ConstantExpression ce = Expression.Constant(val);
BinaryExpression be = Expression.Equal(me, ce);
return Expression.Lambda<Func<T, bool>>(be, pe);
}
Related links:
Using reflection to address a Linqed property
http://social.msdn.microsoft.com/forums/en-US/linqprojectgeneral/thread/df9dba6e-4615-478d-9d8a-9fd80c941ea2/
Runtime creation of generic Func<T>
You need to make the method itself generic:
public static Expression<Func<T, bool>> MakeFilter<T>(string prop, object val)
-+-
^
+- this
There's no generic argument defined for your method. You should define one (MakeFilter<T>):
public static Expression<Func<T, bool>> MakeFilter<T>(string prop, object val)
{
ParameterExpression pe = Expression.Parameter(typeof(T), "p");
PropertyInfo pi = typeof(T).GetProperty(prop);
MemberExpression me = Expression.MakeMemberAccess(pe, pi);
ConstantExpression ce = Expression.Constant(val);
BinaryExpression be = Expression.Equal(me, ce);
return Expression.Lambda<Func<T, bool>>(be, pe);
}
The method needs to be declared as generic (MakeFilter<T>):
public static Expression<Func<T, bool>> MakeFilter<T>(string prop, object val)
Otherwise, how else would the caller be able to specify what T is?

Categories

Resources