Search code examples
c#lambdatostring

Expression to string with avoiding to print the closure full path


I have something like this:

private async Task<ProductDo> GetProductsAsync(int tariffId)
{
    var filter = new MyFilter
    {
         Expression = (x, _) => x.Id == tariffId || x.MarketingProduct.Id == tariffId,
         ...
    };
            
...

My goal is to be able to record the filter to logs and also pass it as an HTTP request. When I use ToString method of the expression, I see a weird closure or something representation like:

(x, _) => (x.Id == value(Services.InternetTariffService+<>c__DisplayClass3_0).tariffId)

But I would love to see:

(x, _) => (x.Id == 777)

Any idea how could I achieve this? Is it even possible?


Solution

  • This is how lambdas work (used in context of expressions trees or as anonymous functions). For tariffId to be captured closure will be created. You can try creating custom expression tree visitor which will substitute generated closure field value with constant (though there are some edge cases when the behaviour can be incorrect, for example if tariffId will be changed after logging), something along this lines to get you started:

    class SimpleConstantSubstitutingVisitor : ExpressionVisitor
    {
        protected override Expression VisitMember(MemberExpression node)
        {
            if (node is { Expression: ConstantExpression ce, Member: FieldInfo fe }) // maybe check for PropertyInfo too?
            {
                return Expression.Constant(fe.GetValue(ce.Value));
            }
    
            return base.VisitMember(node);
        }
    }
    
    public class TestClass
    {
        public int TestProperty { get; set; }
    }
    
    int toBeClosed = 1;
    Expression<Func<TestClass, bool>> testExpression = c => c.TestProperty == toBeClosed;
    var substituted = new SimpleConstantSubstitutingVisitor().Visit(testExpression);
    Console.WriteLine(substituted.ToString()); // prints "c => (c.TestProperty == 1)"
    

    P.S.

    This solution relies on current generation patterns which in theory can change and does not cover all possible cases.