J-R Choiniere
J-R Choiniere

Reputation: 694

Build an Expression Tree with multiple parameters

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?

Upvotes: 0

Views: 2151

Answers (1)

Shlomo
Shlomo

Reputation: 14350

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

Upvotes: 4

Related Questions