Expressions... What am I doing wrong? - c#

I've decided to take some golden time to learn a bit more about Expressions. I'm trying a very simple exercise, namely adding two numbers. I've hit an exception that's proving to be tricky to search for.
Here's my code
Expression<Func<int,int,int>> addExpr = (x, y) => x + y;
var p1 = Expression.Parameter(typeof(int), "p1");
var p2 = Expression.Parameter(typeof(int), "p2");
var lambda = Expression.Lambda<Func<int,int,int>>(addExpr, p1, p2); //<-here
var del = lambda.Compile();
var result = del(2,3); //expect 5
but this is throwing an ArgumentException : Expression of type 'System.Func`3[System.Int32,System.Int32,System.Int32]' cannot be used for return type 'System.Int32'
at the line indicated above. What have I done wrong?

You need to wrap the addExpr in an invoke using the expression parameters
Expression<Func<int,int,int>> addExpr = (x, y) => x + y;
var p1 = Expression.Parameter(typeof(int), "p1");
var p2 = Expression.Parameter(typeof(int), "p2");
var invokeExpression=Expression.Invoke(addExpr,p1,p2);
var lambda = Expression.Lambda<Func<int,int,int>>(invokeExpression,p1,p2);
var del = lambda.Compile();
var result=del(2,3);
The invoke is how you type p1 to x and p2 to y, alternatively you could just write the above as
var p1 = Expression.Parameter(typeof(int), "p1");
var p2 = Expression.Parameter(typeof(int), "p2");
var lambda=Expresion.Lambda<Func<int,int,int>>(Expression.Add(p1,p2),p1,p2);
var del = lambda.Compile();
var result=del(2,3);
Otherwise you need to grab the expression body into the lambda and pass the expression parameters.
var lambda=Expresion.Lambda<Func<int,int,int>>(addExpr.Body,addExpr.Parameters);

Your code should be:
var lambda = Expression.Lambda<Func<Expression<Func<int, int, int>>, int, int>(addExpr, p1, p2);
Your current code expects an int and your passing in Expression<Func<int, int, int>>.
Update
Actually the above won't compile, you would need to do:
var lambda = Expression.Lambda<Func<int, int, int>>(Expression.Add(p1, p2), p1, p2);

You need to decompose addExpr's body or preferably just write it from scratch ie Expression.Add(p1,p2) instead of addExpr.

Related

Expression trees - substitute expression

Consider two expression trees:
Expression<Func<float, float, float>> f1 = (x, y) => x + y;
Expression<Func<float, float>> f2 = x => x * x;
I want to substitute the expression f2 as a second parameter of f1 and obtain the following expression:
Expression<Func<float, float, float>> f3 = (x, y) => x + y * y;
The simplest way is to use Expression.Lambda and Expression.Invoke, but the result will look like
(x, y) => f1(x, f2(y))
But this is unacceptable for me due to ORM limitations that cannot handle invoke/lambda properly.
Is it possible to construct the expression without full traversal of expression trees? A working example that fulfill my needs can be found here but I want simpler solution.
You cannot do it without full traversal of both expressions. Fortunately, ExpressionVisitor makes full traversal really easy:
class ReplaceParameter : ExpressionVisitor {
private readonly Expression replacement;
private readonly ParameterExpression parameter;
public ReplaceParameter(
ParameterExpression parameter
, Expression replacement
) {
this.replacement = replacement;
this.parameter = parameter;
}
protected override Expression VisitParameter(ParameterExpression node) {
return node == parameter ? replacement : node;
}
}
Use this visitor twice to complete the replacement:
Expression<Func<float,float,float>> f1 = (x, y) => x + y;
Expression<Func<float,float>> f2 = x => x * x;
var pX = f2.Parameters[0];
var pY = f1.Parameters[1];
var replacerF2 = new ReplaceParameter(pX, pY);
var replacerF1 = new ReplaceParameter(pY, replacerF2.Visit(f2.Body));
var modifiedF1 = Expression.Lambda(
replacerF1.Visit(f1.Body)
, f1.Parameters
);
Console.WriteLine(modifiedF1);
The above prints
(x, y) => (x + (y * y))
Demo.

Multiply two lambda with Expression

