Skip to content

Commit 689ba18

Browse files
authored
Implement support for anonymous types as dynamic objects (#484)
1 parent 5d740aa commit 689ba18

3 files changed

Lines changed: 29 additions & 16 deletions

File tree

src/System.Linq.Dynamic.Core/DynamicGetMemberBinder.cs

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#if !NET35 && !UAP10_0 && !NETSTANDARD1_3
2+
using System.Collections;
23
using System.Collections.Generic;
34
using System.Dynamic;
45
using System.Linq.Expressions;
@@ -7,31 +8,43 @@
78

89
namespace System.Linq.Dynamic.Core
910
{
10-
/// <summary>
11-
/// Based on SqlLinq by dkackman. https://github.com/dkackman/SqlLinq/blob/210b594e37f14061424397368ed750ce547c21e7/License.md
12-
/// </summary>
1311
/// <seealso cref="GetMemberBinder" />
1412
internal class DynamicGetMemberBinder : GetMemberBinder
1513
{
16-
private static readonly Type IDictionaryType = typeof(IDictionary<string, object>);
17-
private static readonly PropertyInfo Indexer = IDictionaryType.GetProperty("Item");
14+
private static readonly MethodInfo DynamicGetMemberMethod = typeof(DynamicGetMemberBinder).GetMethod(nameof(GetDynamicMember));
1815

1916
public DynamicGetMemberBinder(string name, [CanBeNull] ParsingConfig config) : base(name, !(config?.IsCaseSensitive == true))
2017
{
2118
}
2219

2320
public override DynamicMetaObject FallbackGetMember(DynamicMetaObject target, DynamicMetaObject errorSuggestion)
2421
{
25-
var dictionary = target.Value as IDictionary<string, object>;
26-
if (dictionary == null)
27-
{
28-
throw new InvalidOperationException("Target object is not an ExpandoObject");
29-
}
22+
var instance = Expression.Call(
23+
DynamicGetMemberMethod,
24+
target.Expression,
25+
Expression.Constant(Name),
26+
Expression.Constant(IgnoreCase));
27+
return DynamicMetaObject.Create(target.Value, instance);
28+
}
29+
30+
public static object GetDynamicMember(object value, string name, bool ignoreCase)
31+
{
32+
if (value == null)
33+
throw new InvalidOperationException();
34+
35+
if (value is IDictionary<string, object> dict1)
36+
return dict1[name];
37+
38+
if (value is IDictionary dict2)
39+
return dict2[name];
3040

31-
var instance = Expression.ConvertChecked(target.Expression, IDictionaryType);
32-
var indexExpression = Expression.MakeIndex(instance, Indexer, new Expression[] { Expression.Constant(Name) });
41+
var flags = BindingFlags.Instance | BindingFlags.Public;
42+
if (ignoreCase) flags |= BindingFlags.IgnoreCase;
43+
var property = value.GetType().GetProperty(name, flags);
44+
if (property == null)
45+
throw new InvalidOperationException();
3346

34-
return DynamicMetaObject.Create(dictionary, indexExpression);
47+
return property.GetValue(value, null);
3548
}
3649
}
3750
}

test/System.Linq.Dynamic.Core.Tests/ExpressionTests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1610,12 +1610,12 @@ public void ExpressionTests_OrderBy()
16101610
Check.That(qry.OrderBy(x => x.Id).ThenByDescending(x => x.Profile.Age).ToArray()).ContainsExactly(orderBy.ToArray());
16111611
}
16121612

1613-
//[Fact]
1613+
[Fact]
16141614
public void ExpressionTests_Select_DynamicObjects()
16151615
{
16161616
// Arrange
16171617
dynamic a1 = new { Name = "a", BlogId = 100 };
1618-
dynamic a2 = new { Name = "b", BlogId = 200 };
1618+
dynamic a2 = new { BlogId = 200, Name = "b" };
16191619
var list = new List<dynamic> { a1, a2 };
16201620
IQueryable qry = list.AsQueryable();
16211621

test/System.Linq.Dynamic.Core.Tests/QueryableTests.Where.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,7 @@ public void Where_Dynamic_Object_As_Dictionary()
246246
results.Should().HaveCount(1);
247247
}
248248

249-
[Fact(Skip = "Unable to cast object of type '<>f__AnonymousType35`1[System.String]' to type 'System.Collections.Generic.IDictionary`2[System.String,System.Object]'")]
249+
[Fact]
250250
public void Where_Dynamic_ExpandoObject_As_AnonymousType()
251251
{
252252
// Arrange

0 commit comments

Comments
 (0)