Reputation: 9638
I'm trying to combine two working expressions.
left: an expression that returns the value of a property
Expression<Func<TimeSlot, Guid>> left = x => x.TimeSlotId;
right: an expression that returns a constant value (the value of the variable guid
)
Expression<Func<TimeSlot, Guid>> right = Expression.Lambda<Func<TimeSlot, Guid>>
(Expression.Constant(guid, typeof(Guid)), input);
Now I want to pack these two expressions into another expression that equates the two outcomes. This works:
// given a TimeSlot slot which guid is equal to the constent
bool eq1 = left.Compile()(slot) == right.Compile()(slot); // true
But this doesn't work ParameterExpression input = Expression.Parameter(typeof(TimeSlot));
Expression<Func<TimeSlot, bool>> expression = Expression.Lambda<Func<TimeSlot, bool>>
(Expression.Equal(left, right), input);
bool eq2 = expression.Compile()(slot); // false
I'm totally baffled why this outcome is different. Especially since the DebugView of this expression looks like this:
{Param_0 => (x => x.TimeSlotId == Param_0 => 351155b2-20a5-4c48-8722-ddf0e1f9055a)}
=
.Lambda #Lambda1<System.Func`2[SilverFit.Database.Model.TimeSlot,System.Boolean]>(SilverFit.Database.Model.TimeSlot $var1)
{
.Lambda #Lambda2<System.Func`2[SilverFit.Database.Model.TimeSlot,System.Guid]> == .Lambda #Lambda3<System.Func`2[SilverFit.Database.Model.TimeSlot,System.Guid]>
}
.Lambda #Lambda2<System.Func`2[SilverFit.Database.Model.TimeSlot,System.Guid]>(SilverFit.Database.Model.TimeSlot $x) {
$x.TimeSlotId
}
.Lambda #Lambda3<System.Func`2[SilverFit.Database.Model.TimeSlot,System.Guid]>(SilverFit.Database.Model.TimeSlot $var1) {
.Constant<System.Guid>(351155b2-20a5-4c48-8722-ddf0e1f9055a)
}
Does anyone know the correct way to create an expression that compares the outcome of an expression and a constant?
Upvotes: 2
Views: 2426
Reputation: 70681
The debug output you see is telling you precisely what is going on, and why it's not working. In particular, the things you are comparing with the equality expression is not the values returned by the expressions, but rather the expressions themselves.
One big clue is that both expressions require input, but even in the debug output you can see that that input is only being used in one side of the equality comparison, and there only as input parameter for the second expression.
Compiling both expressions separately is IMHO not a bad solution, actually. You can even cache the compilation result:
Func<TimeSlot, Guid> d1 = left.Compile(), d2 = right.Compile();
Func<TimeSlot, bool> d2 = x => d1(x) == d2(x);
But if you really want to compose the operation as a single expression, then you can do it like this:
ParameterExpression param1 = Expression.Parameter(typeof(TimeSlot));
Expression<Func<TimeSlot, bool>> expression = Expression.Lambda<Func<TimeSlot, bool>>(
Expression.Equal(
Expression.Invoke(left, param1),
Expression.Invoke(right, param1)),
param1);
Here's a complete code example that demonstrates the technique:
class Program
{
class A
{
public Guid Guid { get; private set; }
public A()
{
Guid = Guid.NewGuid();
}
}
static void Main(string[] args)
{
// Set up data values
A a = new A();
Guid guid = a.Guid;
// Create expressions to be composed
Expression<Func<A, Guid>> e1 = arg => arg.Guid, e2 = arg => guid;
// Create lambda expression: invoke both expressions, compare the result
ParameterExpression param1 = Expression.Parameter(typeof(A));
Expression<Func<A, bool>> e3 = Expression.Lambda<Func<A, bool>>(
Expression.Equal(
Expression.Invoke(e1, param1),
Expression.Invoke(e2, param1)),
param1);
// Compile to an actual delegate instance
Func<A, bool> d1 = e3.Compile();
// Check the result
Console.WriteLine(d1(a));
}
}
Upvotes: 2