I have the following Expression:
.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))
}
and want to rewrite it to the following:
.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
}
}
I tried with the Expression Visitor and following Code:
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);
}
but I got this Exception:
A first chance exception of type 'System.ArgumentException' occurred in System.Core.dll
Additional Informations: Expression of type 'DynamicClass1' cannot be used for return type 'System.Linq.Dynamic.DynamicObjectClass'
I think I need to overwrite VisitLambda, but I have no exact Idea! Can anyone help me out?
With this Code it now works at the moment (but I don't think it works for all cases...)
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);
}
}
}
}