Use reflection to get lambda expression from property Name
Asked Answered
W

3

8

I want to give the user the choice of searching by different properties. For instance

[INPUT TEXT] | [SELECT OPTION {ID, NAME, PHONE}] | [SEARCH]

And I would later build my query like this:

repository.Where(lambda-expression)

Where lambda-expression is build from the selected option {ID, NAME, PHONE} (For example: x => x.NAME.Equals(INPUT TEXT))

Is there a way to build the lambda from the Property name perhaps using reflection?

Thanks

Willardwillcox answered 30/8, 2011 at 16:40 Comment(0)
T
26

You don't build a lambda expression - you build an expression tree. It's not terribly hard, but it takes a little patience. In your sample you'd probably need:

ParameterExpression parameter = Expression.Parameter(typeof(Foo), "x");
Expression property = Expression.Property(parameter, propertyName);
Expression target = Expression.Constant(inputText);
Expression equalsMethod = Expression.Call(property, "Equals", null, target);
Expression<Func<Foo, bool>> lambda =
   Expression.Lambda<Func<Foo, bool>>(equalsMethod, parameter); 

That's assuming:

  • The repository element type is Foo
  • You want to use a property called propertyName
  • You want to compare for equality against inputText
Thalassic answered 30/8, 2011 at 16:46 Comment(6)
Thanks, it worked like a charm... I did get an error at first, and I had to change my type from IEnumerable to IQueryable, but I should have been using IQueryable to begin with.Willardwillcox
@AJC: Can you make the method a generic method and have it as a type parameter? Or just take the type as a normal parameter. It's hard to know what to advise as you haven't provided much context.Thalassic
yeah, Sorry I got a little lazy there for a second. I'll pass the Type as a parameter... Thanks...Willardwillcox
I am having a small problem. At first, I was using "Contains" for the method, but now I require an "Equals" comparator and I am getting this error: More than one method 'Equals' on type 'System.String' is compatible with the supplied arguments.. Any thoughts? ThanksWillardwillcox
@AJC: Yes, use an alternative overload of Expression.Call which takes a MethodInfo instead of a string to identify the method.Thalassic
This works great! I had to make one small tweak to get it to compile - define parameter as a ParameterExpression rather than just an Expression.Listing
F
3

For that sort of thing, I use something like this (note: does a Where "Like") :

 public static IQueryable<TEntity> Where<TEntity>(this IQueryable<TEntity> source, string propertyName, string value) 
    {

        Expression<Func<TEntity, bool>> whereExpression = x => x.GetType().InvokeMember(propertyName, BindingFlags.GetProperty, null, x, null).ObjectToString().IndexOf(value, StringComparison.InvariantCultureIgnoreCase) >= 0;

        return source.Where(whereExpression);


    }
Fountainhead answered 30/8, 2011 at 17:14 Comment(1)
This query is useful but when I checked using sql profiler, it first queries all the records and then applies the expression on top of it. How can we get the results including the filter expression in only one (optimised) query?Installation
L
2

I had to face the same sort of problem and the following method resolved my problem perfectly.

PropertyInfo propertyInfoObj = MyClassObject.GetType().GetProperty(PropertyName);
repository.Where(x => propertyInfoObj.GetValue(x) == SearchValue).Select(x => propertyInfoObj.GetValue(x)).FirstOrDefault();
Leonardoleoncavallo answered 22/2, 2015 at 13:45 Comment(1)
Have you checked Sql profiler to see what queries are being emitted? I have a feeling you're going to find your entire table being loaded into memory before the reflection code runs.Improper

© 2022 - 2024 — McMap. All rights reserved.