Reputation: 8237
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
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
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
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
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