Basically I am trying to multiply two lambdas with the class Expression but I don't even manage to build and can't find documentation on this. Here is my code:
var f = x=>x+2;
var g = x=>x+3;
var argX = Expression.Parameter(typeof(double), "x");
var fg = Expression.Multiply(Expression.Constant(g, typeof(Func<double, double>)), Expression.Constant(f, typeof(Func<double, double>)));
//var fg = Expression.Multiply(Expression.Constant(g),Expression.Constant(f));
var lambda = Expression.Lambda<Func<double, double>>(f3, argX);
return lambda.Compile();
Do you want something like this:
Expression<Func<double,double>> f = x => x + 2;
Expression<Func<double,double>> g = x => x + 3;
var param = Expression.Parameter(typeof(double));
var invokeF = Expression.Invoke(f, param);
var invokeG = Expression.Invoke(g, param);
var mult = Expression.Multiply(invokeF,invokeG);
var lambda = ((Expression<Func<double, double>>)Expression.Lambda(mult, param)).Compile();
If i call it this way:
lambda(3);
I get 30 as valid answer.
For this simply define f and g as Expression as well:
var argX = Expression.Parameter(typeof(double), "x");
var f = Expression.Add(argX, Expression.Constant(3.0)); // applies to x => x + 3
var g = Expression.Add(argX, Expression.Constant(2.0)); // applies to x => x + 2
var fg = Expression.Multiply(f, g); // applies to f(x) * g(x)
var lambda = Expression.Lambda<Func<double, double>>(fg, argX);
Now call it like this:
var r = lambda.Compile()(1);
Which returns 12.

How do i append a simple expression onto IQueryable

I have the following method:
public List<Customer> SearchTest(string city, int skip, int take)
{
EcomContext db = new EcomContext();
var results = db.Customers.Where(n => n.City == city).OrdeyBy(n => n.Name).Skip(skip).Take(10);
results = AddDeleteCheck<Customer>(results);
return results.ToList()
}
And this reusable method:
private IQueryable<T> AddArchivedCheck<T>(IQueryable<T> data)
{
var parameter = Expression.Parameter(typeof(T));
var e1 = Expression.Equal(Expression.Property(parameter, "Archived"), Expression.Constant(false));
var e2 = data.Expression;
var e3 = Expression.Lambda<Func<T, bool>>(Expression.AndAlso(e1, e2), parameter);
return data.Where(e3);
}
I want to be able to call this method from a number of different functions so i have made it generic. It should take the expression from the IQueryable object and add a check onto this (Archived == false).
I am getting this error:
The binary operator AndAlso is not defined for the types 'System.Boolean' and 'System.Linq.IQueryable` [Ecom.Customer]
It is easier than what you wrote:
private static IQueryable<T> AddArchivedCheck<T>(IQueryable<T> data)
{
var parameter = Expression.Parameter(typeof(T));
var e1 = Expression.Equal(Expression.Property(parameter, "Archived"), Expression.Constant(false));
var lambda = Expression.Lambda<Func<T, bool>>(e1, parameter);
return data.Where(lambda);
}
Remember that in Linq:
var result = query.Where(condition1).Where(condition2);
is equivalent to:
var result = query.Where(condition1 && condition2);

Get value from a ConstantExpression

