Roy T.
Roy T.

Reputation: 9638

Expression to check if a property is equal to a constant

I'm trying to combine two working expressions.

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

Answers (1)

Peter Duniho
Peter Duniho

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

Related Questions