2

I have a list of Linq expressions like:

List<Expression<Func<Customer, bool>>>

I need to add a whole bunch of predicates from a search page, like:

x.Name.Contains(searchString)
x.Description.Contains(searchString)
...

I want to create a method so I don't end up with a mass of duplicated code. Something with a signature like:

void AddCustomerPredicate(List<Expression<Func<Customer, bool>>> predicates, Expression<Func<Customer, string>> prop, string searchString);

Which I would use like:

var predicates = new List<Expression<Func<Customer, bool>>>();
AddCustomerPredicate(predicates, x => x.Name, this.Name);
AddCustomerPredicate(predicates, x => x.Description, this.Description);
...

I've simplified the problem a bit, but that is the gist of it. I haven't done much work with expression trees and the like, so I'm not sure how to implement this method?

**EDIT**

I might have over simplified the problem too much. I know how to add to the list like predicates.Add(x => x.Name.Contains(this.searchString)), but I have various things I want to do on each search parameter before adding it to the list (e.g. check for null or empty). I therefore want to call a method on each search parameter as above, so all of that stuff can be contained in a single method.

Cocowalla
  • 13,822
  • 6
  • 66
  • 112
  • Do you want to evaluate all the predicates on each result? And if so, do you want to "AND" them together, or "OR" them? – Mike Strobel Nov 05 '13 at 15:28
  • I want to AND them together. I've no problem with that part tho, it's dynamically adding to the list using a method I can't figure out – Cocowalla Nov 05 '13 at 15:29
  • 1
    I'm not sure I understand that nature of your difficulty. You would add the expressions to the list just as you would add anything to a list, e.g., the same way you would add a string to a list of strings. Is there a deeper requirement you have left out? Are you trying to avoid duplicates? – Mike Strobel Nov 05 '13 at 15:31
  • Never mind, I think I see what you are asking. You want to transform the accessor expression into a predicate that checks against the provided value, yes? – Mike Strobel Nov 05 '13 at 15:32

2 Answers2

3

What you really need is to have a method for translated Expression<Func<T, string>> to Expression<Func<T, bool>> :

public static Expression<Func<T, bool>> GetContainsExpression<T>(Expression<Func<T, string>> prop, string searchString)
{
    var method = typeof(string).GetMethod("Contains", new[] { typeof(string) });
    var someValue = Expression.Constant(searchString, typeof(string));
    var containsMethodExp = Expression.Call(prop.Body, method, someValue);

    return Expression.Lambda<Func<T, bool>>(containsMethodExp, prop.Parameters[0]);
}

This is inspired by this other answer : How do I create an expression tree to represent 'String.Contains(“term”)' in C#?

var predicates = new List<Expression<Func<Customer, bool>>>();
predicates.Add(GetContainsExpression((Customer x) => x.Name, this.Name));
predicates.Add(GetContainsExpression((Customer x) => x.Description, this.Description));
Community
  • 1
  • 1
Cyril Gandon
  • 16,830
  • 14
  • 78
  • 122
2

I believe this will do what you want, and will also handle null property values (so calling Contains() does not throw):

private static void AddCustomerPredicate(
    List<Expression<Func<Customer, bool>>> predicates,
    Expression<Func<Customer, string>> accessor,
    string searchString)
{
    var x = accessor.Parameters[0];
    var temp = Expression.Variable(typeof(string), "temp");

    var predicate = Expression.Lambda<Func<Customer, bool>>(
        Expression.Block(
            new[] { temp },
            Expression.Assign(
                temp,
                accessor.Body),
            Expression.AndAlso(
                Expression.IsFalse(
                    Expression.ReferenceEqual(
                        temp,
                        Expression.Default(typeof(string)))),
                Expression.Call(
                    temp,
                    "Contains",
                    Type.EmptyTypes,
                    Expression.Constant(searchString)))),
        x);

    predicates.Add(predicate);
}
Mike Strobel
  • 25,075
  • 57
  • 69