Search code examples
c#lambdaexpression

Wrap Expression in Expression.TryCatch


I have some Expression<Func<T, bool>> exp and I need to write a method that would return new expression that represents the original one wrapped in try catch. For example, if original expression looks something like order => order.Product.Name == "Product 1" but order has no product, I need to return false instead of throwing exception;

Here is my method at the moment, but it throws runtime exception saying 'Expression of type 'System.Func2[MyClass,System.Boolean]' cannot be used for return type 'System.Boolean'`'

private static Expression<Func<T, bool>> FalseOnException<T>(this Expression<Func<T, bool>> exp)
{
    Expression<Func<T, bool>> falseExp = parameter => false;
    ParameterExpression parameter = exp.Parameters[0];
    var tryExp = Expression.TryCatch(exp, Expression.Catch(typeof(NullReferenceException), falseExp));
    return Expression.Lambda<Func<T, bool>>(tryExp.Body, parameter);
}

Where MyClass is T


Solution

  • We can adapt the example from the docs a little and it will work:

    Expression<Func<T, bool>> FalseOnNullReferenceException<T>(Expression<Func<T, bool>> exp) {
    
        var parameterExpression = Expression.Parameter(typeof(T));
        TryExpression tryCatchExpr =
            Expression.TryCatch(
                    Expression.Invoke(exp, parameterExpression)
                ,
                Expression.Catch(
                    typeof(NullReferenceException),
                    Expression.Constant(false, typeof(bool))
                )
            );
        return Expression.Lambda<Func<T, bool>>(tryCatchExpr, parameterExpression);;
    
    }
    

    Quick test:

    class Foo {
        public string Bar { get; set; }
    }
    
    Expression<Func<Foo, bool>> exp = f => f.Bar == "Baz";
    
    //exp.Compile()(null); // throws
    
    var tryCatchExp = FalseOnNullReferenceException(exp);
    
    var compiled = tryCatchExp.Compile();
    var foo = new Foo { Bar = "Baz" };
    Console.WriteLine(compiled(null)); // false
    Console.WriteLine(compiled(foo)); // true