I'm looking to get a value from an
var guid = Guid.Parse("SOMEGUID-GUID-GUID-GUID-SOMEGUIDGUID");
Expression<Func<Someobject, bool>> selector = x => x.SomeId == guid;
For logging purposes I need to be able to fish out that guid.
I tried the following code, which I feel is somewhat close to what I'm looking for, but not quite.
BinaryExpression binaryExpression = (BinaryExpression)selector.Body;
MemberExpression memberExpression = (MemberExpression)((UnaryExpression)binaryExpression.Right).Operand;
ConstantExpression constantExpression = (ConstantExpression)memberExpression.Expression;
Now, ConstantExpression exposes a member 'Value', which does contain what I'm looking for, but I'm a bit puzzled how to actually extract this.
And no:
var val = (Guid)constantExpression.Value;
Does not work :)
SOLVED
The end result looks like:
BinaryExpression binaryExpression = (BinaryExpression)selector.Body;
MemberExpression memberExpression = (MemberExpression)((UnaryExpression)binaryExpression.Right).Operand;
var myGuid = Expression.Lambda(memberExpression).Compile().DynamicInvoke();
Follow-up
I did some rudementary speed testing using the following code:
static void Main(string[] args)
{
var id = Guid.Parse("bleh");
Expression<Func<Thingemebob, bool>> selector = x => x.Id == id;
var tickList = new List<long>();
for (int i = 0; i < 100000; i++)
{
var sw = Stopwatch.StartNew();
GetValueWithExpressionsAndReflection(selector);
sw.Stop();
tickList.Add(sw.ElapsedTicks);
}
Trace.WriteLine("GetValueWithExpressionsAndReflection: Average over 100000, first call included: " + tickList.Average());
Trace.WriteLine("GetValueWithExpressionsAndReflection: First call: " + tickList[0]);
Trace.WriteLine("GetValueWithExpressionsAndReflection: Average over 100000, first call excluded: " + tickList.Skip(1).Average());
tickList = new List<long>();
for (int i = 0; i < 100000; i++)
{
var sw = Stopwatch.StartNew();
GetValueWithCompiledExpression(selector);
sw.Stop();
tickList.Add(sw.ElapsedTicks);
}
Trace.WriteLine("GetValueWithCompiledExpression: Average over 100000, first call included: " + tickList.Average());
Trace.WriteLine("GetValueWithCompiledExpression: First call: " + tickList[0]);
Trace.WriteLine("GetValueWithCompiledExpression: Average over 100000, first call excluded: " + tickList.Skip(1).Average());
Debugger.Break();
}
private static void GetValueWithCompiledExpression(Expression<Func<Note, bool>> selector)
{
BinaryExpression binaryExpression = (BinaryExpression)selector.Body;
MemberExpression memberExpression = (MemberExpression)((UnaryExpression)binaryExpression.Right).Operand;
var o = Expression.Lambda(memberExpression).Compile().DynamicInvoke();
}
private static void GetValueWithExpressionsAndReflection(Expression<Func<Note, bool>> selector)
{
BinaryExpression binaryExpression = (BinaryExpression)selector.Body;
MemberExpression memberExpression = (MemberExpression)((UnaryExpression)binaryExpression.Right).Operand;
ConstantExpression constantExpression = (ConstantExpression)memberExpression.Expression;
FieldInfo member = (FieldInfo)memberExpression.Member;
var instance = constantExpression.Value;
var guid = member.GetValue(instance);
}
Turns out the compile version is MUCH slower. We're looking at a huge difference. (Timing is in ticks):
GetValueWithExpressionsAndReflection: Average over 100000, first call included: 0,93122
GetValueWithExpressionsAndReflection: First call: 851
GetValueWithExpressionsAndReflection: Average over 100000, first call excluded: 0,922719227192272
Versus:
GetValueWithCompiledExpression: Average over 100000, first call included: 499,53669
GetValueWithCompiledExpression: First call: 16818
GetValueWithCompiledExpression: Average over 100000, first call excluded: 499,373503735037
Rudementary tests or not: no doubt I will be using the reflection version.
My results seem to be consistent with:
http://www.minddriven.de/index.php/technology/dot-net/c-sharp/efficient-expression-values
Your const expression has type EFAndExpressions.Program+<>c__DisplayClass0. This means that the expression has the following structure:
var compilerGeneratedClass = new compilerGeneratedClass() {
guid = Guid.Parse("SOMEGUID-GUID-GUID-GUID-SOMEGUIDGUID"); };
Expression<Func<Someobject, bool>> selector = x => x.SomeId == compilerGeneratedClass.guid;
The compiler does this for you. Use a decompiler to check out the details.
Now you know how the expression tree looks like and you can decompose it. You'll need to use reflection to obtain the runtime value of the compilerGeneratedClass.guid field or property.
This value is not part of the expression tree directly.

Is there a bug with nested invoke of LambdaExpression?

I tried to compile and calculate LambdaExpression like:
Plus(10, Plus(1,2))
But result is 4, not 13.
Code:
using System;
using System.Linq.Expressions;
namespace CheckLambdaExpressionBug
{
class Program
{
static void Main(string[] _args)
{
ParameterExpression p1 = Expression.Parameter(typeof (int), "p1");
ParameterExpression p2 = Expression.Parameter(typeof (int), "p2");
LambdaExpression lambda = Expression.Lambda(Expression.Call(typeof(Program).GetMethod("Plus"), p1, p2), p1, p2);
InvocationExpression exp1 = Expression.Invoke(
lambda,
Expression.Constant(1),
Expression.Constant(2)
);
InvocationExpression exp2 = Expression.Invoke(
lambda,
Expression.Constant(10),
exp1
);
var func = (Func<int>) Expression.Lambda(exp2).Compile();
int v = func();
Console.Out.WriteLine("Result = {0}", v);
}
public static int Plus(int a, int b)
{
return a + b;
}
}
}
Since nobody seems to be posting this:
It looks to be a bug in .NET 3.5, and is fixed in .NET 4.
whereas this seems to produce 13 in 3.5 too:
var plus = new Func<int, int, int>((a, b) => a + b);
var func = new Func<int>(() => plus(10, plus(1, 2)));
var res = func();
Console.WriteLine(res);
maybe i needs to assign the result to a local var
try this
var plus = new Func((a, b) => a + b);
var puls_1 = plus(1, 2);
var func = new Func(() => plus(10, plus_1));
var res = func();
Console.WriteLine(res);

Categories

Resources