Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 26 additions & 13 deletions src/System.Linq.Dynamic.Core/DynamicGetMemberBinder.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#if !NET35 && !UAP10_0 && !NETSTANDARD1_3
using System.Collections;
using System.Collections.Generic;
using System.Dynamic;
using System.Linq.Expressions;
Expand All @@ -7,31 +8,43 @@

namespace System.Linq.Dynamic.Core
{
/// <summary>
/// Based on SqlLinq by dkackman. https://github.com/dkackman/SqlLinq/blob/210b594e37f14061424397368ed750ce547c21e7/License.md
/// </summary>
/// <seealso cref="GetMemberBinder" />
internal class DynamicGetMemberBinder : GetMemberBinder
{
private static readonly Type IDictionaryType = typeof(IDictionary<string, object>);
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))
{
}

public override DynamicMetaObject FallbackGetMember(DynamicMetaObject target, DynamicMetaObject errorSuggestion)
{
var dictionary = target.Value as IDictionary<string, object>;
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<string, object> 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);
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions test/System.Linq.Dynamic.Core.Tests/ExpressionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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" };
Comment thread
StefH marked this conversation as resolved.
var list = new List<dynamic> { a1, a2 };
IQueryable qry = list.AsQueryable();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down