1

I learn how to build simple expression which like

c=>c.code=='XXX';

I create these expression by method below:

public static Expression<Func<T, bool>> BuildStringEqualLambda(string propertyName, string propertyValue)
{
    ParameterExpression parameterExp = Expression.Parameter(typeof(T), "type");
    Expression propertyExp = parameterExp;
    foreach (var property in propertyName.Split('.'))
    {
        propertyExp = Expression.PropertyOrField(propertyExp, property);
    }
    Expression right = Expression.Constant(propertyValue);
    Expression e1 = Expression.Equal(propertyExp, right);
    return Expression.Lambda<Func<T, bool>>(e1, new ParameterExpression[] { parameterExp });
}

I think I can build expression

o=>o.code=='XXX' || c.name=='XXX'

But I don't know how can I build method Any.

Thanks

user2155362
  • 1,657
  • 5
  • 18
  • 30
  • take a look https://stackoverflow.com/questions/34730432/how-do-i-build-expression-call-for-any-method-with-generic-parameter – kashi_rock Jun 02 '17 at 08:51

1 Answers1

1

It should be something like:

public static Expression<Func<T, bool>> BuildStringEqualLambda<T>(params Tuple<string, string>[] propertyNameValues)
{
    if (propertyNameValues == null || propertyNameValues.Length == 0)
    {
        throw new ArgumentException(nameof(propertyNameValues));
    }

    ParameterExpression parameterExp = Expression.Parameter(typeof(T), "type");

    Expression body = null;

    foreach (var propertyNameValue in propertyNameValues)
    {
        Expression propertyExp = parameterExp;

        foreach (var property in propertyNameValue.Item1.Split('.'))
        {
            propertyExp = Expression.PropertyOrField(propertyExp, property);
        }

        Expression right = Expression.Constant(propertyNameValue.Item2);
        Expression eq = Expression.Equal(propertyExp, right);

        body = body == null ? eq : Expression.OrElse(body, eq);
    }

    return Expression.Lambda<Func<T, bool>>(body, new ParameterExpression[] { parameterExp });
}

You can probably use some funny LINQ with Aggregate to reduce the number of lines, but it would be undebuggable.

In the end you use the Expression.OrElse (not the Expression.Or that is |!) and you handle the first element case.

Use it like:

var exp = BuildStringEqualLambda(
    Tuple.Create("prop1", "value1"),
    Tuple.Create("prop2", "value2"),
    Tuple.Create("prop3", "value3")
);

Using some LINQ and Aggregate (for those who can't live without LINQing everything) (note that while I wouldn't ever use the LINQed version of the code... It is quite unreadable... Enumerable.Aggregate is "terrible" ):

public static Expression<Func<T, bool>> BuildStringEqualLambda<T>(params Tuple<string, string>[] propertyNameValues)
{
    if (propertyNameValues == null || propertyNameValues.Length == 0)
    {
        throw new ArgumentException(nameof(propertyNameValues));
    }

    ParameterExpression parameterExp = Expression.Parameter(typeof(T), "type");

    Expression body = propertyNameValues
        .Select(x => BuildEqualityExpression<T>(parameterExp, x.Item1, x.Item2))
        .Aggregate((acc, x) => Expression.OrElse(acc, x));

    return Expression.Lambda<Func<T, bool>>(body, new ParameterExpression[] { parameterExp });
}

private static Expression BuildEqualityExpression<T>(ParameterExpression parameterExp, string propertyName, string propertyValue)
{
    Expression propertyExp = propertyName
        .Split('.')
        .Aggregate((Expression)parameterExp, (acc, x) => Expression.PropertyOrField(acc, x));

    Expression right = Expression.Constant(propertyValue);
    Expression eq = Expression.Equal(propertyExp, right);
    return eq;
}
xanatos
  • 109,618
  • 12
  • 197
  • 280