From 7269071a3a70d0428d5bd5a4d95314e1dacea250 Mon Sep 17 00:00:00 2001 From: David Lebee Date: Thu, 25 Oct 2018 18:40:30 -0500 Subject: [PATCH 1/5] support null checking on group :) --- PoweredSoft.DynamicLinq.Test/GroupingTests.cs | 80 +++++++++++++++++++ .../Fluent/Group/GroupBuilder.cs | 10 ++- .../Helpers/QueryableHelpers.cs | 14 +--- 3 files changed, 91 insertions(+), 13 deletions(-) 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); From dcaf114a091a02f7be2e9ae4d06d9d9755639e66 Mon Sep 17 00:00:00 2001 From: David Lebee Date: Thu, 25 Oct 2018 20:02:17 -0500 Subject: [PATCH 2/5] adding lots more select types support. --- PoweredSoft.DynamicLinq.Test/GroupingTests.cs | 22 ++- PoweredSoft.DynamicLinq/Constants.cs | 8 +- .../Fluent/Select/SelectBuilder.cs | 139 +++--------------- .../Helpers/QueryableHelpers.cs | 42 ++++++ 4 files changed, 92 insertions(+), 119 deletions(-) diff --git a/PoweredSoft.DynamicLinq.Test/GroupingTests.cs b/PoweredSoft.DynamicLinq.Test/GroupingTests.cs index 48a8a69..ad2543c 100644 --- a/PoweredSoft.DynamicLinq.Test/GroupingTests.cs +++ b/PoweredSoft.DynamicLinq.Test/GroupingTests.cs @@ -73,7 +73,13 @@ namespace PoweredSoft.DynamicLinq.Test LongCount = t.LongCount(), NetSales = t.Sum(t2 => t2.NetSales), TaxAverage = t.Average(t2 => t2.Tax), - Sales = t.ToList() + Sales = t.ToList(), + MaxNetSales = t.Max(t2 => t2.NetSales), + MinNetSales = t.Min(t2 => t2.NetSales), + First = t.First(), + Last = t.Last(), + FirstOrDefault = t.FirstOrDefault(), + LastOrDefault = t.LastOrDefault() }) .ToList(); @@ -87,6 +93,12 @@ namespace PoweredSoft.DynamicLinq.Test t.LongCount("LongCount"); t.Sum("NetSales"); t.Average("Tax", "TaxAverage"); + t.Max("NetSales", "MaxNetSales"); + t.Min("NetSales", "MinNetSales"); + t.First("First"); + t.Last("Last"); + t.FirstOrDefault("FirstOrDefault"); + t.LastOrDefault("LastOrDefault"); t.ToList("Sales"); }) .ToDynamicClassList(); @@ -101,6 +113,14 @@ namespace PoweredSoft.DynamicLinq.Test Assert.AreEqual(left.Count, right.GetDynamicPropertyValue("Count")); Assert.AreEqual(left.LongCount, right.GetDynamicPropertyValue("LongCount")); Assert.AreEqual(left.TaxAverage, right.GetDynamicPropertyValue("TaxAverage")); + Assert.AreEqual(left.MinNetSales, right.GetDynamicPropertyValue("MinNetSales")); + Assert.AreEqual(left.MaxNetSales, right.GetDynamicPropertyValue("MaxNetSales")); + + Assert.AreEqual(left.First, right.GetDynamicPropertyValue("First")); + Assert.AreEqual(left.FirstOrDefault, right.GetDynamicPropertyValue("FirstOrDefault")); + Assert.AreEqual(left.Last, right.GetDynamicPropertyValue("Last")); + Assert.AreEqual(left.LastOrDefault, right.GetDynamicPropertyValue("LastOrDefault")); + QueryableAssert.AreEqual(left.Sales.AsQueryable(), right.GetDynamicPropertyValue>("Sales").AsQueryable()); } } diff --git a/PoweredSoft.DynamicLinq/Constants.cs b/PoweredSoft.DynamicLinq/Constants.cs index 7b38f59..0364abb 100644 --- a/PoweredSoft.DynamicLinq/Constants.cs +++ b/PoweredSoft.DynamicLinq/Constants.cs @@ -50,7 +50,13 @@ namespace PoweredSoft.DynamicLinq Average, ToList, PathToList, - Path + Path, + Min, + Max, + LastOrDefault, + FirstOrDefault, + Last, + First } public enum SelectCollectionHandling diff --git a/PoweredSoft.DynamicLinq/Fluent/Select/SelectBuilder.cs b/PoweredSoft.DynamicLinq/Fluent/Select/SelectBuilder.cs index 6f7a780..b84f30c 100644 --- a/PoweredSoft.DynamicLinq/Fluent/Select/SelectBuilder.cs +++ b/PoweredSoft.DynamicLinq/Fluent/Select/SelectBuilder.cs @@ -42,25 +42,11 @@ namespace PoweredSoft.DynamicLinq.Fluent throw new Exception($"{propertyName} is already used"); } - public SelectBuilder Key(string propertyName, string path = null) + public SelectBuilder Aggregate(string path, SelectTypes type, string propertyName = null, SelectCollectionHandling selectCollectionHandling = SelectCollectionHandling.LeaveAsIs) { - if (propertyName == null) - propertyName = path.Split('.').LastOrDefault(); + if (propertyName == null && path == null) + throw new Exception("if property name is not specified, a path must be supplied."); - ThrowIfUsedOrEmpty(propertyName); - - Parts.Add(new SelectPart - { - Path = path == null ? "Key" : $"Key.{path}", - PropertyName = propertyName, - SelectType = SelectTypes.Key - }); - - return this; - } - - public SelectBuilder Aggregate(string path, SelectTypes type, string propertyName = null) - { if (propertyName == null) propertyName = path.Split('.').LastOrDefault(); @@ -70,112 +56,31 @@ namespace PoweredSoft.DynamicLinq.Fluent { Path = path, PropertyName = propertyName, - SelectType = type - }); - - return this; - } - - public SelectBuilder Path(string path, string propertyName = null) - { - if (propertyName == null) - propertyName = path.Split('.').LastOrDefault(); - - ThrowIfUsedOrEmpty(propertyName); - - Parts.Add(new SelectPart - { - Path = path, - PropertyName = propertyName, - SelectType = SelectTypes.Path - }); - - return this; - } - - public SelectBuilder Count(string propertyName) - { - ThrowIfUsedOrEmpty(propertyName); - Parts.Add(new SelectPart - { - PropertyName = propertyName, - SelectType = SelectTypes.Count - }); - return this; - } - - public SelectBuilder LongCount(string propertyName) - { - ThrowIfUsedOrEmpty(propertyName); - Parts.Add(new SelectPart - { - PropertyName = propertyName, - SelectType = SelectTypes.LongCount - }); - return this; - } - - public SelectBuilder Sum(string path, string propertyName = null) - { - if (propertyName == null) - propertyName = path.Split('.').LastOrDefault(); - - ThrowIfUsedOrEmpty(propertyName); - - Parts.Add(new SelectPart - { - Path = path, - PropertyName = propertyName, - SelectType = SelectTypes.Sum - }); - return this; - } - - public SelectBuilder Average(string path, string propertyName = null) - { - if (propertyName == null) - propertyName = path.Split('.').LastOrDefault(); - - ThrowIfUsedOrEmpty(propertyName); - - Parts.Add(new SelectPart - { - Path = path, - PropertyName = propertyName, - SelectType = SelectTypes.Average - }); - return this; - } - - public SelectBuilder ToList(string propertyName) - { - ThrowIfUsedOrEmpty(propertyName); - Parts.Add(new SelectPart - { - PropertyName = propertyName, - SelectType = SelectTypes.ToList - }); - return this; - } - - public SelectBuilder PathToList(string path, string propertyName = null, SelectCollectionHandling selectCollectionHandling = SelectCollectionHandling.LeaveAsIs) - { - if (propertyName == null) - propertyName = path.Split('.').LastOrDefault(); - - ThrowIfUsedOrEmpty(propertyName); - - Parts.Add(new SelectPart - { - Path = path, - PropertyName = propertyName, - SelectType = SelectTypes.PathToList, + SelectType = type, SelectCollectionHandling = selectCollectionHandling }); return this; } + public SelectBuilder Key(string propertyName, string path = null) => Aggregate(path == null ? "Key" : $"Key.{path}", SelectTypes.Key, propertyName); + public SelectBuilder Path(string path, string propertyName = null) => Aggregate(path, SelectTypes.Path, propertyName); + public SelectBuilder Count(string propertyName) => Aggregate(null, SelectTypes.Count, propertyName); + public SelectBuilder LongCount(string propertyName) => Aggregate(null, SelectTypes.LongCount, propertyName); + public SelectBuilder Sum(string path, string propertyName = null) => Aggregate(path, SelectTypes.Sum, propertyName); + public SelectBuilder Average(string path, string propertyName = null) => Aggregate(path, SelectTypes.Average, propertyName); + public void Min(string path, string propertyName = null) => Aggregate(path, SelectTypes.Min, propertyName); + public void Max(string path, string propertyName = null) => Aggregate(path, SelectTypes.Max, propertyName); + public SelectBuilder ToList(string propertyName) => Aggregate(null, SelectTypes.ToList, propertyName); + + public SelectBuilder PathToList(string path, string propertyName = null, SelectCollectionHandling selectCollectionHandling = SelectCollectionHandling.LeaveAsIs) + => Aggregate(path, SelectTypes.PathToList, propertyName: propertyName, selectCollectionHandling: selectCollectionHandling); + + public void LastOrDefault(string propertyName) => Aggregate(null, SelectTypes.LastOrDefault, propertyName); + public void FirstOrDefault(string propertyName) => Aggregate(null, SelectTypes.FirstOrDefault, propertyName); + public void Last(string propertyName) => Aggregate(null, SelectTypes.Last, propertyName); + public void First(string propertyName) => Aggregate(null, SelectTypes.First, propertyName); + public virtual IQueryable Build() { if (Empty) diff --git a/PoweredSoft.DynamicLinq/Helpers/QueryableHelpers.cs b/PoweredSoft.DynamicLinq/Helpers/QueryableHelpers.cs index 2a65fb9..ace8133 100644 --- a/PoweredSoft.DynamicLinq/Helpers/QueryableHelpers.cs +++ b/PoweredSoft.DynamicLinq/Helpers/QueryableHelpers.cs @@ -175,12 +175,54 @@ namespace PoweredSoft.DynamicLinq.Helpers var body = Expression.Call(typeof(Enumerable), "Sum", new[] { notGroupedType }, parameter, innerMemberLambda); return body; } + 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; + } + 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; + } else if (selectType == SelectTypes.ToList) { var notGroupedType = parameter.Type.GenericTypeArguments[1]; var body = Expression.Call(typeof(Enumerable), "ToList", new[] { notGroupedType }, parameter); return body; } + else if (selectType == SelectTypes.First) + { + var notGroupedType = parameter.Type.GenericTypeArguments[1]; + var body = Expression.Call(typeof(Enumerable), "First", new[] { notGroupedType }, parameter); + return body; + } + else if (selectType == SelectTypes.Last) + { + var notGroupedType = parameter.Type.GenericTypeArguments[1]; + var body = Expression.Call(typeof(Enumerable), "Last", new[] { notGroupedType }, parameter); + return body; + } + else if (selectType == SelectTypes.FirstOrDefault) + { + var notGroupedType = parameter.Type.GenericTypeArguments[1]; + var body = Expression.Call(typeof(Enumerable), "FirstOrDefault", new[] { notGroupedType }, parameter); + return body; + } + else if (selectType == SelectTypes.LastOrDefault) + { + var notGroupedType = parameter.Type.GenericTypeArguments[1]; + var body = Expression.Call(typeof(Enumerable), "LastOrDefault", new[] { notGroupedType }, parameter); + return body; + } throw new NotSupportedException($"unkown select type {selectType}"); } From 695d3085d17448bfd9bc43960e8d5728f2b72238 Mon Sep 17 00:00:00 2001 From: David Lebee Date: Thu, 25 Oct 2018 21:01:44 -0500 Subject: [PATCH 3/5] added support for lots of new aggregate helpers. first breaking change but I think people will be okay with it :) --- PoweredSoft.DynamicLinq.Test/SelectTests.cs | 39 ++++++++++++++++-- PoweredSoft.DynamicLinq/Constants.cs | 3 +- .../Fluent/Select/SelectBuilder.cs | 27 +++++++++---- .../Helpers/QueryableHelpers.cs | 40 ++++++++++++------- README.md | 4 +- 5 files changed, 84 insertions(+), 29 deletions(-) diff --git a/PoweredSoft.DynamicLinq.Test/SelectTests.cs b/PoweredSoft.DynamicLinq.Test/SelectTests.cs index 36bd62f..e966e48 100644 --- a/PoweredSoft.DynamicLinq.Test/SelectTests.cs +++ b/PoweredSoft.DynamicLinq.Test/SelectTests.cs @@ -47,7 +47,16 @@ namespace PoweredSoft.DynamicLinq.Test { Id = t.Id, FirstNames = t.Bs.SelectMany(t2 => t2.FirstNames).ToList(), - FirstNamesLists = t.Bs.Select(t2 => t2.FirstNames).ToList() + FirstNamesLists = t.Bs.Select(t2 => t2.FirstNames).ToList(), + FirstFirstName = t.Bs.SelectMany(t2 => t2.FirstNames).First(), + FirstOrDefaultFirstName = t.Bs.SelectMany(t2 => t2.FirstNames).FirstOrDefault(), + LastFirstName = t.Bs.SelectMany(t2 => t2.FirstNames).Last(), + LastOrDefaultFirstName = t.Bs.SelectMany(t2 => t2.FirstNames).LastOrDefault(), + + FirstFirstNameList = t.Bs.Select(t2 => t2.FirstNames).First(), + FirstOrDefaultFirstNameList = t.Bs.Select(t2 => t2.FirstNames).FirstOrDefault(), + LastFirstNameList = t.Bs.Select(t2 => t2.FirstNames).Last(), + LastOrDefaultFirstNameList = t.Bs.Select(t2 => t2.FirstNames).LastOrDefault() }); var regularSyntax = regularSyntaxA.ToList(); @@ -57,8 +66,17 @@ namespace PoweredSoft.DynamicLinq.Test .Select(t => { t.Path("Id"); - t.PathToList("Bs.FirstNames", "FirstNames", SelectCollectionHandling.Flatten); - t.PathToList("Bs.FirstNames", "FirstNamesLists", SelectCollectionHandling.LeaveAsIs); + t.ToList("Bs.FirstNames", "FirstNames", SelectCollectionHandling.Flatten); + t.ToList("Bs.FirstNames", "FirstNamesLists", SelectCollectionHandling.LeaveAsIs); + t.First("Bs.FirstNames", "FirstFirstName", SelectCollectionHandling.Flatten); + t.FirstOrDefault("Bs.FirstNames", "FirstOrDefaultFirstName", SelectCollectionHandling.Flatten); + t.Last("Bs.FirstNames", "LastFirstName", SelectCollectionHandling.Flatten); + t.LastOrDefault("Bs.FirstNames", "LastOrDefaultFirstName", SelectCollectionHandling.Flatten); + + t.First("Bs.FirstNames", "FirstFirstNameList", SelectCollectionHandling.LeaveAsIs); + t.FirstOrDefault("Bs.FirstNames", "FirstOrDefaultFirstNameList", SelectCollectionHandling.LeaveAsIs); + t.Last("Bs.FirstNames", "LastFirstNameList", SelectCollectionHandling.LeaveAsIs); + t.LastOrDefault("Bs.FirstNames", "LastOrDefaultFirstNameList", SelectCollectionHandling.LeaveAsIs); }) .ToDynamicClassList(); @@ -66,9 +84,22 @@ namespace PoweredSoft.DynamicLinq.Test for(var i = 0; i < regularSyntax.Count; i++) { Assert.AreEqual(regularSyntax[i].Id, dynamicSyntax[i].GetDynamicPropertyValue("Id")); + Assert.AreEqual(regularSyntax[i].FirstFirstName, dynamicSyntax[i].GetDynamicPropertyValue("FirstFirstName")); + Assert.AreEqual(regularSyntax[i].FirstOrDefaultFirstName, dynamicSyntax[i].GetDynamicPropertyValue("FirstOrDefaultFirstName")); + Assert.AreEqual(regularSyntax[i].LastFirstName, dynamicSyntax[i].GetDynamicPropertyValue("LastFirstName")); + Assert.AreEqual(regularSyntax[i].LastOrDefaultFirstName, dynamicSyntax[i].GetDynamicPropertyValue("LastOrDefaultFirstName")); + + CollectionAssert.AreEqual(regularSyntax[i].FirstFirstNameList, dynamicSyntax[i].GetDynamicPropertyValue>("FirstFirstNameList")); + CollectionAssert.AreEqual(regularSyntax[i].FirstOrDefaultFirstNameList, dynamicSyntax[i].GetDynamicPropertyValue>("FirstOrDefaultFirstNameList")); + CollectionAssert.AreEqual(regularSyntax[i].LastFirstNameList, dynamicSyntax[i].GetDynamicPropertyValue>("LastFirstNameList")); + CollectionAssert.AreEqual(regularSyntax[i].LastOrDefaultFirstNameList, dynamicSyntax[i].GetDynamicPropertyValue>("LastOrDefaultFirstNameList")); + + QueryableAssert.AreEqual(regularSyntax[i].FirstNames.AsQueryable(), dynamicSyntax[i].GetDynamicPropertyValue>("FirstNames").AsQueryable()); + + var left = regularSyntax[i].FirstNamesLists; var right = dynamicSyntax[i].GetDynamicPropertyValue>>("FirstNamesLists"); Assert.AreEqual(left.Count, right.Count); @@ -95,7 +126,7 @@ namespace PoweredSoft.DynamicLinq.Test var querySelect = query.Select(t => { t.NullChecking(true); - t.PathToList("Posts.Comments.CommentLikes", selectCollectionHandling: SelectCollectionHandling.Flatten); + t.ToList("Posts.Comments.CommentLikes", selectCollectionHandling: SelectCollectionHandling.Flatten); }); var b = querySelect.ToDynamicClassList(); diff --git a/PoweredSoft.DynamicLinq/Constants.cs b/PoweredSoft.DynamicLinq/Constants.cs index 0364abb..8e5c05e 100644 --- a/PoweredSoft.DynamicLinq/Constants.cs +++ b/PoweredSoft.DynamicLinq/Constants.cs @@ -49,14 +49,13 @@ namespace PoweredSoft.DynamicLinq Sum, Average, ToList, - PathToList, Path, Min, Max, LastOrDefault, FirstOrDefault, Last, - First + First, } public enum SelectCollectionHandling diff --git a/PoweredSoft.DynamicLinq/Fluent/Select/SelectBuilder.cs b/PoweredSoft.DynamicLinq/Fluent/Select/SelectBuilder.cs index b84f30c..18b2d23 100644 --- a/PoweredSoft.DynamicLinq/Fluent/Select/SelectBuilder.cs +++ b/PoweredSoft.DynamicLinq/Fluent/Select/SelectBuilder.cs @@ -69,17 +69,28 @@ namespace PoweredSoft.DynamicLinq.Fluent public SelectBuilder LongCount(string propertyName) => Aggregate(null, SelectTypes.LongCount, propertyName); public SelectBuilder Sum(string path, string propertyName = null) => Aggregate(path, SelectTypes.Sum, propertyName); public SelectBuilder Average(string path, string propertyName = null) => Aggregate(path, SelectTypes.Average, propertyName); - public void Min(string path, string propertyName = null) => Aggregate(path, SelectTypes.Min, propertyName); - public void Max(string path, string propertyName = null) => Aggregate(path, SelectTypes.Max, propertyName); + public SelectBuilder Min(string path, string propertyName = null) => Aggregate(path, SelectTypes.Min, propertyName); + public SelectBuilder Max(string path, string propertyName = null) => Aggregate(path, SelectTypes.Max, propertyName); public SelectBuilder ToList(string propertyName) => Aggregate(null, SelectTypes.ToList, propertyName); + public SelectBuilder LastOrDefault(string propertyName) => Aggregate(null, SelectTypes.LastOrDefault, propertyName); + public SelectBuilder FirstOrDefault(string propertyName) => Aggregate(null, SelectTypes.FirstOrDefault, propertyName); + public SelectBuilder Last(string propertyName) => Aggregate(null, SelectTypes.Last, propertyName); + public SelectBuilder First(string propertyName) => Aggregate(null, SelectTypes.First, propertyName); - public SelectBuilder PathToList(string path, string propertyName = null, SelectCollectionHandling selectCollectionHandling = SelectCollectionHandling.LeaveAsIs) - => Aggregate(path, SelectTypes.PathToList, propertyName: propertyName, selectCollectionHandling: selectCollectionHandling); + public SelectBuilder ToList(string path, string propertyName = null, SelectCollectionHandling selectCollectionHandling = SelectCollectionHandling.LeaveAsIs) + => Aggregate(path, SelectTypes.ToList, propertyName: propertyName, selectCollectionHandling: selectCollectionHandling); - public void LastOrDefault(string propertyName) => Aggregate(null, SelectTypes.LastOrDefault, propertyName); - public void FirstOrDefault(string propertyName) => Aggregate(null, SelectTypes.FirstOrDefault, propertyName); - public void Last(string propertyName) => Aggregate(null, SelectTypes.Last, propertyName); - public void First(string propertyName) => Aggregate(null, SelectTypes.First, propertyName); + public SelectBuilder First(string path, string propertyName = null, SelectCollectionHandling selectCollectionHandling = SelectCollectionHandling.LeaveAsIs) + => Aggregate(path, SelectTypes.First, propertyName: propertyName, selectCollectionHandling: selectCollectionHandling); + + public SelectBuilder FirstOrDefault(string path, string propertyName = null, SelectCollectionHandling selectCollectionHandling = SelectCollectionHandling.LeaveAsIs) + => Aggregate(path, SelectTypes.FirstOrDefault, propertyName: propertyName, selectCollectionHandling: selectCollectionHandling); + + public SelectBuilder Last(string path, string propertyName = null, SelectCollectionHandling selectCollectionHandling = SelectCollectionHandling.LeaveAsIs) + => Aggregate(path, SelectTypes.Last, propertyName: propertyName, selectCollectionHandling: selectCollectionHandling); + + public SelectBuilder LastOrDefault(string path, string propertyName = null, SelectCollectionHandling selectCollectionHandling = SelectCollectionHandling.LeaveAsIs) + => Aggregate(path, SelectTypes.LastOrDefault, propertyName: propertyName, selectCollectionHandling: selectCollectionHandling); public virtual IQueryable Build() { diff --git a/PoweredSoft.DynamicLinq/Helpers/QueryableHelpers.cs b/PoweredSoft.DynamicLinq/Helpers/QueryableHelpers.cs index ace8133..eb4db22 100644 --- a/PoweredSoft.DynamicLinq/Helpers/QueryableHelpers.cs +++ b/PoweredSoft.DynamicLinq/Helpers/QueryableHelpers.cs @@ -246,25 +246,37 @@ namespace PoweredSoft.DynamicLinq.Helpers resolver.Resolve(); return resolver.GetResultBodyExpression(); } - else if (selectType == SelectTypes.PathToList) - { - var parser = new ExpressionParser(parameter, path); - var resolver = new PathExpressionResolver(parser); - resolver.NullChecking = nullChecking; - resolver.CollectionHandling = selectCollectionHandling; - resolver.Resolve(); - var expr = (resolver.Result as LambdaExpression).Body; - var notGroupedType = expr.Type.GenericTypeArguments.FirstOrDefault(); - if (notGroupedType == null) - throw new Exception($"Path must be a Enumerable but its a {expr.Type}"); + else if (selectType == SelectTypes.ToList) + return CreateSelectExpressionPathWithMethodName(parameter, path, selectCollectionHandling, nullChecking, "ToList"); + else if (selectType == SelectTypes.First) + return CreateSelectExpressionPathWithMethodName(parameter, path, selectCollectionHandling, nullChecking, "First"); + else if (selectType == SelectTypes.FirstOrDefault) + return CreateSelectExpressionPathWithMethodName(parameter, path, selectCollectionHandling, nullChecking, "FirstOrDefault"); + else if (selectType == SelectTypes.Last) + return CreateSelectExpressionPathWithMethodName(parameter, path, selectCollectionHandling, nullChecking, "Last"); + else if (selectType == SelectTypes.LastOrDefault) + return CreateSelectExpressionPathWithMethodName(parameter, path, selectCollectionHandling, nullChecking, "LastOrDefault"); - var body = Expression.Call(typeof(Enumerable), "ToList", new[] { notGroupedType }, expr) as Expression; - return body; - } throw new NotSupportedException($"unkown select type {selectType}"); } + private static Expression CreateSelectExpressionPathWithMethodName(ParameterExpression parameter, string path, SelectCollectionHandling selectCollectionHandling, bool nullChecking, string methodName) + { + var parser = new ExpressionParser(parameter, path); + var resolver = new PathExpressionResolver(parser); + resolver.NullChecking = nullChecking; + resolver.CollectionHandling = selectCollectionHandling; + resolver.Resolve(); + var expr = (resolver.Result as LambdaExpression).Body; + var notGroupedType = expr.Type.GenericTypeArguments.FirstOrDefault(); + if (notGroupedType == null) + throw new Exception($"Path must be a Enumerable but its a {expr.Type}"); + + var body = Expression.Call(typeof(Enumerable), methodName, new[] { notGroupedType }, expr) as Expression; + return body; + } + private static bool IsGrouping(IQueryable query) { // TODO needs to be alot better than this, but it will do for now. diff --git a/README.md b/README.md index d56afd9..bf0dba1 100644 --- a/README.md +++ b/README.md @@ -99,11 +99,13 @@ someQsomeQueryableOfTueryable.Count(); ### Select +> Note **PathToList** has been renamed to just **ToList** it seemed redudant, sorry for breaking change. + ```csharp var querySelect = query.Select(t => { t.NullChecking(true); // not obligated but usefull for in memory queries. -t.PathToList("Posts.Comments.CommentLikes", selectCollectionHandling: SelectCollectionHandling.Flatten); +t.ToList("Posts.Comments.CommentLikes", selectCollectionHandling: SelectCollectionHandling.Flatten); t.Path("FirstName"); t.Path("LastName", "ChangePropertyNameOfLastName"); }); From 7c0d0ef6c9a2bc0ad87307659915fcac2ab204f0 Mon Sep 17 00:00:00 2001 From: David Lebee Date: Thu, 25 Oct 2018 21:03:29 -0500 Subject: [PATCH 4/5] added old method for people that are upgrading, and will let them the time to migrate their solution. --- PoweredSoft.DynamicLinq/Fluent/Select/SelectBuilder.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/PoweredSoft.DynamicLinq/Fluent/Select/SelectBuilder.cs b/PoweredSoft.DynamicLinq/Fluent/Select/SelectBuilder.cs index 18b2d23..5dbbff9 100644 --- a/PoweredSoft.DynamicLinq/Fluent/Select/SelectBuilder.cs +++ b/PoweredSoft.DynamicLinq/Fluent/Select/SelectBuilder.cs @@ -77,6 +77,10 @@ namespace PoweredSoft.DynamicLinq.Fluent public SelectBuilder Last(string propertyName) => Aggregate(null, SelectTypes.Last, propertyName); public SelectBuilder First(string propertyName) => Aggregate(null, SelectTypes.First, propertyName); + [System.Obsolete("Use ToList instead")] + public SelectBuilder PathToList(string path, string propertyName = null, SelectCollectionHandling selectCollectionHandling = SelectCollectionHandling.LeaveAsIs) + => ToList(path, propertyName, selectCollectionHandling); + public SelectBuilder ToList(string path, string propertyName = null, SelectCollectionHandling selectCollectionHandling = SelectCollectionHandling.LeaveAsIs) => Aggregate(path, SelectTypes.ToList, propertyName: propertyName, selectCollectionHandling: selectCollectionHandling); From fe9cb782f6d8063fc0d73ebc4a2d9439c6c74295 Mon Sep 17 00:00:00 2001 From: David Lebee Date: Thu, 25 Oct 2018 21:09:55 -0500 Subject: [PATCH 5/5] updated documentation, and made the old method obsolete using attribute, would force compilation to fail. --- PoweredSoft.DynamicLinq/Fluent/Select/SelectBuilder.cs | 2 +- README.md | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/PoweredSoft.DynamicLinq/Fluent/Select/SelectBuilder.cs b/PoweredSoft.DynamicLinq/Fluent/Select/SelectBuilder.cs index 5dbbff9..2365aba 100644 --- a/PoweredSoft.DynamicLinq/Fluent/Select/SelectBuilder.cs +++ b/PoweredSoft.DynamicLinq/Fluent/Select/SelectBuilder.cs @@ -77,7 +77,7 @@ namespace PoweredSoft.DynamicLinq.Fluent public SelectBuilder Last(string propertyName) => Aggregate(null, SelectTypes.Last, propertyName); public SelectBuilder First(string propertyName) => Aggregate(null, SelectTypes.First, propertyName); - [System.Obsolete("Use ToList instead")] + [System.Obsolete("Use ToList instead", true)] public SelectBuilder PathToList(string path, string propertyName = null, SelectCollectionHandling selectCollectionHandling = SelectCollectionHandling.LeaveAsIs) => ToList(path, propertyName, selectCollectionHandling); diff --git a/README.md b/README.md index bf0dba1..10e74be 100644 --- a/README.md +++ b/README.md @@ -192,6 +192,8 @@ Assert.AreEqual(first?.FirstName, "David"); ### How it can be used in a web api +> I highly suggest looking @ https://github.com/poweredsoft/dynamicquery if you are interested in this sample. + ```csharp [HttpGet][Route("FindClients")] public IHttpActionResult FindClients(string filterField = null, string filterValue = null,