重写表达式树

时间:2014-07-28 19:58:46

标签: c# linq expression expressionvisitor

我有以下表达式:

.Call System.Linq.Queryable.Select(
    .Constant<System.Linq.EnumerableQuery`1[System.Linq.Dynamic.Tests.Helpers.User]>(System.Linq.Dynamic.Tests.Helpers.User[]),
    '(.Lambda #Lambda1<System.Func`2[System.Linq.Dynamic.Tests.Helpers.User,System.Linq.Dynamic.DynamicObjectClass]>))

.Lambda #Lambda1<System.Func`2[System.Linq.Dynamic.Tests.Helpers.User,System.Linq.Dynamic.DynamicObjectClass]>(System.Linq.Dynamic.Tests.Helpers.User $var1)
{
    .New System.Linq.Dynamic.DynamicObjectClass(
        .New System.Collections.Generic.KeyValuePair`2[System.String, System.Object](
            "UserName",
            (System.Object)$var1.UserName),
        .New System.Collections.Generic.KeyValuePair`2[System.String, System.Object](
            "MyFirstName",
            (System.Object)($var1.Profile).FirstName))
}

并希望将其重写为以下内容:

.Call System.Linq.Queryable.Select(
    .Constant<System.Linq.EnumerableQuery`1[System.Linq.Dynamic.Tests.Helpers.User]>(System.Linq.Dynamic.Tests.Helpers.User[]),
    '(.Lambda #Lambda1<System.Func`2[System.Linq.Dynamic.Tests.Helpers.User,DynamicClass1]>))

.Lambda #Lambda1<System.Func`2[System.Linq.Dynamic.Tests.Helpers.User,DynamicClass1]>(System.Linq.Dynamic.Tests.Helpers.User $var1)
{
    .New DynamicClass1()
{
    UserName = $var1.UserName,
        MyFirstName = ($var1.Profile).FirstName
    }
}

我尝试使用表达式访问者并遵循代码:

        protected override Expression VisitNew(NewExpression node)
        {
            if (node.Type == typeof(DynamicObjectClass))
            {
                var properties = new List<DynamicProperty>(node.Arguments.Count);
                var expressions = new List<Expression>(node.Arguments.Count);
                foreach (NewExpression newEx in node.Arguments)
                {
                    var name = ((ConstantExpression)newEx.Arguments.First()).Value as string;
                    var parameter = ((UnaryExpression)newEx.Arguments.Skip(1).First()).Operand;
                    properties.Add(new DynamicProperty(name, parameter.Type));
                    expressions.Add(parameter);
                }

                Type type = DynamicExpression.CreateClass(properties);

                MemberBinding[] bindings = new MemberBinding[properties.Count];
                for (int i = 0; i < bindings.Length; i++)
                    bindings[i] = Expression.Bind(type.GetProperty(properties[i].Name), expressions[i]);
                return Expression.MemberInit(Expression.New(type), bindings);
            }
            return base.VisitNew(node);
        }

但我得到了这个例外:

  

类型&#39; System.ArgumentException&#39;的第一次机会异常。发生了   在System.Core.dll

中      

其他信息:表达类型&#39; DynamicClass1&#39;不可能是   用于返回类型&#39; System.Linq.Dynamic.DynamicObjectClass&#39;

我想我需要覆盖VisitLambda,但我没有确切的想法!任何人都可以帮助我吗?

1 个答案:

答案 0 :(得分:1)

使用此代码,它现在可以正常运行 (但我不认为它适用于所有情况......)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;

namespace System.Linq.Dynamic
{
    public static class ExpressionConverter
    {
        private static ExpressionConverterVisitor visitor = new ExpressionConverterVisitor();

        public static Expression DynamicObjectClassToAnonymousType(this Expression expression)
        {
            return visitor.Visit(expression);
        }

        private class ExpressionConverterVisitor : ExpressionVisitor
        {
            protected override Expression VisitLambda<T>(Expression<T> node)
            {
                if (node.Body is NewExpression && ((NewExpression)node.Body).Type == typeof(DynamicObjectClass))
                {
                    var e = node.Body as NewExpression;

                    var properties = new List<DynamicProperty>(e.Arguments.Count);
                    var expressions = new List<Expression>(e.Arguments.Count);
                    foreach (NewExpression newEx in e.Arguments)
                    {
                        var name = ((ConstantExpression)newEx.Arguments.First()).Value as string;
                        var parameter = ((UnaryExpression)newEx.Arguments.Skip(1).First()).Operand;
                        properties.Add(new DynamicProperty(name, parameter.Type));
                        expressions.Add(parameter);
                    }

                    Type type = DynamicExpression.CreateClass(properties);

                    MemberBinding[] bindings = new MemberBinding[properties.Count];
                    for (int i = 0; i < bindings.Length; i++)
                        bindings[i] = Expression.Bind(type.GetProperty(properties[i].Name), expressions[i]);
                    var membInit = Expression.MemberInit(Expression.New(type), bindings);

                    var typeOfTArgs = typeof(T).GetGenericArguments();

                    var funcTType = typeof(Func<,>).MakeGenericType(new[] { typeOfTArgs.First(), type });
                    var mi = typeof(Expression).GetMethods().FirstOrDefault(x => x.Name == "Lambda" && x.ContainsGenericParameters);
                    MethodInfo genericMethod = mi.MakeGenericMethod(new[] { funcTType });
                    var lambda = genericMethod.Invoke(null, new object[] { membInit, node.Parameters.ToArray() }) as Expression;
                    return lambda;
                }
                return base.VisitLambda<T>(node);
            }

            protected override Expression VisitMethodCall(MethodCallExpression node)
            {
                if (node.Method.Name == "Select")
                {
                    var arguments = node.Arguments.ToList();
                    for (int n = 0; n < arguments.Count; n++)
                        arguments[n] = visitor.Visit(arguments[n]);
                    var typeList = arguments.Select(x => x.Type).ToArray();
                    var funcTType = typeof(Func<,>).MakeGenericType(typeList);

                    var argsmth = node.Method.GetGenericArguments().ToArray();
                    argsmth[1] = ((LambdaExpression)((UnaryExpression)arguments[1]).Operand).Body.Type;
                    var mi = node.Method.DeclaringType.GetMethods().FirstOrDefault(x => x.Name == "Select");
                    var mth = mi.MakeGenericMethod(argsmth);

                    return Expression.Call(mth, arguments);
                }
                return base.VisitMethodCall(node);
            }
        }
    }
}