From aba20f04e285cb67b9ea671398c8785f1ec7ec18 Mon Sep 17 00:00:00 2001 From: David Lebee Date: Fri, 26 Oct 2018 15:54:05 -0500 Subject: [PATCH 1/3] reusable methods for diff kind of calls. --- PoweredSoft.DynamicLinq.Test/GroupingTests.cs | 78 +++++++++++ .../Helpers/QueryableHelpers.cs | 122 +++++++++--------- 2 files changed, 141 insertions(+), 59 deletions(-) diff --git a/PoweredSoft.DynamicLinq.Test/GroupingTests.cs b/PoweredSoft.DynamicLinq.Test/GroupingTests.cs index ad2543c..7ea08ea 100644 --- a/PoweredSoft.DynamicLinq.Test/GroupingTests.cs +++ b/PoweredSoft.DynamicLinq.Test/GroupingTests.cs @@ -249,5 +249,83 @@ namespace PoweredSoft.DynamicLinq.Test CollectionAssert.AreEqual(expected.Titles as ICollection, titles); } } + + [TestMethod] + public void GroupByToListWithPath() + { + var limitResult = TestData.Posts.Where(t => t.Author != null); + + var expected = limitResult.GroupBy(t => new + { + AuthorFirstName = t.Author.FirstName + }) + .Select(t => new + { + Key = t.Key.AuthorFirstName, + Contents = t.Select(t2 => t2.Content).ToList() + }) + .ToList(); + + var actualQuery = limitResult + .GroupBy(t => t.Path("Author.FirstName", "AuthorFirstName")) + .Select(t => + { + t.Key("Key", "AuthorFirstName"); + t.ToList("Content", "Contents", SelectCollectionHandling.LeaveAsIs); + }); + + var actual = actualQuery.ToDynamicClassList(); + + Assert.AreEqual(expected.Count, actual.Count); + for(var i = 0; i < expected.Count; i++) + { + var itExpected = expected[i]; + var itActual = actual[i]; + + + Assert.AreEqual(itExpected.Key, itActual.GetDynamicPropertyValue("Key")); + CollectionAssert.AreEqual(itExpected.Contents, itActual.GetDynamicPropertyValue("Contents") as ICollection); + } + } + + [TestMethod] + public void GroupByToListWithPathWithNullChecking() + { + var limitResult = TestData.Authors; + + var expected = limitResult.GroupBy(t => new + { + AuthorFirstName = t.FirstName + }) + .Select(t => new + { + Key = t.Key.AuthorFirstName, + Contents = t.SelectMany(t2 => t2.Posts.Select(t3 => t3.Content)).ToList() + }) + .ToList(); + + var actualQuery = limitResult + .GroupBy(t => t.NullChecking().Path("FirstName", "AuthorFirstName")) + .Select(t => + { + t.NullChecking(); + t.Key("Key", "AuthorFirstName"); + t.ToList("Posts.Content", "Contents", SelectCollectionHandling.Flatten); + }); + + var actual = actualQuery.ToDynamicClassList(); + + Assert.AreEqual(expected.Count, actual.Count); + for (var i = 0; i < expected.Count; i++) + { + var itExpected = expected[i]; + var itActual = actual[i]; + + + Assert.AreEqual(itExpected.Key, itActual.GetDynamicPropertyValue("Key")); + CollectionAssert.AreEqual(itExpected.Contents, itActual.GetDynamicPropertyValue("Contents") as ICollection); + } + } + } } diff --git a/PoweredSoft.DynamicLinq/Helpers/QueryableHelpers.cs b/PoweredSoft.DynamicLinq/Helpers/QueryableHelpers.cs index eb4db22..d16cef1 100644 --- a/PoweredSoft.DynamicLinq/Helpers/QueryableHelpers.cs +++ b/PoweredSoft.DynamicLinq/Helpers/QueryableHelpers.cs @@ -142,7 +142,11 @@ namespace PoweredSoft.DynamicLinq.Helpers { if (selectType == SelectTypes.Key) { - return ResolvePathForExpression(parameter, path); + var parser = new ExpressionParser(parameter, path); + var resolver = new PathExpressionResolver(parser); + resolver.NullChecking = nullChecking; + resolver.Resolve(); + return resolver.GetResultBodyExpression(); } else if (selectType == SelectTypes.Count) { @@ -157,76 +161,69 @@ namespace PoweredSoft.DynamicLinq.Helpers return body; } else if (selectType == SelectTypes.Average) - { - /// https://stackoverflow.com/questions/25482097/call-enumerable-average-via-expression - var notGroupedType = parameter.Type.GenericTypeArguments[1]; - var innerParameter = Expression.Parameter(notGroupedType); - var innerMemberExpression = ResolvePathForExpression(innerParameter, path); - var innerMemberLambda = Expression.Lambda(innerMemberExpression, innerParameter); - var body = Expression.Call(typeof(Enumerable), "Average", new[] { notGroupedType }, parameter, innerMemberLambda); - return body; - } + return CreateGroupedAggregateExpression(parameter, path, "Average"); else if (selectType == SelectTypes.Sum) - { - var notGroupedType = parameter.Type.GenericTypeArguments[1]; - var innerParameter = Expression.Parameter(notGroupedType); - var innerMemberExpression = ResolvePathForExpression(innerParameter, path); - var innerMemberLambda = Expression.Lambda(innerMemberExpression, innerParameter); - var body = Expression.Call(typeof(Enumerable), "Sum", new[] { notGroupedType }, parameter, innerMemberLambda); - return body; - } + return CreateGroupedAggregateExpression(parameter, path, "Sum"); else if (selectType == SelectTypes.Min) - { - var notGroupedType = parameter.Type.GenericTypeArguments[1]; - var innerParameter = Expression.Parameter(notGroupedType); - var innerMemberExpression = ResolvePathForExpression(innerParameter, path); - var innerMemberLambda = Expression.Lambda(innerMemberExpression, innerParameter); - var body = Expression.Call(typeof(Enumerable), "Min", new[] { notGroupedType }, parameter, innerMemberLambda); - return body; - } + return CreateGroupedAggregateExpression(parameter, path, "Min"); else if (selectType == SelectTypes.Max) - { - var notGroupedType = parameter.Type.GenericTypeArguments[1]; - var innerParameter = Expression.Parameter(notGroupedType); - var innerMemberExpression = ResolvePathForExpression(innerParameter, path); - var innerMemberLambda = Expression.Lambda(innerMemberExpression, innerParameter); - var body = Expression.Call(typeof(Enumerable), "Max", new[] { notGroupedType }, parameter, innerMemberLambda); - return body; - } + return CreateGroupedAggregateExpression(parameter, path, "Max"); else if (selectType == SelectTypes.ToList) - { - var notGroupedType = parameter.Type.GenericTypeArguments[1]; - var body = Expression.Call(typeof(Enumerable), "ToList", new[] { notGroupedType }, parameter); - return body; - } + return CreateGroupedPathExpressionWithMethod(parameter, path, selectCollectionHandling, nullChecking, "ToList"); else if (selectType == SelectTypes.First) - { - var notGroupedType = parameter.Type.GenericTypeArguments[1]; - var body = Expression.Call(typeof(Enumerable), "First", new[] { notGroupedType }, parameter); - return body; - } + return CreateGroupedPathExpressionWithMethod(parameter, path, selectCollectionHandling, nullChecking, "First"); else if (selectType == SelectTypes.Last) - { - var notGroupedType = parameter.Type.GenericTypeArguments[1]; - var body = Expression.Call(typeof(Enumerable), "Last", new[] { notGroupedType }, parameter); - return body; - } + return CreateGroupedPathExpressionWithMethod(parameter, path, selectCollectionHandling, nullChecking, "Last"); else if (selectType == SelectTypes.FirstOrDefault) - { - var notGroupedType = parameter.Type.GenericTypeArguments[1]; - var body = Expression.Call(typeof(Enumerable), "FirstOrDefault", new[] { notGroupedType }, parameter); - return body; - } + return CreateGroupedPathExpressionWithMethod(parameter, path, selectCollectionHandling, nullChecking, "FirstOrDefault"); else if (selectType == SelectTypes.LastOrDefault) - { - var notGroupedType = parameter.Type.GenericTypeArguments[1]; - var body = Expression.Call(typeof(Enumerable), "LastOrDefault", new[] { notGroupedType }, parameter); - return body; - } + return CreateGroupedPathExpressionWithMethod(parameter, path, selectCollectionHandling, nullChecking, "LastOrDefault"); throw new NotSupportedException($"unkown select type {selectType}"); } + private static Expression CreateGroupedAggregateExpression(ParameterExpression parameter, string path, string methodName) + { + /// https://stackoverflow.com/questions/25482097/call-enumerable-average-via-expression + var notGroupedType = parameter.Type.GenericTypeArguments[1]; + var innerParameter = Expression.Parameter(notGroupedType); + var innerMemberExpression = ResolvePathForExpression(innerParameter, path); + var innerMemberLambda = Expression.Lambda(innerMemberExpression, innerParameter); + var body = Expression.Call(typeof(Enumerable), methodName, new[] { notGroupedType }, parameter, innerMemberLambda); + return body; + } + + private static Expression CreateGroupedPathExpressionWithMethod(ParameterExpression parameter, string path, SelectCollectionHandling selectCollectionHandling, bool nullChecking, string methodName) + { + if (string.IsNullOrWhiteSpace(path)) + { + var notGroupedType = parameter.Type.GenericTypeArguments[1]; + var body = Expression.Call(typeof(Enumerable), methodName, new[] { notGroupedType }, parameter); + return body; + } + else + { + var notGroupedType = parameter.Type.GenericTypeArguments[1]; + var innerParameter = Expression.Parameter(notGroupedType); + var parser = new ExpressionParser(innerParameter, path); + var resolver = new PathExpressionResolver(parser); + resolver.NullChecking = nullChecking; + resolver.Resolve(); + var expression = resolver.Result; + var selectExpression = WrapIntoSelectFromGrouping(parameter, expression, selectCollectionHandling); + var body = CallMethodOnSelectExpression(methodName, selectExpression); + return body; + } + } + + private static Expression WrapIntoSelectFromGrouping(ParameterExpression parameter, Expression innerLambdaExpression, SelectCollectionHandling selectCollectionHandling) + { + var selectType = parameter.Type.GenericTypeArguments.Skip(1).First(); + var innerSelectType = ((LambdaExpression)innerLambdaExpression).ReturnType; + var selectExpression = Expression.Call(typeof(Enumerable), "Select", new Type[] { selectType, innerSelectType }, parameter, innerLambdaExpression); + return selectExpression; + } + private static Expression CreateSelectExpression(IQueryable query, ParameterExpression parameter, SelectTypes selectType, string path, SelectCollectionHandling selectCollectionHandling, bool nullChecking) { if (!IsGrouping(query)) @@ -277,6 +274,13 @@ namespace PoweredSoft.DynamicLinq.Helpers return body; } + private static Expression CallMethodOnSelectExpression(string methodName, Expression selectExpression) + { + var notGroupedType = selectExpression.Type.GenericTypeArguments.First(); + var body = Expression.Call(typeof(Enumerable), methodName, new[] { notGroupedType }, selectExpression) as Expression; + return body; + } + private static bool IsGrouping(IQueryable query) { // TODO needs to be alot better than this, but it will do for now. From a7937cd500f684d51e10c160a0c796313a807cd0 Mon Sep 17 00:00:00 2001 From: David Lebee Date: Fri, 26 Oct 2018 16:13:42 -0500 Subject: [PATCH 2/3] flattening support on wrap select. --- PoweredSoft.DynamicLinq.Test/GroupingTests.cs | 5 +++-- PoweredSoft.DynamicLinq/Helpers/QueryableHelpers.cs | 10 ++++++++-- .../Helpers/QueryablePathHelpers.cs | 12 ------------ 3 files changed, 11 insertions(+), 16 deletions(-) delete mode 100644 PoweredSoft.DynamicLinq/Helpers/QueryablePathHelpers.cs diff --git a/PoweredSoft.DynamicLinq.Test/GroupingTests.cs b/PoweredSoft.DynamicLinq.Test/GroupingTests.cs index 7ea08ea..5749bd8 100644 --- a/PoweredSoft.DynamicLinq.Test/GroupingTests.cs +++ b/PoweredSoft.DynamicLinq.Test/GroupingTests.cs @@ -9,6 +9,7 @@ using PoweredSoft.DynamicLinq.Dal; using System.Diagnostics; using PoweredSoft.DynamicLinq.Test.Helpers; using System.Collections; +using PoweredSoft.DynamicLinq.Dal.Pocos; namespace PoweredSoft.DynamicLinq.Test { @@ -289,7 +290,7 @@ namespace PoweredSoft.DynamicLinq.Test } [TestMethod] - public void GroupByToListWithPathWithNullChecking() + public void GroupByToListWithPathWithNullCheckingWithFlattening() { var limitResult = TestData.Authors; @@ -300,7 +301,7 @@ namespace PoweredSoft.DynamicLinq.Test .Select(t => new { Key = t.Key.AuthorFirstName, - Contents = t.SelectMany(t2 => t2.Posts.Select(t3 => t3.Content)).ToList() + Contents = t.SelectMany(t2 => t2.Posts == null ? new List() : t2.Posts.Select(t3 => t3.Content)).ToList() }) .ToList(); diff --git a/PoweredSoft.DynamicLinq/Helpers/QueryableHelpers.cs b/PoweredSoft.DynamicLinq/Helpers/QueryableHelpers.cs index d16cef1..d9985a6 100644 --- a/PoweredSoft.DynamicLinq/Helpers/QueryableHelpers.cs +++ b/PoweredSoft.DynamicLinq/Helpers/QueryableHelpers.cs @@ -220,10 +220,16 @@ namespace PoweredSoft.DynamicLinq.Helpers { var selectType = parameter.Type.GenericTypeArguments.Skip(1).First(); var innerSelectType = ((LambdaExpression)innerLambdaExpression).ReturnType; - var selectExpression = Expression.Call(typeof(Enumerable), "Select", new Type[] { selectType, innerSelectType }, parameter, innerLambdaExpression); + + Expression selectExpression; + if (QueryableHelpers.IsGenericEnumerable(innerSelectType) && selectCollectionHandling == SelectCollectionHandling.Flatten) + selectExpression = Expression.Call(typeof(Enumerable), "SelectMany", new Type[] { selectType, innerSelectType.GenericTypeArguments.First() }, parameter, innerLambdaExpression); + else + selectExpression = Expression.Call(typeof(Enumerable), "Select", new Type[] { selectType, innerSelectType }, parameter, innerLambdaExpression); + return selectExpression; } - + private static Expression CreateSelectExpression(IQueryable query, ParameterExpression parameter, SelectTypes selectType, string path, SelectCollectionHandling selectCollectionHandling, bool nullChecking) { if (!IsGrouping(query)) diff --git a/PoweredSoft.DynamicLinq/Helpers/QueryablePathHelpers.cs b/PoweredSoft.DynamicLinq/Helpers/QueryablePathHelpers.cs deleted file mode 100644 index d5b6a9a..0000000 --- a/PoweredSoft.DynamicLinq/Helpers/QueryablePathHelpers.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Collections.Generic; -using System.Linq.Expressions; -using System.Text; - -namespace PoweredSoft.DynamicLinq.Helpers -{ - - public static class QueryablePathHelpers - { - - } -} From de89d82f7dbda536457630dea769555b6e8b2ca3 Mon Sep 17 00:00:00 2001 From: David Lebee Date: Fri, 26 Oct 2018 16:17:15 -0500 Subject: [PATCH 3/3] ready for release. --- .../PoweredSoft.DynamicLinq.EntityFramework.csproj | 2 +- PoweredSoft.DynamicLinq/PoweredSoft.DynamicLinq.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/PoweredSoft.DynamicLinq.EntityFramework/PoweredSoft.DynamicLinq.EntityFramework.csproj b/PoweredSoft.DynamicLinq.EntityFramework/PoweredSoft.DynamicLinq.EntityFramework.csproj index 4f6c1e3..0144242 100644 --- a/PoweredSoft.DynamicLinq.EntityFramework/PoweredSoft.DynamicLinq.EntityFramework.csproj +++ b/PoweredSoft.DynamicLinq.EntityFramework/PoweredSoft.DynamicLinq.EntityFramework.csproj @@ -10,7 +10,7 @@ https://github.com/PoweredSoft/DynamicLinq entity framework ef dynamic linq https://github.com/PoweredSoft/DynamicLinq - 1.1.6 + 1.1.7 1.1.6.0 1.1.6.0 diff --git a/PoweredSoft.DynamicLinq/PoweredSoft.DynamicLinq.csproj b/PoweredSoft.DynamicLinq/PoweredSoft.DynamicLinq.csproj index 3115486..a005c1b 100644 --- a/PoweredSoft.DynamicLinq/PoweredSoft.DynamicLinq.csproj +++ b/PoweredSoft.DynamicLinq/PoweredSoft.DynamicLinq.csproj @@ -13,7 +13,7 @@ dynamic linq queryable 1.1.6.0 1.1.6.0 - 1.1.6 + 1.1.7 https://github.com/PoweredSoft/DynamicLinq