Skip to content

Commit f2e0ec7

Browse files
authored
Fix GroupByMany using composite key and normal key (#946)
* Fix GroupByMany using composite key and normal key * . * readme
1 parent 53361d6 commit f2e0ec7

File tree

4 files changed

+93
-59
lines changed

4 files changed

+93
-59
lines changed

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/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 (!lambdaExpression.GetReturnType().GetTypeInfo().IsSubclassOf(typeof(DynamicClass)))
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: 4 additions & 4 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

@@ -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
}
Lines changed: 73 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,82 @@
11
using System.Collections.Generic;
2+
using FluentAssertions;
23
using NFluent;
34
using Xunit;
45

5-
namespace System.Linq.Dynamic.Core.Tests
6+
namespace System.Linq.Dynamic.Core.Tests;
7+
8+
public partial class QueryableTests
69
{
7-
public partial class QueryableTests
10+
[Fact]
11+
public void GroupByMany_Dynamic_LambdaExpressions()
12+
{
13+
var lst = new List<Tuple<int, int, int>>
14+
{
15+
new(1, 1, 1),
16+
new(1, 1, 2),
17+
new(1, 1, 3),
18+
new(2, 2, 4),
19+
new(2, 2, 5),
20+
new(2, 2, 6),
21+
new(2, 3, 7)
22+
};
23+
24+
var sel = lst.GroupByMany(x => x.Item1, x => x.Item2).ToArray();
25+
26+
Assert.Equal(2, sel.Length);
27+
Assert.Single(sel.First().Subgroups);
28+
Assert.Equal(2, sel.Skip(1).First().Subgroups.Count());
29+
}
30+
31+
[Fact]
32+
public void GroupByMany_Dynamic_StringExpressions()
833
{
9-
[Fact]
10-
public void GroupByMany_Dynamic_LambdaExpressions()
34+
var lst = new List<Tuple<int, int, int>>
1135
{
12-
var lst = new List<Tuple<int, int, int>>
13-
{
14-
new Tuple<int, int, int>(1, 1, 1),
15-
new Tuple<int, int, int>(1, 1, 2),
16-
new Tuple<int, int, int>(1, 1, 3),
17-
new Tuple<int, int, int>(2, 2, 4),
18-
new Tuple<int, int, int>(2, 2, 5),
19-
new Tuple<int, int, int>(2, 2, 6),
20-
new Tuple<int, int, int>(2, 3, 7)
21-
};
22-
23-
var sel = lst.AsQueryable().GroupByMany(x => x.Item1, x => x.Item2);
24-
25-
Assert.Equal(2, sel.Count());
26-
Assert.Single(sel.First().Subgroups);
27-
Assert.Equal(2, sel.Skip(1).First().Subgroups.Count());
28-
}
29-
30-
[Fact]
31-
public void GroupByMany_Dynamic_StringExpressions()
36+
new(1, 1, 1),
37+
new(1, 1, 2),
38+
new(1, 1, 3),
39+
new(2, 2, 4),
40+
new(2, 2, 5),
41+
new(2, 2, 6),
42+
new(2, 3, 7)
43+
};
44+
45+
var sel = lst.GroupByMany("Item1", "Item2").ToList();
46+
47+
Check.That(sel.Count).Equals(2);
48+
49+
var firstGroupResult = sel.First();
50+
Check.That(firstGroupResult.ToString()).Equals("1 (3)");
51+
Check.That(firstGroupResult.Subgroups.Count()).Equals(1);
52+
53+
var skippedGroupResult = sel.Skip(1).First();
54+
Check.That(skippedGroupResult.ToString()).Equals("2 (4)");
55+
Check.That(skippedGroupResult.Subgroups.Count()).Equals(2);
56+
}
57+
58+
[Fact]
59+
public void GroupByMany_Dynamic_CompositeKey()
60+
{
61+
// Arrange
62+
var data = new[]
3263
{
33-
var lst = new List<Tuple<int, int, int>>
34-
{
35-
new Tuple<int, int, int>(1, 1, 1),
36-
new Tuple<int, int, int>(1, 1, 2),
37-
new Tuple<int, int, int>(1, 1, 3),
38-
new Tuple<int, int, int>(2, 2, 4),
39-
new Tuple<int, int, int>(2, 2, 5),
40-
new Tuple<int, int, int>(2, 2, 6),
41-
new Tuple<int, int, int>(2, 3, 7)
42-
};
43-
44-
var sel = lst.AsQueryable().GroupByMany("Item1", "Item2").ToList();
45-
46-
Check.That(sel.Count).Equals(2);
47-
48-
var firstGroupResult = sel.First();
49-
Check.That(firstGroupResult.ToString()).Equals("1 (3)");
50-
Check.That(firstGroupResult.Subgroups.Count()).Equals(1);
51-
52-
var skippedGroupResult = sel.Skip(1).First();
53-
Check.That(skippedGroupResult.ToString()).Equals("2 (4)");
54-
Check.That(skippedGroupResult.Subgroups.Count()).Equals(2);
55-
}
64+
new { MachineId = 1, Machine = new { Id = 1, Name = "A" } },
65+
new { MachineId = 1, Machine = new { Id = 1, Name = "A" } },
66+
new { MachineId = 2, Machine = new { Id = 2, Name = "B" } }
67+
};
68+
69+
// Act
70+
var normalResult = data
71+
.GroupByMany(d => new { d.MachineId, d.Machine.Name }, a => a.Machine.Id)
72+
.Select(x => x.ToString())
73+
.ToList();
74+
var result = data
75+
.GroupByMany("new (MachineId, Machine.Name)", "Machine.Id")
76+
.Select(x => x.ToString())
77+
.ToList();
78+
79+
// Assert
80+
result.Should().BeEquivalentTo(normalResult);
5681
}
57-
}
82+
}

0 commit comments

Comments
 (0)