Skip to content

Commit d6b966e

Browse files
committed
Merge branch 'master' into stef-PredefinedMethodsHelper
2 parents e289618 + 1cc4ea0 commit d6b966e

19 files changed

+256
-91
lines changed

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
1+
# v1.6.8 (28 September 2025)
2+
- [#946](https://github.com/zzzprojects/System.Linq.Dynamic.Core/pull/946) - Fix GroupByMany using composite key and normal key [bug] contributed by [StefH](https://github.com/StefH)
3+
- [#948](https://github.com/zzzprojects/System.Linq.Dynamic.Core/pull/948) - Add IndexerName attribute to DynamicClass to fix naming issues with Item [bug] contributed by [StefH](https://github.com/StefH)
4+
- [#936](https://github.com/zzzprojects/System.Linq.Dynamic.Core/issues/936) - AmbiguousMatchException when selecting a property named “Item” using EF Core DbContext [bug]
5+
- [#945](https://github.com/zzzprojects/System.Linq.Dynamic.Core/issues/945) - ParseException when using composite key for grouping in GroupByMany [bug]
6+
7+
# 1.6.7 (28 July 2025)
8+
- [#938](https://github.com/zzzprojects/System.Linq.Dynamic.Core/pull/938) - Use TryConvertTypes also for strings [bug] contributed by [StefH](https://github.com/StefH)
9+
110
# v1.6.6 (11 June 2025)
211
- [#929](https://github.com/zzzprojects/System.Linq.Dynamic.Core/pull/929) - Add GroupBy method for Z.DynamicLinq.SystemTextJson and Z.DynamicLinq.NewtonsoftJson contributed by [StefH](https://github.com/StefH)
312
- [#932](https://github.com/zzzprojects/System.Linq.Dynamic.Core/pull/932) - Fix "in" for nullable Enums [bug] contributed by [StefH](https://github.com/StefH)

Generate-ReleaseNotes.bat

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
rem https://github.com/StefH/GitHubReleaseNotes
22

3-
SET version=v1.6.6
3+
SET version=v1.6.8
44

55
GitHubReleaseNotes --output CHANGELOG.md --exclude-labels known_issue out_of_scope not_planned invalid question documentation wontfix environment duplicate --language en --version %version% --token %GH_TOKEN%

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ Or provide a list of additional types in the [DefaultDynamicLinqCustomTypeProvid
8484
|   **Issues** | [![GitHub issues](https://img.shields.io/github/issues/StefH/System.Linq.Dynamic.Core.svg)](https://github.com/StefH/System.Linq.Dynamic.Core/issues) |
8585
| | |
8686
| ***Quality*** |   |
87-
|   **CI Workflow** | ![CI Workflow](https://github.com/zzzprojects/System.Linq.Dynamic.Core/actions/workflows/ci.yml/badge.svg) |
87+
|   **CI Workflow** | [![CI Workflow](https://github.com/zzzprojects/System.Linq.Dynamic.Core/actions/workflows/ci.yml/badge.svg)](https://github.com/zzzprojects/System.Linq.Dynamic.Core/actions/workflows/ci.yml) |
8888
|   **SonarCloud** | [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=zzzprojects_System.Linq.Dynamic.Core&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=zzzprojects_System.Linq.Dynamic.Core) |
8989
| |
9090
| ***NuGet*** |   |

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

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.Collections.Generic;
33
using System.Dynamic;
44
using System.Reflection;
5+
using System.Runtime.CompilerServices;
56

67
namespace System.Linq.Dynamic.Core;
78

@@ -18,6 +19,8 @@ namespace System.Linq.Dynamic.Core;
1819
/// </summary>
1920
public abstract class DynamicClass : DynamicObject
2021
{
22+
internal const string IndexerName = "System_Linq_Dynamic_Core_DynamicClass_Indexer";
23+
2124
private Dictionary<string, object?>? _propertiesDictionary;
2225

2326
private Dictionary<string, object?> Properties
@@ -99,11 +102,12 @@ public void SetDynamicPropertyValue(string propertyName, object value)
99102
/// <value>The <see cref="object"/>.</value>
100103
/// <param name="name">The name.</param>
101104
/// <returns>Value from the property.</returns>
105+
[IndexerName(IndexerName)]
102106
public object? this[string name]
103107
{
104108
get
105109
{
106-
return Properties.TryGetValue(name, out object? result) ? result : null;
110+
return Properties.TryGetValue(name, out var result) ? result : null;
107111
}
108112

109113
set
@@ -153,7 +157,7 @@ public override bool TryGetMember(GetMemberBinder binder, out object? result)
153157
/// </returns>
154158
public override bool TrySetMember(SetMemberBinder binder, object? value)
155159
{
156-
string name = binder.Name;
160+
var name = binder.Name;
157161
if (Properties.ContainsKey(name))
158162
{
159163
Properties[name] = value;

src/System.Linq.Dynamic.Core/DynamicClass.net35.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ namespace System.Linq.Dynamic.Core;
66
/// </summary>
77
public abstract class DynamicClass
88
{
9+
internal const string IndexerName = "System_Linq_Dynamic_Core_DynamicClass_Indexer";
10+
911
/// <summary>
1012
/// Gets the dynamic property by name.
1113
/// </summary>

src/System.Linq.Dynamic.Core/DynamicClass.uap.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#if UAP10_0
22
using System.Collections.Generic;
33
using System.Dynamic;
4+
using System.Runtime.CompilerServices;
45

56
namespace System.Linq.Dynamic.Core;
67

@@ -9,6 +10,8 @@ namespace System.Linq.Dynamic.Core;
910
/// </summary>
1011
public class DynamicClass : DynamicObject
1112
{
13+
internal const string IndexerName = "System_Linq_Dynamic_Core_DynamicClass_Indexer";
14+
1215
private readonly Dictionary<string, object> _properties = new();
1316

1417
/// <summary>
@@ -31,6 +34,7 @@ public DynamicClass(params KeyValuePair<string, object>[] propertylist)
3134
/// </value>
3235
/// <param name="name">The name.</param>
3336
/// <returns>Value from the property.</returns>
37+
[IndexerName(IndexerName)]
3438
public object this[string name]
3539
{
3640
get

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

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -871,12 +871,10 @@ public static IEnumerable<GroupResult> GroupByMany<TElement>(this IEnumerable<TE
871871
Check.HasNoNulls(keySelectors);
872872

873873
var selectors = new List<Func<TElement, object>>(keySelectors.Length);
874-
875-
bool createParameterCtor = true;
876874
foreach (var selector in keySelectors)
877875
{
878-
LambdaExpression l = DynamicExpressionParser.ParseLambda(config, createParameterCtor, typeof(TElement), typeof(object), selector);
879-
selectors.Add((Func<TElement, object>)l.Compile());
876+
var lambdaExpression = DynamicExpressionParser.ParseLambda(config, createParameterCtor: true, typeof(TElement), null, selector);
877+
selectors.Add((Func<TElement, object>)EnsureLambdaExpressionReturnsObject(lambdaExpression).Compile());
880878
}
881879

882880
return GroupByManyInternal(source, selectors.ToArray(), 0);
@@ -913,8 +911,9 @@ private static IEnumerable<GroupResult> GroupByManyInternal<TElement>(IEnumerabl
913911

914912
var selector = keySelectors[currentSelector];
915913

916-
var result = source.GroupBy(selector).Select(
917-
g => new GroupResult
914+
var result = source
915+
.GroupBy(selector)
916+
.Select(g => new GroupResult
918917
{
919918
Key = g.Key,
920919
Count = g.Count(),
@@ -2847,6 +2846,16 @@ private static TResult ConvertResultIfNeeded<TResult>(object result)
28472846

28482847
return (TResult?)Convert.ChangeType(result, typeof(TResult))!;
28492848
}
2849+
2850+
private static LambdaExpression EnsureLambdaExpressionReturnsObject(LambdaExpression lambdaExpression)
2851+
{
2852+
if (!TypeHelper.IsDynamicClass(lambdaExpression.GetReturnType()))
2853+
{
2854+
return Expression.Lambda(Expression.Convert(lambdaExpression.Body, typeof(object)), lambdaExpression.Parameters.ToArray());
2855+
}
2856+
2857+
return lambdaExpression;
2858+
}
28502859
#endregion Private Helpers
28512860
}
28522861
}

src/System.Linq.Dynamic.Core/Parser/ExpressionHelper.cs

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -45,15 +45,15 @@ public bool TryUnwrapAsConstantExpression<TValue>(Expression? expression, [NotNu
4545
return true;
4646
}
4747

48-
value = default;
48+
value = null;
4949
return false;
5050
}
5151

5252
public bool TryUnwrapAsConstantExpression(Expression? expression, [NotNullWhen(true)] out ConstantExpression? value)
5353
{
5454
if (!_parsingConfig.UseParameterizedNamesInDynamicQuery || expression is not MemberExpression memberExpression)
5555
{
56-
value = default;
56+
value = null;
5757
return false;
5858
}
5959

@@ -68,7 +68,7 @@ public bool TryUnwrapAsConstantExpression(Expression? expression, [NotNullWhen(t
6868
return true;
6969
}
7070

71-
value = default;
71+
value = null;
7272
return false;
7373
}
7474

@@ -378,6 +378,26 @@ public Expression ConvertAnyArrayToObjectArray(Expression arrayExpression)
378378
);
379379
}
380380

381+
/// <inheritdoc/>
382+
public bool TryConvertTypes(ref Expression left, ref Expression right)
383+
{
384+
if (!_parsingConfig.ConvertObjectToSupportComparison || left.Type == right.Type || Constants.IsNull(left) || Constants.IsNull(right))
385+
{
386+
return false;
387+
}
388+
389+
if (left.Type == typeof(object))
390+
{
391+
left = Expression.Convert(left, right.Type);
392+
}
393+
else if (right.Type == typeof(object))
394+
{
395+
right = Expression.Convert(right, left.Type);
396+
}
397+
398+
return true;
399+
}
400+
381401
private Expression? GetMemberExpression(Expression? expression)
382402
{
383403
if (ExpressionQualifiesForNullPropagation(expression))
@@ -455,26 +475,6 @@ private List<Expression> CollectExpressions(bool addSelf, Expression sourceExpre
455475
return list;
456476
}
457477

458-
/// <summary>
459-
/// If the types are different (and not null), try to convert the object type to other type.
460-
/// </summary>
461-
private void TryConvertTypes(ref Expression left, ref Expression right)
462-
{
463-
if (!_parsingConfig.ConvertObjectToSupportComparison || left.Type == right.Type || Constants.IsNull(left) || Constants.IsNull(right))
464-
{
465-
return;
466-
}
467-
468-
if (left.Type == typeof(object))
469-
{
470-
left = Expression.Convert(left, right.Type);
471-
}
472-
else if (right.Type == typeof(object))
473-
{
474-
right = Expression.Convert(right, left.Type);
475-
}
476-
}
477-
478478
private static Expression GenerateStaticMethodCall(string methodName, Expression left, Expression right)
479479
{
480480
if (!TryGetStaticMethod(methodName, left, right, out var methodInfo))
@@ -531,6 +531,6 @@ private static object[] ConvertIfIEnumerableHasValues(IEnumerable? input)
531531
return input.Cast<object>().ToArray();
532532
}
533533

534-
return new object[0];
534+
return [];
535535
}
536536
}

src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -529,7 +529,11 @@ private Expression ParseComparisonOperator()
529529
// If left or right is NullLiteral, just continue. Else check if the types differ.
530530
if (!(Constants.IsNull(left) || Constants.IsNull(right)) && left.Type != right.Type)
531531
{
532-
if (left.Type.IsAssignableFrom(right.Type) || HasImplicitConversion(right.Type, left.Type))
532+
if ((left.Type == typeof(object) || right.Type == typeof(object)) && _expressionHelper.TryConvertTypes(ref left, ref right))
533+
{
534+
// #937
535+
}
536+
else if (left.Type.IsAssignableFrom(right.Type) || HasImplicitConversion(right.Type, left.Type))
533537
{
534538
right = Expression.Convert(right, left.Type);
535539
}
@@ -1578,7 +1582,7 @@ private Expression CreateNewExpression(List<DynamicProperty> properties, List<Ex
15781582
var propertyInfos = type.GetProperties();
15791583
if (type.GetTypeInfo().BaseType == typeof(DynamicClass))
15801584
{
1581-
propertyInfos = propertyInfos.Where(x => x.Name != "Item").ToArray();
1585+
propertyInfos = propertyInfos.Where(x => x.Name != DynamicClass.IndexerName).ToArray();
15821586
}
15831587

15841588
var propertyTypes = propertyInfos.Select(p => p.PropertyType).ToArray();
@@ -1906,7 +1910,7 @@ private Expression ParseMemberAccess(Type? type, Expression? expression, string?
19061910
#if UAP10_0 || NETSTANDARD1_3
19071911
if (type == typeof(DynamicClass))
19081912
{
1909-
return Expression.MakeIndex(expression, typeof(DynamicClass).GetProperty("Item"), new[] { Expression.Constant(id) });
1913+
return Expression.MakeIndex(expression!, typeof(DynamicClass).GetProperty(DynamicClass.IndexerName), [Expression.Constant(id)]);
19101914
}
19111915
#endif
19121916
if (TryFindPropertyOrField(type!, id, expression, out var propertyOrFieldExpression))
@@ -1920,7 +1924,8 @@ private Expression ParseMemberAccess(Type? type, Expression? expression, string?
19201924

19211925
if (!_parsingConfig.DisableMemberAccessToIndexAccessorFallback && extraCheck)
19221926
{
1923-
var indexerMethod = expression?.Type.GetMethod("get_Item", new[] { typeof(string) });
1927+
var indexerName = TypeHelper.IsDynamicClass(type!) ? DynamicClass.IndexerName : "Item";
1928+
var indexerMethod = expression?.Type.GetMethod($"get_{indexerName}", [typeof(string)]);
19241929
if (indexerMethod != null)
19251930
{
19261931
return Expression.Call(expression, indexerMethod, Expression.Constant(id));
@@ -2555,7 +2560,7 @@ private bool TokenIsIdentifier(string id)
25552560
{
25562561
return _textParser.TokenIsIdentifier(id);
25572562
}
2558-
2563+
25592564
private string GetIdentifier()
25602565
{
25612566
_textParser.ValidateToken(TokenId.Identifier, Res.IdentifierExpected);

src/System.Linq.Dynamic.Core/Parser/IExpressionHelper.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,4 +48,9 @@ internal interface IExpressionHelper
4848
Expression GenerateDefaultExpression(Type type);
4949

5050
Expression ConvertAnyArrayToObjectArray(Expression arrayExpression);
51+
52+
/// <summary>
53+
/// If the types are different (and not null), try to convert the object type to other type.
54+
/// </summary>
55+
public bool TryConvertTypes(ref Expression left, ref Expression right);
5156
}

0 commit comments

Comments
 (0)