Stacker
Stacker

Reputation: 8237

Func or Predicate to ExpressionTree

Lets say i have :

 Func<Customer,bool > a = (c) => c.fullName == "John";

now i want to convert to expressiontree any way to do that ?

i know i can define it from the first place as expressiontree but the situation i have is different because i must concatenate some lambda expressions first then pass it to a method which take expressiontree, doing so result in compile time error!

example:

        Func<Customer, bool> a = (c) => c.fullName == "John";
        Func<Customer, bool> b = (c) => c.LastName == "Smith";
        Func<Customer, bool> final = c => a(c) && b(c); 

now i want to pass final to a method which takes

ExpressionTree<Func<Customer,bool >>

it gives compile time error

thanks in advance

Upvotes: 3

Views: 1167

Answers (4)

Andre Vianna
Andre Vianna

Reputation: 1732

Here is a solution that I think will work for you. I converts a Func<TInput, TOutput> to a Expression<Func<TInput, TOutput>>.

It was based in this post from ElegantCode

In this example I used a Func:

using System;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;

namespace ConsoleApplication1
{
    class Program
    {
        public static bool Method(Expression<Func<int, bool>> predicate, int value)
        {
            return predicate.Compile()(value);
        }

        static void Main(string[] args)
        {
            Func<int, bool> testPredicate = n => n == 1;
            var output = ConvertFuncToExpression(testPredicate);
            Console.WriteLine(Method(output, 3));
            Console.WriteLine(Method(output, 1));
        }

        private static Expression<TDelegate> CreateExpression<TDelegate>(MethodBase method)
        {
            var delegateArguments = typeof(TDelegate).GetMethod("Invoke").GetParameters().Select((parameter, index) => Expression.Parameter(parameter.ParameterType, "param_" + index)).ToArray();
            if (delegateArguments.Count() != method.GetParameters().Count()) throw new InvalidOperationException("The number of parameters of the requested delegate does not match the number parameters of the specified method.");

            var argumentsTypes = method.GetGenericArguments();
            argumentsTypes = (argumentsTypes.Length > 0) ? argumentsTypes : null;
            var convertedArguments = method.GetParameters().Select((parameter, index) => Expression.Convert(delegateArguments[index], parameter.ParameterType)).ToArray();
            var call = Expression.Call(method.DeclaringType, method.Name, argumentsTypes, convertedArguments);

            var lambda = Expression.Lambda<TDelegate>(call, delegateArguments);
            return lambda;
        }

        private static Expression<Func<TIn1, TOut>> ConvertFuncToExpression<TIn1, TOut>(Func<TIn1, TOut> input)
        {
            MethodInfo method = input.Method;
            return CreateExpression<Func<TIn1, TOut>>(method);
        }
    }
}

Upvotes: 1

Timwi
Timwi

Reputation: 66573

You cannot do that. A variable of type Func<...> is a delegate, which is basically like a pointer to a memory location that contains the compiled code for the lambda expression. There is no functionality in .NET to turn already-compiled code back into an expression tree.

Depending on what you are trying to do, maybe you can contend with an incomplete solution: create an expression tree that calls the delegates. Since I don’t know anything about the method to which you want to pass the expression tree, I have no idea whether this is at all a feasible solution for you.

Summary: If you want the complete expression tree of all the expressions, you need to make sure that they are expression trees right from the start. As soon as you compile it into a delegate, the expression tree is lost.

Once you’ve made sure that they are expression trees, you can combine them using something like the following:

Expression<Func<Customer, bool>> a = c => c.FullName == "John";
Expression<Func<Customer, bool>> b = c => c.LastName == "Smith";

var cp = Expression.Parameter(typeof(Customer), "c");

var ai = Expression.Invoke(a, cp);
var bi = Expression.Invoke(b, cp);

var final = Expression.Lambda<Func<Customer, bool>>(
    Expression.AndAlso(ai, bi), cp);

Of course, this uses the AndAlso operator (&&); you can also use OrElse for || etc.

Upvotes: 5

Daniel Pratt
Daniel Pratt

Reputation: 12077

Regarding your revised question, I think this will work:

Expression<Func<Customer, bool>> a = (c) => c.FullName == "John";
Expression<Func<Customer, bool>> b = (c) => c.LastName == "Smith";

var cp = Expression.Parameter(typeof(Customer), "c");

var ai = Expression.Invoke(a, cp);
var bi = Expression.Invoke(b, cp);

var final = Expression.Lambda<Func<Customer, bool>>(Expression.And(ai, bi), cp);

Upvotes: 2

Shlomo
Shlomo

Reputation: 14350

You can go from Expression to a Func, but not the other way around.

You can do this:

Expression<Func<Customer, bool>> exprA = (c) => c.fullName == "John";
Func<Customer, bool> funcA = exprA.Compile();

But there's no way to go the other way.

Upvotes: 2

Related Questions