0

Is there is a way to shortcut this code

Expression<Func<MyType, bool>> a => t => ...;
Expression<Func<MyType, bool>> b => t => ...;
Expression<Func<MyType, bool>> c => t => ...;

await myIQueryableOfMyType
  .Where(a)
  .Where(b)
  .Where(c)
  .ToArrayAsync()

in a way that I have had a single Experssion withing a Single Where clause. My purpose is to replace the "a", "b", "c" expressions to variable set of conditions.

I have tried to aggregate a function and set it in the new Lambda Expression

Expression<Func<MyType, bool>>[] predicates = ...

Func<MyType, bool> func = t => predicates == null || predicates.Aggregate(true, (prev, current) => prev && current.Compile()(t));

but it does not work for IQueryable collections of Entity Framework

Yaugen Vlasau
  • 2,148
  • 1
  • 17
  • 38
  • Possible duplicate of [Combining two expressions (Expression>)](https://stackoverflow.com/questions/457316/combining-two-expressions-expressionfunct-bool) – Aleks Andreev Sep 25 '19 at 13:09

1 Answers1

2

Expression is basically in-memory code representation, so it is definitely possible.

Example code:

static void Main(string[] args)
{
    Expression<Func<MyType, bool>> a = t => t.Id == 1;
    Expression<Func<MyType, bool>> b = t => t.Name == "Name1";
    Expression<Func<MyType, bool>> c = t => t.Desc == "Desc1";

    Expression<Func<MyType, bool>> combined = LinqExtensions.And(a, b, c);

    await myIQueryableOfMyType
        .Where(combined)
        .ToArrayAsync()
}

public static class LinqExtensions
{
    // Combine array of predicates into one
    // For example for input:
    //    [ t1 => t1.Id == 1, t2 => t2.Name == "Name1", t3 => t3.Desc == "Desc1" ]
    // output will be
    //    p => p.Id == 1 && p.Name == "Name1" && p.Desc == "Desc1"
    public static Expression<Func<T, bool>> And<T>(params Expression<Func<T, bool>>[] predicates)
    {
        // this is `p`
        ParameterExpression param = Expression.Parameter(typeof(T));
        Expression body = null;

        foreach (Expression<Func<T, bool>> predicate in predicates)
        {
            body = body == null
                // first expression, just assign it to body
                ? predicate.Body
                // join body with predicate using &&
                : Expression.AndAlso(body, predicate.Body);
        }

        // Create lambda
        return Expression.Lambda<Func<T, bool>>(
            // this is where we replace t1, t2, t3 with p
            new ParamExpressionVisitor(param).Visit(body),
            param
        );
    }

    private class ParamExpressionVisitor : ExpressionVisitor
    {
        private ParameterExpression _parameter;

        public ParamExpressionVisitor(ParameterExpression parameter)
        {
            this._parameter = parameter;
        }

        protected override Expression VisitParameter(ParameterExpression node)
        {
            if (node.Type == this._parameter.Type)
            {
                return this._parameter;
            }

            return base.VisitParameter(node);
        }
    }
}
Krzysztof
  • 15,900
  • 2
  • 46
  • 76