fingers10
fingers10

Reputation: 7927

How to convert LambdaExpression to Expression<Func<T,bool>> in C#

I have the below code that generates LambdaExpression at run time based on my SearchTerm inputs. I'm trying to build a dynamic where clause. However I'm stuck at how to convert from LambdaExpression to Expression<Func<T,bool>>

private static Expression<Func<T,bool>> GetSearchAppliedQuery(IEnumerable<SearchTerm> terms)
{
    var parameterExpression = ExpressionHelper.Parameter<T>();
    Expression finalExpression = Expression.Constant(true);
    Expression subExpression = Expression.Constant(false);

    // Build up the LINQ Expression backwards:
    // query = query.Where(x => x.Property == "Value" && (x.AnotherProperty == "Value" || x.SomeAnotherProperty == "Value"));

    foreach (var term in terms)
    {
        var hasMultipleTerms = term.EntityName?.Contains(',') ?? false;

        if (hasMultipleTerms)
        {
            var entityTerms = term.EntityName.Split(',');

            foreach (var entityTerm in entityTerms)
            {
                term.EntityName = entityTerm;

                // x => x.Property == "Value" || x.AnotherProperty == "Value"
                subExpression = Expression.OrElse(subExpression, GetComparisonExpression(term, parameterExpression));
            }
        }

        // x => x.Property == "Value" && x.AnotherProperty == "Value"
        finalExpression = Expression.AndAlso(finalExpression, hasMultipleTerms ? subExpression : GetComparisonExpression(term, parameterExpression));
    }

    // x => x.Property == "Value" && (x.AnotherProperty == "Value" || x.SomeAnotherProperty == "Value")
    var lambdaExpression = ExpressionHelper.GetLambda<T, bool>(parameterExpression, finalExpression);

    // How to do this conversion??
    Expression<Func<T,bool>> returnValue = ..??;

    return returnValue;
}

I'm trying to apply the result of above method to get the query as shown below:

public static IQueryable<T> GetQuery(IQueryable<T> inputQuery, ISpecification<T> specification)
{
    var query = inputQuery;

    // modify the IQueryable using the specification's criteria expression
    if (specification.Criteria != null)
    {
        query = query.Where(specification.Criteria);
    }

    ...
    return query;
}

So that my final query will look like,

query = query.Where(x => x.Property == "Value" && (x.AnotherProperty == "Value" || x.SomeAnotherProperty == "Value"))

Edit-1: Adding the ExpressionHelper.GetLambda method as requested by @Ivan Stoev

public static class ExpressionHelper
{
    public static LambdaExpression GetLambda<TSource, TDest>(ParameterExpression obj, Expression arg)
    {
        return GetLambda(typeof(TSource), typeof(TDest), obj, arg);
    }

    public static LambdaExpression GetLambda(Type source, Type dest, ParameterExpression obj, Expression arg)
    {
        var lambdaBuilder = GetLambdaFuncBuilder(source, dest);
        return (LambdaExpression)lambdaBuilder.Invoke(null, new object[] { arg, new[] { obj } });
    }

    private static MethodInfo GetLambdaFuncBuilder(Type source, Type dest)
    {
        var predicateType = typeof(Func<,>).MakeGenericType(source, dest);
        return LambdaMethod.MakeGenericMethod(predicateType);
    }
}

Am I missing something very basic or doing anything wrong? Please assist.

Upvotes: 1

Views: 2788

Answers (1)

Ivan Stoev
Ivan Stoev

Reputation: 205539

The ExpressionHelper.GetLambda<T, bool> method used to obtain the lambda expression hides its actual type, which is the desired Expression<Func<T, bool>>, so all you need is to use a cast operator:

return (Expression<Func<T, bool>>)lambdaExpression;

Or better, either change the result type of ExpressionHelper.GetLambda<TSource, TDest> to Expression<Func<TSource, TDest>>, or don't use that helper method - when you know the generic type arguments at compile time, simply use one if the generic Expression.Lambda methods (ExpressionHelper.GetLambda<TSource, TDest> seems to be the equivalent of Expression.Lambda<Func<TSource, TDest>>), e.g.

var lambdaExpression = Expression.Lambda<Func<T, bool>>(parameterExpression, finalExpression);

Upvotes: 1

Related Questions