Build an Expression Tree with multiple parameters

c# expression-trees lambda

Question

I am trying to create a method that can accept multiple property names and a string value that will be use to research in entity possessing those properties

I want to a generic method to replace this one:

    public static Client[] GetClientByInfo(string info)
    {
        return Context.Db.Clients.Where(c =>
            c.LastName.Contains(info) ||
            c.FirstName.Contains(info)).ToArray();
    }

So I tried this:

    public static T[] FindByText<T>(string text, string[] properties)
        where T: class
    {
        return Context.Db.Set<T>().AsNoTracking().Where(PropertyEquals<T, string>(properties, text)).ToArray();
    }

    public static Expression<Func<TItem, bool>> PropertyEquals<TItem, TValue>(string[] properties, TValue value)
    {
        MethodInfo startWithMethod = typeof(string).GetMethod("StartsWith", BindingFlags.Public | BindingFlags.Instance, null, new Type[] { typeof(string) }, null);
        ParameterExpression parameter = Expression.Parameter(typeof(TItem));
        ConstantExpression constant = Expression.Constant(value, typeof(TValue));
        MemberExpression[] members = new MemberExpression[properties.Length];
        for (int i = 0; i < properties.Length; i++)
            members[i] = Expression.Property(parameter, properties[i]);

        MethodCallExpression callExp = Expression.Call(parameter, startWithMethod, members);

        return Expression.Lambda<Func<TItem, bool>>(callExp, parameter);
    }

I do something wrong when I call Expression.Call Is it possible to build an expression like this or i must use concatenate expression?

Accepted Answer

I'm not sure what you want to do with each parameter. Assuming that all of the fields are string, and that you want to call StartsWith against each of them, this will work:

public static Expression<Func<TItem, bool>> PropertyEquals<TItem>(string[] properties, string value)
{
    MethodInfo startWithMethod = typeof(string).GetMethod("StartsWith", BindingFlags.Public | BindingFlags.Instance, null, new Type[] { typeof(string) }, null);
    ParameterExpression parameter = Expression.Parameter(typeof(TItem));
    ConstantExpression constant = Expression.Constant(value);
    MemberExpression[] members = new MemberExpression[properties.Length];
    for (int i = 0; i < properties.Length; i++)
        members[i] = Expression.Property(parameter, properties[i]);

    Expression predicate = null;
    foreach (var item in members)
    {
        MethodCallExpression callExp = Expression.Call(item, startWithMethod, constant);
        predicate = predicate == null 
            ? (Expression)callExp
            : Expression.OrElse(predicate, callExp);
    }

    return Expression.Lambda<Func<TItem, bool>>(predicate, parameter);
}


Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Is this KB legal? Yes, learn why
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Is this KB legal? Yes, learn why