Search code examples
c#linqexpressionexpression-trees

Can i traverse a expression bodied member with an ExpressionVisitor?


I have the following data structure:

class OrderLine : Table
{
    public int Id { get; set; }

    public Order Order { get; set; }

    public decimal Quantity { get; set; }

    public decimal UnitPrice { get; set; }

    [CalculatedField]
    public decimal LinePrice {
        get => Quantity * LinePrice;
    }
}

I want to traverse the Expression of the LinePrice getter with an ExpressionVisitor. To build a request to a remote system.

Is there a way to (via reflection?) to access the expression bodied member getter's Expression?


Solution

  • You cannot traverse the Expression of expression-bodied properties, because they are not Expression objects. Expression-bodied properties are no different from other properties, except their syntax. Your property here:

    public decimal LinePrice {
        get => Quantity * LinePrice; // did you mean UnitPrice?
    }
    

    is compiled into: (as is seen on SharpLab)

    .method public hidebysig specialname 
        instance valuetype [System.Private.CoreLib]System.Decimal get_LinePrice () cil managed 
    {
        .maxstack 8
    
        IL_0000: ldarg.0
        IL_0001: call instance valuetype [System.Private.CoreLib]System.Decimal OrderLine::get_Quantity()
        IL_0006: ldarg.0
        IL_0007: call instance valuetype [System.Private.CoreLib]System.Decimal OrderLine::get_LinePrice()
        IL_000c: call valuetype [System.Private.CoreLib]System.Decimal [System.Private.CoreLib]System.Decimal::op_Multiply(valuetype [System.Private.CoreLib]System.Decimal, valuetype [System.Private.CoreLib]System.Decimal)
        IL_0011: ret
    }
    

    which is the same code that would be generated, if you used a block-bodied property. And as you can see, there's no Expressions anywhere. You can try this on SharpLab. This shows that expression-bodied members are purely syntactic sugar.

    If you want to traverse it as an Expression, you should actually declare an Expression:

    // now you can traverse this with ExpressionVisitor
    public static readonly Expression<Func<OrderLine, decimal>> LinePriceExpression 
        = x => x.Quantity * x.UnitPrice;
    
    // to avoid repeating "Quantity * UnitPrice" again in the property getter,
    // you can compile the expression and reuse it
    private static readonly Func<OrderLine, decimal> LinePriceExpressionCompiled 
        = LinePriceExpression.Compile();
    
    public decimal LinePrice => LinePriceExpressionCompiled(this);