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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ Or provide a list of additional types in the [DefaultDynamicLinqCustomTypeProvid
|   **Issues** | [![GitHub issues](https://img.shields.io/github/issues/StefH/System.Linq.Dynamic.Core.svg)](https://github.com/StefH/System.Linq.Dynamic.Core/issues) |
| | |
| ***Quality*** |   |
|   **CI Workflow** | ![CI Workflow](https://github.com/zzzprojects/System.Linq.Dynamic.Core/actions/workflows/ci.yml/badge.svg) |
|   **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) |
|   **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) |
| |
| ***NuGet*** |   |
Expand Down
21 changes: 15 additions & 6 deletions src/System.Linq.Dynamic.Core/DynamicQueryableExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -871,12 +871,10 @@
Check.HasNoNulls(keySelectors);

var selectors = new List<Func<TElement, object>>(keySelectors.Length);

bool createParameterCtor = true;
foreach (var selector in keySelectors)
{
LambdaExpression l = DynamicExpressionParser.ParseLambda(config, createParameterCtor, typeof(TElement), typeof(object), selector);
selectors.Add((Func<TElement, object>)l.Compile());
var lambdaExpression = DynamicExpressionParser.ParseLambda(config, createParameterCtor: true, typeof(TElement), null, selector);
selectors.Add((Func<TElement, object>)EnsureLambdaExpressionReturnsObject(lambdaExpression).Compile());
}

return GroupByManyInternal(source, selectors.ToArray(), 0);
Expand Down Expand Up @@ -913,8 +911,9 @@

var selector = keySelectors[currentSelector];

var result = source.GroupBy(selector).Select(
g => new GroupResult
var result = source
.GroupBy(selector)
.Select(g => new GroupResult
{
Key = g.Key,
Count = g.Count(),
Expand Down Expand Up @@ -2136,7 +2135,7 @@
/// <inheritdoc cref="SelectMany(IQueryable, ParsingConfig, string, string, string, string, object[], object[])"/>
public static IQueryable SelectMany(this IQueryable source, string collectionSelector, string resultSelector, string collectionParameterName, string resultParameterName, object?[]? collectionSelectorArgs = null, params object?[]? resultSelectorArgs)
{
return SelectMany(source, ParsingConfig.Default, collectionSelector, resultSelector, collectionParameterName, resultParameterName, collectionSelectorArgs, resultSelectorArgs);

Check warning on line 2138 in src/System.Linq.Dynamic.Core/DynamicQueryableExtensions.cs

View workflow job for this annotation

GitHub Actions / Windows: Build and Tests

Argument of type 'object?[]' cannot be used for parameter 'resultSelectorArgs' of type 'object[]' in 'IQueryable DynamicQueryableExtensions.SelectMany(IQueryable source, ParsingConfig config, string collectionSelector, string resultSelector, string collectionParameterName, string resultParameterName, object?[]? collectionSelectorArgs = null, params object[]? resultSelectorArgs)' due to differences in the nullability of reference types.

Check warning on line 2138 in src/System.Linq.Dynamic.Core/DynamicQueryableExtensions.cs

View workflow job for this annotation

GitHub Actions / Linux: Build and Tests

Argument of type 'object?[]' cannot be used for parameter 'resultSelectorArgs' of type 'object[]' in 'IQueryable DynamicQueryableExtensions.SelectMany(IQueryable source, ParsingConfig config, string collectionSelector, string resultSelector, string collectionParameterName, string resultParameterName, object?[]? collectionSelectorArgs = null, params object[]? resultSelectorArgs)' due to differences in the nullability of reference types.

Check warning on line 2138 in src/System.Linq.Dynamic.Core/DynamicQueryableExtensions.cs

View workflow job for this annotation

GitHub Actions / Linux: Build and Tests

Argument of type 'object?[]' cannot be used for parameter 'resultSelectorArgs' of type 'object[]' in 'IQueryable DynamicQueryableExtensions.SelectMany(IQueryable source, ParsingConfig config, string collectionSelector, string resultSelector, string collectionParameterName, string resultParameterName, object?[]? collectionSelectorArgs = null, params object[]? resultSelectorArgs)' due to differences in the nullability of reference types.
}

#endregion SelectMany
Expand Down Expand Up @@ -2847,6 +2846,16 @@

return (TResult?)Convert.ChangeType(result, typeof(TResult))!;
}

private static LambdaExpression EnsureLambdaExpressionReturnsObject(LambdaExpression lambdaExpression)
{
if (!lambdaExpression.GetReturnType().GetTypeInfo().IsSubclassOf(typeof(DynamicClass)))
{
return Expression.Lambda(Expression.Convert(lambdaExpression.Body, typeof(object)), lambdaExpression.Parameters.ToArray());
}

return lambdaExpression;
}
#endregion Private Helpers
}
}
8 changes: 4 additions & 4 deletions src/System.Linq.Dynamic.Core/Parser/ExpressionHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,15 +45,15 @@ public bool TryUnwrapAsConstantExpression<TValue>(Expression? expression, [NotNu
return true;
}

value = default;
value = null;
return false;
}

public bool TryUnwrapAsConstantExpression(Expression? expression, [NotNullWhen(true)] out ConstantExpression? value)
{
if (!_parsingConfig.UseParameterizedNamesInDynamicQuery || expression is not MemberExpression memberExpression)
{
value = default;
value = null;
return false;
}

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

value = default;
value = null;
return false;
}

Expand Down Expand Up @@ -531,6 +531,6 @@ private static object[] ConvertIfIEnumerableHasValues(IEnumerable? input)
return input.Cast<object>().ToArray();
}

return new object[0];
return [];
}
}
121 changes: 73 additions & 48 deletions test/System.Linq.Dynamic.Core.Tests/QueryableTests.GroupByMany.cs
Original file line number Diff line number Diff line change
@@ -1,57 +1,82 @@
using System.Collections.Generic;
using FluentAssertions;
using NFluent;
using Xunit;

