diff --git a/src/System.Linq.Dynamic.Core/DynamicGetMemberBinder.cs b/src/System.Linq.Dynamic.Core/DynamicGetMemberBinder.cs index 93adad1d..46be79a9 100644 --- a/src/System.Linq.Dynamic.Core/DynamicGetMemberBinder.cs +++ b/src/System.Linq.Dynamic.Core/DynamicGetMemberBinder.cs @@ -1,4 +1,5 @@ #if !NET35 && !UAP10_0 && !NETSTANDARD1_3 +using System.Collections; using System.Collections.Generic; using System.Dynamic; using System.Linq.Expressions; @@ -7,14 +8,10 @@ namespace System.Linq.Dynamic.Core { - /// - /// Based on SqlLinq by dkackman. https://github.com/dkackman/SqlLinq/blob/210b594e37f14061424397368ed750ce547c21e7/License.md - /// /// internal class DynamicGetMemberBinder : GetMemberBinder { - private static readonly Type IDictionaryType = typeof(IDictionary); - private static readonly PropertyInfo Indexer = IDictionaryType.GetProperty("Item"); + private static readonly MethodInfo DynamicGetMemberMethod = typeof(DynamicGetMemberBinder).GetMethod(nameof(GetDynamicMember)); public DynamicGetMemberBinder(string name, [CanBeNull] ParsingConfig config) : base(name, !(config?.IsCaseSensitive == true)) { @@ -22,16 +19,32 @@ public DynamicGetMemberBinder(string name, [CanBeNull] ParsingConfig config) : b public override DynamicMetaObject FallbackGetMember(DynamicMetaObject target, DynamicMetaObject errorSuggestion) { - var dictionary = target.Value as IDictionary; - if (dictionary == null) - { - throw new InvalidOperationException("Target object is not an ExpandoObject"); - } + var instance = Expression.Call( + DynamicGetMemberMethod, + target.Expression, + Expression.Constant(Name), + Expression.Constant(IgnoreCase)); + return DynamicMetaObject.Create(target.Value, instance); + } + + public static object GetDynamicMember(object value, string name, bool ignoreCase) + { + if (value == null) + throw new InvalidOperationException(); + + if (value is IDictionary dict1) + return dict1[name]; + + if (value is IDictionary dict2) + return dict2[name]; - var instance = Expression.ConvertChecked(target.Expression, IDictionaryType); - var indexExpression = Expression.MakeIndex(instance, Indexer, new Expression[] { Expression.Constant(Name) }); + var flags = BindingFlags.Instance | BindingFlags.Public; + if (ignoreCase) flags |= BindingFlags.IgnoreCase; + var property = value.GetType().GetProperty(name, flags); + if (property == null) + throw new InvalidOperationException(); - return DynamicMetaObject.Create(dictionary, indexExpression); + return property.GetValue(value, null); } } } diff --git a/test/System.Linq.Dynamic.Core.Tests/ExpressionTests.cs b/test/System.Linq.Dynamic.Core.Tests/ExpressionTests.cs index 2f8940f0..9173dfe8 100644 --- a/test/System.Linq.Dynamic.Core.Tests/ExpressionTests.cs +++ b/test/System.Linq.Dynamic.Core.Tests/ExpressionTests.cs @@ -1610,12 +1610,12 @@ public void ExpressionTests_OrderBy() Check.That(qry.OrderBy(x => x.Id).ThenByDescending(x => x.Profile.Age).ToArray()).ContainsExactly(orderBy.ToArray()); } - //[Fact] + [Fact] public void ExpressionTests_Select_DynamicObjects() { // Arrange dynamic a1 = new { Name = "a", BlogId = 100 }; - dynamic a2 = new { Name = "b", BlogId = 200 }; + dynamic a2 = new { BlogId = 200, Name = "b" }; var list = new List { a1, a2 }; IQueryable qry = list.AsQueryable(); diff --git a/test/System.Linq.Dynamic.Core.Tests/QueryableTests.Where.cs b/test/System.Linq.Dynamic.Core.Tests/QueryableTests.Where.cs index f1d7dca3..9369f92d 100644 --- a/test/System.Linq.Dynamic.Core.Tests/QueryableTests.Where.cs +++ b/test/System.Linq.Dynamic.Core.Tests/QueryableTests.Where.cs @@ -246,7 +246,7 @@ public void Where_Dynamic_Object_As_Dictionary() results.Should().HaveCount(1); } - [Fact(Skip = "Unable to cast object of type '<>f__AnonymousType35`1[System.String]' to type 'System.Collections.Generic.IDictionary`2[System.String,System.Object]'")] + [Fact] public void Where_Dynamic_ExpandoObject_As_AnonymousType() { // Arrange