Skip to content

Commit e2dbfc7

Browse files
committed
A ExpressionConverter to Convert a Expression Recieved from WinRT (with a DynamicObjectClass) to a Expression for .NET (with a Anonymous Type)
1 parent 87c6a0b commit e2dbfc7

File tree

7 files changed

+156
-31
lines changed

7 files changed

+156
-31
lines changed

Src/System.Linq.Dynamic.Tests/ExpressionTests.cs

+22-2
Original file line numberDiff line numberDiff line change
@@ -199,8 +199,28 @@ public void ExpressionTests_DistinctBy()
199199
Assert.AreEqual(qry1.Count(), 2);
200200
Assert.AreEqual(qry2.Count(), 2);
201201
Assert.AreEqual(qry3.Count(), 2);
202-
}
203-
202+
}
203+
204+
#if !SILVERLIGHT && !NETFX_CORE && !NET35
205+
[TestMethod]
206+
public void ExpressionTests_Convert()
207+
{
208+
List<int> range = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
209+
var testList = User.GenerateSampleModels(100);
210+
var qry = testList.AsQueryable();
211+
212+
var userFirstName = qry.Select("new (UserName, Profile.FirstName as MyFirstName)");
213+
GlobalConfig.UseDynamicObjectClassForAnonymousTypes = true;
214+
var userFirstName2 = qry.Select("new (UserName, Profile.FirstName as MyFirstName)");
215+
216+
var exp = ExpressionConverter.DynamicObjectClassToAnonymousType(userFirstName2.Expression);
217+
var userFirstName3 = System.Linq.Expressions.Expression.Lambda(exp).Compile().DynamicInvoke();
218+
219+
Assert.AreEqual(userFirstName.Expression.ToString(), exp.ToString());
220+
Assert.AreNotEqual(userFirstName2.Expression.ToString(), exp.ToString());
221+
}
222+
#endif
223+
204224
[TestMethod]
205225
public void ExpressionTests_ContextKeywordsAndSymbols()
206226
{

Src/System.Linq.Dynamic.WinRT/System.Linq.Dynamic.WinRT.csproj

+3-1
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,9 @@
110110
<Compile Include="..\System.Linq.Dynamic\DynamicLinqTypeProvider.cs">
111111
<Link>DynamicLinqTypeProvider.cs</Link>
112112
</Compile>
113+
<Compile Include="..\System.Linq.Dynamic\DynamicObjectClass.cs">
114+
<Link>DynamicObjectClass.cs</Link>
115+
</Compile>
113116
<Compile Include="..\System.Linq.Dynamic\DynamicOrdering.cs">
114117
<Link>DynamicOrdering.cs</Link>
115118
</Compile>
@@ -141,7 +144,6 @@
141144
<Link>Res.cs</Link>
142145
</Compile>
143146
<Compile Include="AppDomain.cs" />
144-
<Compile Include="DynamicClass.cs" />
145147
<Compile Include="ExtensionMethods.cs" />
146148
<Compile Include="Properties\AssemblyInfo.cs" />
147149
</ItemGroup>

Src/System.Linq.Dynamic.WinRT/DynamicClass.cs Src/System.Linq.Dynamic/DynamicObjectClass.cs

+12-13
Original file line numberDiff line numberDiff line change
@@ -8,40 +8,39 @@
88
namespace System.Linq.Dynamic
99
{
1010
/// <summary>
11-
/// Provides a base class for dynamic objects created by using the <see cref="DynamicQueryable.Select(IQueryable,string,object[])"/>
12-
/// method. For internal use only.
11+
/// A DynamicClass as Replacement for creating classes via Reflection Emit, wich is not supported in WinRT
1312
/// </summary>
14-
public class DynamicClass : DynamicObject
13+
public class DynamicObjectClass : DynamicObject
1514
{
1615
Dictionary<string, object> _properties = new Dictionary<string, object>();
1716

18-
public DynamicClass(KeyValuePair<string, object> _1)
17+
public DynamicObjectClass(KeyValuePair<string, object> _1)
1918
{
2019
_properties.Add(_1.Key, _1.Value);
2120
}
2221

23-
public DynamicClass(KeyValuePair<string, object> _1, KeyValuePair<string, object> _2)
22+
public DynamicObjectClass(KeyValuePair<string, object> _1, KeyValuePair<string, object> _2)
2423
{
2524
_properties.Add(_1.Key, _1.Value);
2625
_properties.Add(_2.Key, _2.Value);
2726
}
2827

29-
public DynamicClass(KeyValuePair<string, object> _1, KeyValuePair<string, object> _2, KeyValuePair<string, object> _3)
28+
public DynamicObjectClass(KeyValuePair<string, object> _1, KeyValuePair<string, object> _2, KeyValuePair<string, object> _3)
3029
{
3130
_properties.Add(_1.Key, _1.Value);
3231
_properties.Add(_2.Key, _2.Value);
3332
_properties.Add(_3.Key, _3.Value);
3433
}
3534

36-
public DynamicClass(KeyValuePair<string, object> _1, KeyValuePair<string, object> _2, KeyValuePair<string, object> _3, KeyValuePair<string, object> _4)
35+
public DynamicObjectClass(KeyValuePair<string, object> _1, KeyValuePair<string, object> _2, KeyValuePair<string, object> _3, KeyValuePair<string, object> _4)
3736
{
3837
_properties.Add(_1.Key, _1.Value);
3938
_properties.Add(_2.Key, _2.Value);
4039
_properties.Add(_3.Key, _3.Value);
4140
_properties.Add(_4.Key, _4.Value);
4241
}
4342

44-
public DynamicClass(KeyValuePair<string, object> _1, KeyValuePair<string, object> _2, KeyValuePair<string, object> _3, KeyValuePair<string, object> _4, KeyValuePair<string, object> _5)
43+
public DynamicObjectClass(KeyValuePair<string, object> _1, KeyValuePair<string, object> _2, KeyValuePair<string, object> _3, KeyValuePair<string, object> _4, KeyValuePair<string, object> _5)
4544
{
4645
_properties.Add(_1.Key, _1.Value);
4746
_properties.Add(_2.Key, _2.Value);
@@ -50,7 +49,7 @@ public DynamicClass(KeyValuePair<string, object> _1, KeyValuePair<string, object
5049
_properties.Add(_5.Key, _5.Value);
5150
}
5251

53-
public DynamicClass(KeyValuePair<string, object> _1, KeyValuePair<string, object> _2, KeyValuePair<string, object> _3, KeyValuePair<string, object> _4, KeyValuePair<string, object> _5, KeyValuePair<string, object> _6)
52+
public DynamicObjectClass(KeyValuePair<string, object> _1, KeyValuePair<string, object> _2, KeyValuePair<string, object> _3, KeyValuePair<string, object> _4, KeyValuePair<string, object> _5, KeyValuePair<string, object> _6)
5453
{
5554
_properties.Add(_1.Key, _1.Value);
5655
_properties.Add(_2.Key, _2.Value);
@@ -60,7 +59,7 @@ public DynamicClass(KeyValuePair<string, object> _1, KeyValuePair<string, object
6059
_properties.Add(_6.Key, _6.Value);
6160
}
6261

63-
public DynamicClass(KeyValuePair<string, object> _1, KeyValuePair<string, object> _2, KeyValuePair<string, object> _3, KeyValuePair<string, object> _4, KeyValuePair<string, object> _5, KeyValuePair<string, object> _6, KeyValuePair<string, object> _7)
62+
public DynamicObjectClass(KeyValuePair<string, object> _1, KeyValuePair<string, object> _2, KeyValuePair<string, object> _3, KeyValuePair<string, object> _4, KeyValuePair<string, object> _5, KeyValuePair<string, object> _6, KeyValuePair<string, object> _7)
6463
{
6564
_properties.Add(_1.Key, _1.Value);
6665
_properties.Add(_2.Key, _2.Value);
@@ -71,7 +70,7 @@ public DynamicClass(KeyValuePair<string, object> _1, KeyValuePair<string, object
7170
_properties.Add(_7.Key, _7.Value);
7271
}
7372

74-
public DynamicClass(KeyValuePair<string, object> _1, KeyValuePair<string, object> _2, KeyValuePair<string, object> _3, KeyValuePair<string, object> _4, KeyValuePair<string, object> _5, KeyValuePair<string, object> _6, KeyValuePair<string, object> _7, KeyValuePair<string, object> _8)
73+
public DynamicObjectClass(KeyValuePair<string, object> _1, KeyValuePair<string, object> _2, KeyValuePair<string, object> _3, KeyValuePair<string, object> _4, KeyValuePair<string, object> _5, KeyValuePair<string, object> _6, KeyValuePair<string, object> _7, KeyValuePair<string, object> _8)
7574
{
7675
_properties.Add(_1.Key, _1.Value);
7776
_properties.Add(_2.Key, _2.Value);
@@ -83,7 +82,7 @@ public DynamicClass(KeyValuePair<string, object> _1, KeyValuePair<string, object
8382
_properties.Add(_8.Key, _8.Value);
8483
}
8584

86-
public DynamicClass(KeyValuePair<string, object> _1, KeyValuePair<string, object> _2, KeyValuePair<string, object> _3, KeyValuePair<string, object> _4, KeyValuePair<string, object> _5, KeyValuePair<string, object> _6, KeyValuePair<string, object> _7, KeyValuePair<string, object> _8, KeyValuePair<string, object> _9)
85+
public DynamicObjectClass(KeyValuePair<string, object> _1, KeyValuePair<string, object> _2, KeyValuePair<string, object> _3, KeyValuePair<string, object> _4, KeyValuePair<string, object> _5, KeyValuePair<string, object> _6, KeyValuePair<string, object> _7, KeyValuePair<string, object> _8, KeyValuePair<string, object> _9)
8786
{
8887
_properties.Add(_1.Key, _1.Value);
8988
_properties.Add(_2.Key, _2.Value);
@@ -159,7 +158,7 @@ public override bool Equals(object obj)
159158
{
160159
if (object.ReferenceEquals(this, obj)) return true;
161160

162-
var other = obj as DynamicClass;
161+
var other = obj as DynamicObjectClass;
163162

164163
if (other == null) return false;
165164

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Linq.Expressions;
5+
using System.Reflection;
6+
using System.Text;
7+
8+
namespace System.Linq.Dynamic
9+
{
10+
public static class ExpressionConverter
11+
{
12+
private static ExpressionConverterVisitor visitor = new ExpressionConverterVisitor();
13+
14+
public static Expression DynamicObjectClassToAnonymousType(this Expression expression)
15+
{
16+
return visitor.Visit(expression);
17+
}
18+
19+
private class ExpressionConverterVisitor : ExpressionVisitor
20+
{
21+
protected override Expression VisitLambda<T>(Expression<T> node)
22+
{
23+
if (node.Body is NewExpression && ((NewExpression)node.Body).Type == typeof(DynamicObjectClass))
24+
{
25+
var e = node.Body as NewExpression;
26+
27+
var properties = new List<DynamicProperty>(e.Arguments.Count);
28+
var expressions = new List<Expression>(e.Arguments.Count);
29+
foreach (NewExpression newEx in e.Arguments)
30+
{
31+
var name = ((ConstantExpression)newEx.Arguments.First()).Value as string;
32+
var parameter = ((UnaryExpression)newEx.Arguments.Skip(1).First()).Operand;
33+
properties.Add(new DynamicProperty(name, parameter.Type));
34+
expressions.Add(parameter);
35+
}
36+
37+
Type type = DynamicExpression.CreateClass(properties);
38+
39+
MemberBinding[] bindings = new MemberBinding[properties.Count];
40+
for (int i = 0; i < bindings.Length; i++)
41+
bindings[i] = Expression.Bind(type.GetProperty(properties[i].Name), expressions[i]);
42+
var membInit = Expression.MemberInit(Expression.New(type), bindings);
43+
44+
var typeOfTArgs = typeof(T).GetGenericArguments();
45+
46+
var funcTType = typeof(Func<,>).MakeGenericType(new[] { typeOfTArgs.First(), type });
47+
var mi = typeof(Expression).GetMethods().FirstOrDefault(x => x.Name == "Lambda" && x.ContainsGenericParameters);
48+
MethodInfo genericMethod = mi.MakeGenericMethod(new[] { funcTType });
49+
var lambda = genericMethod.Invoke(null, new object[] { membInit, node.Parameters.ToArray() }) as Expression;
50+
return lambda;
51+
}
52+
return base.VisitLambda<T>(node);
53+
}
54+
55+
protected override Expression VisitMethodCall(MethodCallExpression node)
56+
{
57+
if (node.Method.Name == "Select")
58+
{
59+
var arguments = node.Arguments.ToList();
60+
for (int n = 0; n < arguments.Count; n++)
61+
arguments[n] = visitor.Visit(arguments[n]);
62+
var typeList = arguments.Select(x => x.Type).ToArray();
63+
var funcTType = typeof(Func<,>).MakeGenericType(typeList);
64+
65+
var argsmth = node.Method.GetGenericArguments().ToArray();
66+
argsmth[1] = ((LambdaExpression)((UnaryExpression)arguments[1]).Operand).Body.Type;
67+
var mi = node.Method.DeclaringType.GetMethods().FirstOrDefault(x => x.Name == "Select");
68+
var mth = mi.MakeGenericMethod(argsmth);
69+
70+
return Expression.Call(mth, arguments);
71+
}
72+
return base.VisitMethodCall(node);
73+
}
74+
}
75+
}
76+
}

Src/System.Linq.Dynamic/ExpressionParser.cs

+36-15
Original file line numberDiff line numberDiff line change
@@ -983,26 +983,47 @@ Expression ParseNew()
983983
ValidateToken(TokenId.CloseParen, Res.CloseParenOrCommaExpected);
984984
NextToken();
985985

986-
#if !NETFX_CORE
987-
Type type = DynamicExpression.CreateClass(properties);
988-
989-
MemberBinding[] bindings = new MemberBinding[properties.Count];
990-
for (int i = 0; i < bindings.Length; i++)
991-
bindings[i] = Expression.Bind(type.GetProperty(properties[i].Name), expressions[i]);
992-
return Expression.MemberInit(Expression.New(type), bindings);
993-
#else
994-
Type type = typeof(DynamicClass);
986+
987+
#if NETFX_CORE
988+
Type ttype = typeof(DynamicObjectClass);
995989
List<Expression> paras = new List<Expression>();
996990
var constructorKeyValuePair = typeof(KeyValuePair<string, object>).GetTypeInfo().DeclaredConstructors.First();
997991
for (int i = 0; i < properties.Count; i++)
998992
{
999993
var item = Expression.New(constructorKeyValuePair, new[] { (Expression)Expression.Constant(properties[i].Name), Expression.Convert(expressions[i], typeof(object)) });
1000994
paras.Add(item);
1001995
}
1002-
var constructor = type.GetTypeInfo().DeclaredConstructors.First(x => x.GetParameters().Count() == properties.Count());
1003-
var instance = Expression.New(constructor, paras);
996+
var constructor = ttype.GetTypeInfo().DeclaredConstructors.First(x => x.GetParameters().Count() == properties.Count());
1004997

1005-
return instance;
998+
return Expression.New(constructor, paras);
999+
#elif NET35 || SILVERLIGHT
1000+
Type type = DynamicExpression.CreateClass(properties);
1001+
1002+
MemberBinding[] bindings = new MemberBinding[properties.Count];
1003+
for (int i = 0; i < bindings.Length; i++)
1004+
bindings[i] = Expression.Bind(type.GetProperty(properties[i].Name), expressions[i]);
1005+
return Expression.MemberInit(Expression.New(type), bindings);
1006+
#else
1007+
if (GlobalConfig.UseDynamicObjectClassForAnonymousTypes)
1008+
{
1009+
Type ttype = typeof(DynamicObjectClass);
1010+
List<Expression> paras = new List<Expression>();
1011+
var constructorKeyValuePair = typeof(KeyValuePair<string, object>).GetConstructors().First();
1012+
for (int i = 0; i < properties.Count; i++)
1013+
{
1014+
var item = Expression.New(constructorKeyValuePair, new[] { (Expression)Expression.Constant(properties[i].Name), Expression.Convert(expressions[i], typeof(object)) });
1015+
paras.Add(item);
1016+
}
1017+
var constructor = ttype.GetConstructors().First(x => x.GetParameters().Count() == properties.Count());
1018+
1019+
return Expression.New(constructor, paras);
1020+
}
1021+
Type type = DynamicExpression.CreateClass(properties);
1022+
1023+
MemberBinding[] bindings = new MemberBinding[properties.Count];
1024+
for (int i = 0; i < bindings.Length; i++)
1025+
bindings[i] = Expression.Bind(type.GetProperty(properties[i].Name), expressions[i]);
1026+
return Expression.MemberInit(Expression.New(type), bindings);
10061027
#endif
10071028
}
10081029

@@ -1137,9 +1158,9 @@ Expression ParseMemberAccess(Type type, Expression instance)
11371158
}
11381159

11391160
#if NETFX_CORE
1140-
if (type == typeof(DynamicClass))
1161+
if (type == typeof(DynamicObjectClass))
11411162
{
1142-
return Expression.MakeIndex(instance, typeof(DynamicClass).GetProperty("Item"), new[] { Expression.Constant(id) });
1163+
return Expression.MakeIndex(instance, typeof(DynamicObjectClass).GetProperty("Item"), new[] { Expression.Constant(id) });
11431164
}
11441165
#endif
11451166
MemberInfo member = FindPropertyOrField(type, id, instance == null);
@@ -1343,7 +1364,7 @@ static bool TryGetMemberName(Expression expression, out string memberName)
13431364

13441365
#if NETFX_CORE
13451366
var indexExpression = expression as IndexExpression;
1346-
if (indexExpression != null && indexExpression.Indexer.DeclaringType == typeof(DynamicClass))
1367+
if (indexExpression != null && indexExpression.Indexer.DeclaringType == typeof(DynamicObjectClass))
13471368
{
13481369
memberName = ((ConstantExpression)indexExpression.Arguments.First()).Value as string;
13491370
return true;

Src/System.Linq.Dynamic/GlobalConfig.cs

+5
Original file line numberDiff line numberDiff line change
@@ -53,5 +53,10 @@ public static bool AreContextKeywordsEnabled
5353
}
5454
}
5555
}
56+
57+
/// <summary>
58+
///
59+
/// </summary>
60+
public static bool UseDynamicObjectClassForAnonymousTypes { get; set; }
5661
}
5762
}

Src/System.Linq.Dynamic/System.Linq.Dynamic.csproj

+2
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,11 @@
6060
<Compile Include="DynamicClass.cs" />
6161
<Compile Include="DynamicExpression.cs" />
6262
<Compile Include="DynamicLinqTypeProvider.cs" />
63+
<Compile Include="DynamicObjectClass.cs" />
6364
<Compile Include="DynamicQueryable.cs" />
6465
<Compile Include="DynamicOrdering.cs" />
6566
<Compile Include="DynamicProperty.cs" />
67+
<Compile Include="ExpressionConverter.cs" />
6668
<Compile Include="ExpressionParser.cs" />
6769
<Compile Include="GlobalConfig.cs" />
6870
<Compile Include="GlobalSuppressions.cs" />

0 commit comments

Comments
 (0)