7

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

AJC
  • 1,853
  • 1
  • 17
  • 28

3 Answers3

25

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
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 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. – AJC Aug 30 '11 at 19:30
  • @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. – Jon Skeet Aug 30 '11 at 19:33
  • yeah, Sorry I got a little lazy there for a second. I'll pass the Type as a parameter... Thanks... – AJC Aug 30 '11 at 19:35
  • 1
    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? Thanks – AJC Aug 30 '11 at 20:51
  • @AJC: Yes, use an alternative overload of `Expression.Call` which takes a `MethodInfo` instead of a `string` to identify the method. – Jon Skeet Aug 31 '11 at 01:11
  • 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. – ThisGuy Jun 06 '13 at 23:47
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);


    }
Icarus
  • 63,293
  • 14
  • 100
  • 115
  • 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? – Sagar Dev Timilsina Aug 29 '18 at 11:20
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();
Mudasser
  • 31
  • 6
  • 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. – Shaul Behr Feb 11 '19 at 14:28