如何使用参数序列化方法调用表达式?

时间:2016-04-26 09:45:59

标签: c# json reflection expression-trees

我打电话给远程服务,其描述如下:

var user = new User { Name = "check" };
WcfService<IMyService>.Call(s => s.MyMethod(1, "param", user, new Entity { ID = 2 }));

在我的Call方法中,我需要将此方法调用序列化为JSON,该方法将放入WebSphere队列中:

{
    "Interface": "IMyService",
    "Method": "MyMethod",
    "Arguments": [
        1,
        "param",
        {
            "Name": "check"
        },
        {
            "ID": 2
        }
    ]
}

我知道如何获取接口和方法名称,但我无法获得非常量值:

public static class WcfService<TInterface>
{
    public static void Call(Expression<Action<TInterface>> expr)
    {
        var mce = (MethodCallExpression)expr.Body;

        string interfaceName = typeof(TInterface).Name;
        string methodName = mce.Method.Name;

        var args = mce.Arguments
            .Cast<ConstantExpression>()
            .Select(e => e.Value)
            .ToArray();
    }
}

此代码适用于1"param",但不适用于usernew Entity { ID = 2 }),因为它们分别为FieldExpressionNewExpression 。如何获取实际值,传递给函数调用,而不是表达式表示?

更新: suggested duplicate question的答案不合适,因为我不想编译我的表达式并执行它 - 我只需要评估参数。

2 个答案:

答案 0 :(得分:1)

FieldExpressionMember(字段)和Expression,在这种情况下是ConstantExpression,其中包含包含该字段的对象。这里,常量表达式的Value是一个捕获本地user变量的匿名对象。将Member投射到FieldInfo允许您在其上调用GetValue,并通过传递该常量表达式的Value,您将获得User个对象正在寻找。

NewExpression具有ConstructorArguments属性(表达式列表),但没有值,因为只有在实际调用该函数时才会生成新对象。您可以编译该表达式,调用它并序列化结果,或者您可以序列化构造函数调用本身 - 类似于您序列化该服务调用的方式。

请注意,{ "Name": "check" }{ "ID": 2 }缺少类型信息。

答案 1 :(得分:1)

我结合了this answer和Pieter Witvoet的答案中的信息,并收到了以下功能:

public static object Evaluate(Expression expr)
{
    switch (expr.NodeType)
    {
        case ExpressionType.Constant:
            return ((ConstantExpression)expr).Value;
        case ExpressionType.MemberAccess:
            var me = (MemberExpression)expr;
            object target = Evaluate(me.Expression);
            switch (me.Member.MemberType)
            {
                case MemberTypes.Field:
                    return ((FieldInfo)me.Member).GetValue(target);
                case MemberTypes.Property:
                    return ((PropertyInfo)me.Member).GetValue(target, null);
                default:
                    throw new NotSupportedException(me.Member.MemberType.ToString());
            }
        case ExpressionType.New:
            return ((NewExpression)expr).Constructor
                .Invoke(((NewExpression)expr).Arguments.Select(Evaluate).ToArray());
        default:
            throw new NotSupportedException(expr.NodeType.ToString());
    }
}

现在,我可以简单地执行以下操作:

var args = mce.Arguments.Select(ExpressionEvaluator.Evaluate).ToArray();