diff --git a/PoweredSoft.DynamicLinq.Test/GroupingTests.cs b/PoweredSoft.DynamicLinq.Test/GroupingTests.cs index 9c694cf..48a8a69 100644 --- a/PoweredSoft.DynamicLinq.Test/GroupingTests.cs +++ b/PoweredSoft.DynamicLinq.Test/GroupingTests.cs @@ -8,6 +8,7 @@ using PoweredSoft.DynamicLinq; using PoweredSoft.DynamicLinq.Dal; using System.Diagnostics; using PoweredSoft.DynamicLinq.Test.Helpers; +using System.Collections; namespace PoweredSoft.DynamicLinq.Test { @@ -149,5 +150,84 @@ namespace PoweredSoft.DynamicLinq.Test QueryableAssert.AreEqual(left.Sales.AsQueryable(), right.GetDynamicPropertyValue>("Sales").AsQueryable()); } } + + [TestMethod] + public void GroupWithoutNullCheckComplex() + { + var limitResult = TestData.Authors.Where(t => t.Posts != null).AsQueryable(); + + var posts = limitResult + .GroupBy(t => new + { + Titles = t.Posts.Select(t2 => t2.Title) + }) + .Select(t => new + { + Titles = t.Key.Titles, + Data = t.ToList() + }) + .ToList(); + + var posts2 = limitResult + .GroupBy(gb => gb.Path("Posts.Title", "Titles")) + .Select(sb => + { + sb.Key("Titles"); + sb.ToList("Data"); + }) + .ToDynamicClassList(); + + Assert.AreEqual(posts.Count, posts2.Count); + for(var i = 0; i < posts.Count; i++) + { + var expected = posts[0]; + var actual = posts2[0]; + + var titles = actual.GetDynamicPropertyValue("Titles") as ICollection; + + CollectionAssert.AreEqual(expected.Titles as ICollection, titles); + } + } + + [TestMethod] + public void GroupWithNullCheckComplex() + { + var limitResult = TestData.Authors.AsQueryable(); + + var posts = limitResult + .GroupBy(t => new + { + Titles = t.Posts == null ? new List() : t.Posts.Select(t2 => t2.Title) + }) + .Select(t => new + { + Titles = t.Key.Titles, + Data = t.ToList() + }) + .ToList(); + + var tempQueryable = limitResult + .GroupBy(gb => gb.NullChecking().Path("Posts.Title", "Titles")); + + + var posts2 = tempQueryable + .Select(sb => + { + sb.Key("Titles"); + sb.ToList("Data"); + }) + .ToDynamicClassList(); + + Assert.AreEqual(posts.Count, posts2.Count); + for (var i = 0; i < posts.Count; i++) + { + var expected = posts[0]; + var actual = posts2[0]; + + var titles = actual.GetDynamicPropertyValue("Titles") as ICollection; + + CollectionAssert.AreEqual(expected.Titles as ICollection, titles); + } + } } } diff --git a/PoweredSoft.DynamicLinq/Fluent/Group/GroupBuilder.cs b/PoweredSoft.DynamicLinq/Fluent/Group/GroupBuilder.cs index 64a5bcf..6de733f 100644 --- a/PoweredSoft.DynamicLinq/Fluent/Group/GroupBuilder.cs +++ b/PoweredSoft.DynamicLinq/Fluent/Group/GroupBuilder.cs @@ -15,6 +15,8 @@ namespace PoweredSoft.DynamicLinq.Fluent public IQueryable Query { get; protected set; } + public bool IsNullCheckingEnabled { get; protected set; } = false; + public GroupBuilder(IQueryable query) { Query = query; @@ -41,6 +43,12 @@ namespace PoweredSoft.DynamicLinq.Fluent return this; } + public GroupBuilder NullChecking(bool nullChecking = true) + { + IsNullCheckingEnabled = nullChecking; + return this; + } + public GroupBuilder UseType(Type type) { Type = type; @@ -58,7 +66,7 @@ namespace PoweredSoft.DynamicLinq.Fluent if (Empty) throw new Exception("No group specified, please specify at least one group"); - var ret = QueryableHelpers.GroupBy(Query, Query.ElementType, Parts, Type, EqualityComparerType); + var ret = QueryableHelpers.GroupBy(Query, Query.ElementType, Parts, groupToType: Type, equalityCompareType: EqualityComparerType, nullChecking: IsNullCheckingEnabled); return ret; } } diff --git a/PoweredSoft.DynamicLinq/Helpers/QueryableHelpers.cs b/PoweredSoft.DynamicLinq/Helpers/QueryableHelpers.cs index 64d6f96..2a65fb9 100644 --- a/PoweredSoft.DynamicLinq/Helpers/QueryableHelpers.cs +++ b/PoweredSoft.DynamicLinq/Helpers/QueryableHelpers.cs @@ -83,7 +83,7 @@ namespace PoweredSoft.DynamicLinq.Helpers - public static IQueryable GroupBy(IQueryable query, Type type, List<(string path, string propertyName)> parts, Type groupToType = null, Type equalityCompareType = null) + public static IQueryable GroupBy(IQueryable query, Type type, List<(string path, string propertyName)> parts, Type groupToType = null, Type equalityCompareType = null, bool nullChecking = false) { // EXPRESSION var parameter = Expression.Parameter(type, "t"); @@ -94,22 +94,12 @@ namespace PoweredSoft.DynamicLinq.Helpers // resolve part expression and create the fields inside the anonymous type. parts.ForEach(part => { - var partExpression = ResolvePathForExpression(parameter, part.path); + var partExpression = CreateSelectExpression(query, parameter, SelectTypes.Path, part.path, SelectCollectionHandling.LeaveAsIs, nullChecking: nullChecking); fields.Add((partExpression.Type, part.propertyName)); partExpressions.Add((partExpression, part.propertyName)); }); var keyType = groupToType ?? DynamicClassFactory.CreateType(fields); - - /* - var constructorTypes = fields.Select(t => t.type).ToArray(); - var constructor = anonymousType.GetConstructor(constructorTypes); - var newExpression = Expression.New(constructor, partExpressions); - var genericMethod = Constants.GroupByMethod.MakeGenericMethod(type, anonymousType); - var lambda = Expression.Lambda(newExpression, parameter); - var groupByExpression = Expression.Call(genericMethod, query.Expression, lambda); - var result = query.Provider.CreateQuery(groupByExpression);*/ - var ctor = Expression.New(keyType); var bindings = partExpressions.Select(partExpression => Expression.Bind(keyType.GetProperty(partExpression.propertyName), partExpression.expression)).ToArray(); var mi = Expression.MemberInit(ctor, bindings);