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}"); }