以人类可读的格式显示表达式

Display an Expression in a human readable format

我正在尝试创建一个扩展方法,如果表达式不匹配,该方法将抛出错误

我有以下代码开始......

public static void Exists<TEntityType>(this IEnumerable<TEntityType> source, Expression<Func<TEntityType, bool>> input) where TEntityType : class
{
    if (input is null) throw new ArgumentNullException(nameof(input));

    var record = repository.Where(input);

    if (record == null)
    {
        // issue here ...

        throw new NotFoundException(typeof(TEntityType).FullName, string.Empty); // todo provide value
    }
}

我可以这样调用这段代码...

records.Exists(x => x.Description == methodParameter.PropertyName);

这会根据 属性 正确抛出 NotFoundException。不幸的是,这个错误对用户来说不是很友好,你很难说出关于这个问题的任何细节。我想做的是将表达式简化为更简单的术语。 为了实现这个目标,我尝试了以下方法。

var compileTarget = input.Compile().Target;

里面 compileTarget 是我正在寻找的一些值。在快速观看中它看起来像这样

((System.Runtime.CompilerServices.Closure)compileTarget).Constants[0]

我还可以在即时 Window、局部变量和变量检查器中看到值,但我似乎无法将这些值放入代码中。有什么方法可以解释这些值,以便将整个表达式转换为人类可读的代码吗?

如果 methodParameter.PropertyNametest 那么我会寻找类似 Description = testDescription is test?

的东西

这是一个 ExpressionVisitor 的开始,它用它们的值替换了引用常量的 MemberExpression,假设那些只出现在可评估的情况下(真的,真的未经测试)。

public static class ExpressionExt {
    public static Expression Simplify(this Expression e) => (new SimplifyVisitor()).Visit(e);
    public static string SimplifiedString(this LambdaExpression e) => e.Body.Simplify().ToString();
}

public class SimplifyVisitor : ExpressionVisitor {
    protected override Expression VisitMember(MemberExpression node) {
        if (node.Expression is ConstantExpression)
            return Expression.Constant(Expression.Lambda<Func<object>>(Expression.Convert(node, typeof(object))).Compile().Invoke(), node.Type);
        else
            return node;
    }
}

有了这些定义,你就可以使用

throw new NotFoundException(typeof(TEntityType).FullName, input.SimplifiedString());

注意:如果您愿意使用DynamicInvoke,您可以避免访问者中的强制转换:

return Expression.Constant(Expression.Lambda(node).Compile().DynamicInvoke(), node.Type);