-
-
Notifications
You must be signed in to change notification settings - Fork 385
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
LINQ Enumerable Extensions Do Not Work With ExpandoObject Collection Properties #304
Comments
Currently, as a temporary solution (until implementing proper dynamic LINQ support), I've solved that using LINQ extension methods for Then, by using the extensions below, one can define the interpreter as follows to enable dynamic LINQ functionality:
Notes:
Implementation: DynamicLinqExtensionspublic static class DynamicLinqExtensions
{
private static IEnumerable<object?> AsEnumerable(this object? source) => source is IEnumerable enumerable
? enumerable.Cast<object?>()
: throw new InvalidCastException($"Type '{source?.GetType().ToString() ?? "null"}' is not enumerable");
private static object? Aggregate(this object source, Func<object?, object?, object?> func) =>
Enumerable.Aggregate(source.AsEnumerable(), func);
private static object? Aggregate(this object source, object? seed, Func<object?, object?, object?> func,
Func<object?, object?>? resultSelector = null) =>
resultSelector is null
? Enumerable.Aggregate(source.AsEnumerable(), seed, func)
: Enumerable.Aggregate(source.AsEnumerable(), seed, func, resultSelector);
private static bool Any(this object source, Func<object?, bool>? predicate = null) =>
predicate is null
? Enumerable.Any(source.AsEnumerable())
: Enumerable.Any(source.AsEnumerable(), predicate);
private static bool All(this object source, Func<object?, bool> predicate) =>
Enumerable.All(source.AsEnumerable(), predicate);
private static IEnumerable<object?> Append(this object source, object? element) =>
Enumerable.Append(source.AsEnumerable(), element);
private static IEnumerable<object?> Prepend(this object source, object? element) =>
Enumerable.Prepend(source.AsEnumerable(), element);
private static double? Average(this object source, Func<object?, double?>? selector = null) =>
selector is null
? Enumerable.Average(source.AsEnumerable(), AsNumeric)
: Enumerable.Average(source.AsEnumerable(), selector);
private static IEnumerable<object?[]> Chunk(this object source, int size) =>
Enumerable.Chunk(source.AsEnumerable(), size);
private static IEnumerable<object?> Concat(this object first, object? second) =>
Enumerable.Concat(first.AsEnumerable(), second.AsEnumerable());
private static bool Contains(this object source, object? value) =>
Enumerable.Contains(source.AsEnumerable(), value);
private static int Count(this object source, Func<object?, bool>? predicate = null) =>
predicate is null
? Enumerable.Count(source.AsEnumerable())
: Enumerable.Count(source.AsEnumerable(), predicate);
private static long LongCount(this object source, Func<object?, bool>? predicate = null) =>
predicate is null
? Enumerable.LongCount(source.AsEnumerable())
: Enumerable.LongCount(source.AsEnumerable(), predicate);
private static bool TryGetNonEnumeratedCount(this object source, out int count) =>
Enumerable.TryGetNonEnumeratedCount(source.AsEnumerable(), out count);
private static IEnumerable<object?> DefaultIfEmpty(this object source, object? defaultValue = null) =>
Enumerable.DefaultIfEmpty(source.AsEnumerable(), defaultValue);
private static IEnumerable<object?> Distinct(this object source) =>
Enumerable.Distinct(source.AsEnumerable());
private static IEnumerable<object?> DistinctBy(this object source, Func<object?, object?> keySelector) =>
Enumerable.DistinctBy(source.AsEnumerable(), keySelector);
private static object? ElementAt(this object source, int index) =>
Enumerable.ElementAt(source.AsEnumerable(), index);
private static object? ElementAtOrDefault(this object source, int index) =>
Enumerable.ElementAtOrDefault(source.AsEnumerable(), index);
private static IEnumerable<object?> Except(this object first, object second) =>
Enumerable.Except(first.AsEnumerable(), second.AsEnumerable());
private static IEnumerable<object?> ExceptBy(this object first, object second, Func<object?, object?> keySelector) =>
Enumerable.ExceptBy(first.AsEnumerable(), second.AsEnumerable(), keySelector);
private static object? First(this object source, Func<object?, bool>? predicate = null) =>
predicate is null
? Enumerable.First(source.AsEnumerable())
: Enumerable.First(source.AsEnumerable(), predicate);
private static object? FirstOrDefault(this object source, object? defaultValue) =>
Enumerable.FirstOrDefault(source.AsEnumerable(), defaultValue);
private static object? FirstOrDefault(this object source, Func<object?, bool>? predicate = null, object? defaultValue = null) =>
predicate is null
? Enumerable.FirstOrDefault(source.AsEnumerable(), defaultValue)
: Enumerable.FirstOrDefault(source.AsEnumerable(), predicate, defaultValue);
private static IEnumerable<IGrouping<object?, object?>> GroupBy(this object source,
Func<object?, object?> keySelector, Func<object?, object?>? elementSelector = null) =>
elementSelector is null
? Enumerable.GroupBy(source.AsEnumerable(), keySelector)
: Enumerable.GroupBy(source.AsEnumerable(), keySelector, elementSelector);
private static IEnumerable<object?> GroupBy(this object source,
Func<object?, object?> keySelector, Func<object?, IEnumerable<object?>, object?> resultSelector) =>
Enumerable.GroupBy(source.AsEnumerable(), keySelector, resultSelector);
private static IEnumerable<object?> GroupBy(this object source, Func<object?, object?> keySelector, Func<object?,
object?> elementSelector, Func<object?, IEnumerable<object?>, object?> resultSelector) =>
Enumerable.GroupBy(source.AsEnumerable(), keySelector, elementSelector, resultSelector);
private static IEnumerable<object?> GroupJoin(this object outer, object inner, Func<object?, object?> outerKeySelector,
Func<object?, object?> innerKeySelector, Func<object?, IEnumerable<object?>, object?> resultSelector) =>
Enumerable.GroupJoin(outer.AsEnumerable(), inner.AsEnumerable(), outerKeySelector, innerKeySelector, resultSelector);
private static IEnumerable<object?> Intersect(this object first, object second) =>
Enumerable.Intersect(first.AsEnumerable(), second.AsEnumerable());
private static IEnumerable<object?> IntersectBy(this object first, object second, Func<object?, object?> keySelector) =>
Enumerable.IntersectBy(first.AsEnumerable(), second.AsEnumerable(), keySelector);
private static IEnumerable<object?> Join(this object outer, object inner, Func<object?, object?> outerKeySelector,
Func<object?, object?> innerKeySelector, Func<object?, object?, object?> resultSelector) =>
Enumerable.Join(outer.AsEnumerable(), inner.AsEnumerable(), outerKeySelector, innerKeySelector, resultSelector);
private static object? Last(this object source, Func<object?, bool>? predicate = null) =>
predicate is null
? Enumerable.Last(source.AsEnumerable())
: Enumerable.Last(source.AsEnumerable(), predicate);
private static object? LastOrDefault(this object source, object? defaultValue) =>
Enumerable.LastOrDefault(source.AsEnumerable(), defaultValue);
private static object? LastOrDefault(this object source, Func<object?, bool>? predicate = null, object? defaultValue = null) =>
predicate is null
? Enumerable.LastOrDefault(source.AsEnumerable(), defaultValue)
: Enumerable.LastOrDefault(source.AsEnumerable(), predicate, defaultValue);
private static ILookup<object, object?> ToLookup(this object source,
Func<object?, object> keySelector, Func<object?, object?>? elementSelector = null) =>
elementSelector is null
? Enumerable.ToLookup(source.AsEnumerable(), keySelector)
: Enumerable.ToLookup(source.AsEnumerable(), keySelector, elementSelector);
private static double? Max(this object source, Func<object?, double?>? selector = null) =>
selector is null
? Enumerable.Max(source.AsEnumerable(), AsNumeric)
: Enumerable.Max(source.AsEnumerable(), selector);
private static object? MaxBy(this object source, Func<object?, double?> keySelector) =>
Enumerable.MaxBy(source.AsEnumerable(), keySelector);
private static double? Min(this object source, Func<object?, double?>? selector = null) =>
selector is null
? Enumerable.Min(source.AsEnumerable(), AsNumeric)
: Enumerable.Min(source.AsEnumerable(), selector);
private static object? MinBy(this object source, Func<object?, double?> keySelector) =>
Enumerable.MinBy(source.AsEnumerable(), keySelector);
private static IOrderedEnumerable<object?> Order(this object source) =>
Enumerable.Order(source.AsEnumerable());
private static IOrderedEnumerable<object?> OrderBy(this object source, Func<object?, object?> keySelector) =>
Enumerable.OrderBy(source.AsEnumerable(), keySelector);
private static IOrderedEnumerable<object?> OrderDescending(this object source) =>
Enumerable.OrderDescending(source.AsEnumerable());
private static IOrderedEnumerable<object?> OrderByDescending(this object source, Func<object?, object?> keySelector) =>
Enumerable.OrderByDescending(source.AsEnumerable(), keySelector);
private static IOrderedEnumerable<object?> ThenBy(this IOrderedEnumerable<object?> source, Func<object?, object?> keySelector) =>
Enumerable.ThenBy(source, keySelector);
private static IOrderedEnumerable<object?> ThenByDescending(this IOrderedEnumerable<object?> source,
Func<object?, object?> keySelector) =>
Enumerable.ThenByDescending(source, keySelector);
private static IEnumerable<object?> Reverse(this object source) =>
Enumerable.Reverse(source.AsEnumerable());
private static IEnumerable<object?> Select(this object source, Func<object?, object?> selector) =>
Enumerable.Select(source.AsEnumerable(), selector);
private static IEnumerable<object?> Select(this object source, Func<object?, int, object?> selector) =>
Enumerable.Select(source.AsEnumerable(), selector);
private static IEnumerable<object?> SelectMany(this object source, Func<object?, IEnumerable<object?>> selector) =>
Enumerable.SelectMany(source.AsEnumerable(), selector);
private static IEnumerable<object?> SelectMany(this object source, Func<object?, int, IEnumerable<object?>> selector) =>
Enumerable.SelectMany(source.AsEnumerable(), selector);
private static IEnumerable<object?> SelectMany(this object source,
Func<object?, IEnumerable<object?>> collectionSelector, Func<object?, object?, object?> resultSelector) =>
Enumerable.SelectMany(source.AsEnumerable(), collectionSelector, resultSelector);
private static IEnumerable<object?> SelectMany(this object source,
Func<object?, int, IEnumerable<object?>> collectionSelector, Func<object?, object?, object?> resultSelector) =>
Enumerable.SelectMany(source.AsEnumerable(), collectionSelector, resultSelector);
private static bool SequenceEqual(this object first, object second) =>
Enumerable.SequenceEqual(first.AsEnumerable(), second.AsEnumerable());
private static object? Single(this object source, Func<object?, bool>? predicate = null) =>
predicate is null
? Enumerable.Single(source.AsEnumerable())
: Enumerable.Single(source.AsEnumerable(), predicate);
private static object? SingleOrDefault(this object source, object? defaultValue) =>
Enumerable.SingleOrDefault(source.AsEnumerable(), defaultValue);
private static object? SingleOrDefault(this object source, Func<object?, bool>? predicate = null, object? defaultValue = null) =>
predicate is null
? Enumerable.SingleOrDefault(source.AsEnumerable(), defaultValue)
: Enumerable.SingleOrDefault(source.AsEnumerable(), predicate, defaultValue);
private static IEnumerable<object?> Skip(this object source, int count) =>
Enumerable.Skip(source.AsEnumerable(), count);
private static IEnumerable<object?> SkipWhile(this object source, Func<object?, int, bool> predicate) =>
Enumerable.SkipWhile(source.AsEnumerable(), predicate);
private static IEnumerable<object?> SkipWhile(this object source, Func<object?, bool> predicate) =>
Enumerable.SkipWhile(source.AsEnumerable(), predicate);
private static IEnumerable<object?> SkipLast(this object source, int count) =>
Enumerable.SkipLast(source.AsEnumerable(), count);
private static double? Sum(this object source, Func<object?, double?>? selector = null) =>
selector is null
? Enumerable.Sum(source.AsEnumerable(), AsNumeric)
: Enumerable.Sum(source.AsEnumerable(), selector);
private static IEnumerable<object?> Take(this object source, int count) =>
Enumerable.Take(source.AsEnumerable(), count);
private static IEnumerable<object?> TakeLast(this object source, int count) =>
Enumerable.TakeLast(source.AsEnumerable(), count);
private static IEnumerable<object?> TakeWhile(this object source, Func<object?, bool> predicate) =>
Enumerable.TakeWhile(source.AsEnumerable(), predicate);
private static IEnumerable<object?> TakeWhile(this object source, Func<object?, int, bool> predicate) =>
Enumerable.TakeWhile(source.AsEnumerable(), predicate);
private static object?[] ToArray(this object source) =>
Enumerable.ToArray(source.AsEnumerable());
private static List<object?> ToList(this object source) =>
Enumerable.ToList(source.AsEnumerable());
private static Dictionary<object, object?> ToDictionary(this object source, Func<object?, object> keySelector,
Func<object?, object?>? elementSelector = null) =>
elementSelector is null
? Enumerable.ToDictionary(source.AsEnumerable(), keySelector)
: Enumerable.ToDictionary(source.AsEnumerable(), keySelector, elementSelector);
private static HashSet<object?> ToHashSet(this object source) =>
Enumerable.ToHashSet(source.AsEnumerable());
private static IEnumerable<object?> Union(this object first, object second) =>
Enumerable.Union(first.AsEnumerable(), second.AsEnumerable());
private static IEnumerable<object?> UnionBy(this object first, object second, Func<object?, object?> keySelector) =>
Enumerable.UnionBy(first.AsEnumerable(), second.AsEnumerable(), keySelector);
private static IEnumerable<object?> Where(this object source, Func<object?, bool> predicate) =>
Enumerable.Where(source.AsEnumerable(), predicate);
private static IEnumerable<object?> Where(this object source, Func<object?, int, bool> predicate) =>
Enumerable.Where(source.AsEnumerable(), predicate);
private static IEnumerable<object?> Zip(this object first, object second, Func<object?, object?, object?> resultSelector) =>
Enumerable.Zip(first.AsEnumerable(), second.AsEnumerable(), resultSelector);
private static IEnumerable<(object? First, object? Second)> Zip(this object first, object second) =>
Enumerable.Zip(first.AsEnumerable(), second.AsEnumerable());
private static IEnumerable<(object? First, object? Second, object? Third)> Zip(this object first, object second, object third) =>
Enumerable.Zip(first.AsEnumerable(), second.AsEnumerable(), third.AsEnumerable());
private static double? AsNumeric(object? item) => item is null ? null : Convert.ToDouble(item);
} |
It's worth noting that the C# compiler doesn't allow it either: dynamic dynamicData = new ExpandoObject();
dynamicData.Some = new List<string> { "one", "two", "two", "three" };
var result = dynamicData.Some.Any(x => x == "two"); raises compiler error
|
If C# compiler doesn't allow this scenario, I think we can ignore it. |
@davideicardi I've ended up implementing an optimized custom expression tree compiler for my exact scenario (object with nested I agree that ExpandoObject should be avoided in general. However, in cases where it can't be avoided, LINQ support can be an optional feature—not that mandatory, though. |
LINQ extensions within expressions do not work when applied to properties of
ExpandoObject
.(For comparison - ExpressionEvaluator does support that with OptionInstanceMethodsCallActive)
Given the following code:
The following exception is thrown:
The text was updated successfully, but these errors were encountered: