重写一个表达式树

本文关键字:一个 表达式 重写 | 更新日期: 2023-09-27 18:15:21

我有以下表达式:

.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);
        }

但是我得到了这个Exception:

类型为'System '的第一次异常。ArgumentException的发生在System.Core.dll

附加信息:类型'DynamicClass1'的表达式不能是用于返回类型'System.Linq.Dynamic.DynamicObjectClass'

我想我需要覆盖VisitLambda,但我没有确切的想法!有人能帮我吗?

重写一个表达式树

有了这段代码,它现在可以工作了(但我不认为它适用于所有情况…)

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);
            }
        }
    }
}