namespace System.Linq.Dynamic.Core.Tests
namespace System.Linq.Dynamic.Core.Tests;

public partial class QueryableTests
{
public partial class QueryableTests
[Fact]
public void GroupByMany_Dynamic_LambdaExpressions()
{
var lst = new List<Tuple<int, int, int>>
{
new(1, 1, 1),
new(1, 1, 2),
new(1, 1, 3),
new(2, 2, 4),
new(2, 2, 5),
new(2, 2, 6),
new(2, 3, 7)
};

var sel = lst.GroupByMany(x => x.Item1, x => x.Item2).ToArray();

Assert.Equal(2, sel.Length);
Assert.Single(sel.First().Subgroups);
Assert.Equal(2, sel.Skip(1).First().Subgroups.Count());
}

[Fact]
public void GroupByMany_Dynamic_StringExpressions()
{
[Fact]
public void GroupByMany_Dynamic_LambdaExpressions()
var lst = new List<Tuple<int, int, int>>
{
var lst = new List<Tuple<int, int, int>>
{
new Tuple<int, int, int>(1, 1, 1),
new Tuple<int, int, int>(1, 1, 2),
new Tuple<int, int, int>(1, 1, 3),
new Tuple<int, int, int>(2, 2, 4),
new Tuple<int, int, int>(2, 2, 5),
new Tuple<int, int, int>(2, 2, 6),
new Tuple<int, int, int>(2, 3, 7)
};

var sel = lst.AsQueryable().GroupByMany(x => x.Item1, x => x.Item2);

Assert.Equal(2, sel.Count());
Assert.Single(sel.First().Subgroups);
Assert.Equal(2, sel.Skip(1).First().Subgroups.Count());
}

[Fact]
public void GroupByMany_Dynamic_StringExpressions()
new(1, 1, 1),
new(1, 1, 2),
new(1, 1, 3),
new(2, 2, 4),
new(2, 2, 5),
new(2, 2, 6),
new(2, 3, 7)
};

var sel = lst.GroupByMany("Item1", "Item2").ToList();

Check.That(sel.Count).Equals(2);

var firstGroupResult = sel.First();
Check.That(firstGroupResult.ToString()).Equals("1 (3)");
Check.That(firstGroupResult.Subgroups.Count()).Equals(1);

var skippedGroupResult = sel.Skip(1).First();
Check.That(skippedGroupResult.ToString()).Equals("2 (4)");
Check.That(skippedGroupResult.Subgroups.Count()).Equals(2);
}

[Fact]
public void GroupByMany_Dynamic_CompositeKey()
{
// Arrange
var data = new[]
{
var lst = new List<Tuple<int, int, int>>
{
new Tuple<int, int, int>(1, 1, 1),
new Tuple<int, int, int>(1, 1, 2),
new Tuple<int, int, int>(1, 1, 3),
new Tuple<int, int, int>(2, 2, 4),
new Tuple<int, int, int>(2, 2, 5),
new Tuple<int, int, int>(2, 2, 6),
new Tuple<int, int, int>(2, 3, 7)
};

var sel = lst.AsQueryable().GroupByMany("Item1", "Item2").ToList();

Check.That(sel.Count).Equals(2);

var firstGroupResult = sel.First();
Check.That(firstGroupResult.ToString()).Equals("1 (3)");
Check.That(firstGroupResult.Subgroups.Count()).Equals(1);

var skippedGroupResult = sel.Skip(1).First();
Check.That(skippedGroupResult.ToString()).Equals("2 (4)");
Check.That(skippedGroupResult.Subgroups.Count()).Equals(2);
}
new { MachineId = 1, Machine = new { Id = 1, Name = "A" } },
new { MachineId = 1, Machine = new { Id = 1, Name = "A" } },
new { MachineId = 2, Machine = new { Id = 2, Name = "B" } }
};

// Act
var normalResult = data
.GroupByMany(d => new { d.MachineId, d.Machine.Name }, a => a.Machine.Id)
.Select(x => x.ToString())
.ToList();
var result = data
.GroupByMany("new (MachineId, Machine.Name)", "Machine.Id")
.Select(x => x.ToString())
.ToList();

// Assert
result.Should().BeEquivalentTo(normalResult);
}
}
}
Loading