diff --git a/PoweredSoft.DynamicLinq.Test/GroupingTests.cs b/PoweredSoft.DynamicLinq.Test/GroupingTests.cs index db3db78..bc9f24e 100644 --- a/PoweredSoft.DynamicLinq.Test/GroupingTests.cs +++ b/PoweredSoft.DynamicLinq.Test/GroupingTests.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Text; using System.Threading.Tasks; using PoweredSoft.DynamicLinq; +using PoweredSoft.DynamicLinq.Dal; namespace PoweredSoft.DynamicLinq.Test { @@ -72,31 +73,42 @@ namespace PoweredSoft.DynamicLinq.Test int i = 0;*/ - /* - .Select(t => new + var dynamicSyntax3 = TestData.Sales + .AsQueryable() + .GroupBy(t => t.ClientId) + .Select(t => new + { + TheClientId = t.Key, + Count = t.Count(), + });*/ + + BlogContext bc = null; + if (bc != null) { - TheClientId = t.Key, - Count = t.Count(), - CountClientId = t.Count(t2 => t2.ClientId > 1), - LongCount = t.LongCount(), - NetSales = t.Sum(t2 => t2.NetSales), - TaxAverage = t.Average(t2 => t2.Tax), - Sales = t.ToList() - });*/ + bc.Authors.GroupBy(t => new { t.LastName }).Select(t => new + { + t.Key.LastName, + Count = t.Count() + + }); + } var dynamicSyntax2 = TestData.Sales .AsQueryable() .GroupBy(t => t.Path("ClientId")) .Select(t => { - t.Key("TheClientId", "ClientId"); - t.Count("Count"); + t.Key("TheClientId", "ClientId"); + t.Count("Count"); + /* t.LongCount("LongCount"); t.Sum("NetSales"); t.Average("Tax", "TaxAverage"); - t.ToList("Sales"); + t.ToList("Sales");*/ }); + + var result = dynamicSyntax2.ToObjectList(); } private object compare(MockSale arg) diff --git a/PoweredSoft.DynamicLinq/Extensions/QueryableExtensions.cs b/PoweredSoft.DynamicLinq/Extensions/QueryableExtensions.cs index fd82841..66b0192 100644 --- a/PoweredSoft.DynamicLinq/Extensions/QueryableExtensions.cs +++ b/PoweredSoft.DynamicLinq/Extensions/QueryableExtensions.cs @@ -96,7 +96,27 @@ namespace PoweredSoft.DynamicLinq if (sb.Empty) throw new Exception("No select specified, please specify at least one select path"); - return query; + return QueryableHelpers.Select(query, + sb.Parts.Select(t => (selectType: t.SelectType, propertyName: t.PropertyName, path: t.Path)).ToList(), + sb.DestinationType); + } + + public static List ToObjectList(this IQueryable query) + { + var ret = new List(); + foreach (var o in query) + ret.Add(o); + + return ret; + } + + public static List ToDynamicList(this IQueryable query) + { + var ret = new List(); + foreach (var o in query) + ret.Add(o); + + return ret; } } } diff --git a/PoweredSoft.DynamicLinq/Fluent/Select/SelectBuilder.cs b/PoweredSoft.DynamicLinq/Fluent/Select/SelectBuilder.cs index 5403c96..6379769 100644 --- a/PoweredSoft.DynamicLinq/Fluent/Select/SelectBuilder.cs +++ b/PoweredSoft.DynamicLinq/Fluent/Select/SelectBuilder.cs @@ -15,7 +15,7 @@ namespace PoweredSoft.DynamicLinq.Fluent public class SelectBuilder { public List Parts = new List(); - + public Type DestinationType { get; set; } public bool Empty => Parts?.Count == 0; protected void throwIfUsedOrEmpty(string propertyName) diff --git a/PoweredSoft.DynamicLinq/Helpers/QueryableHelpers.cs b/PoweredSoft.DynamicLinq/Helpers/QueryableHelpers.cs index 2ebd9a4..426ba2d 100644 --- a/PoweredSoft.DynamicLinq/Helpers/QueryableHelpers.cs +++ b/PoweredSoft.DynamicLinq/Helpers/QueryableHelpers.cs @@ -5,14 +5,15 @@ using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Reflection; -using System.Text; -using System.Threading.Tasks; namespace PoweredSoft.DynamicLinq.Helpers { public static class QueryableHelpers { + + + public static Expression GetConditionExpressionForMember(ParameterExpression parameter, Expression member, ConditionOperators conditionOperator, ConstantExpression constant, StringComparison? stringComparision) { if (parameter == null) @@ -106,8 +107,8 @@ namespace PoweredSoft.DynamicLinq.Helpers var result = query.Provider.CreateQuery(groupByExpression);*/ var ctor = Expression.New(keyType); - var bindings = partExpressions.Select(partExpression => Expression.Bind(keyType.GetProperty(partExpression.propertyName), partExpression.expression)).ToList(); - var mi = Expression.MemberInit(ctor, bindings.ToArray()); + var bindings = partExpressions.Select(partExpression => Expression.Bind(keyType.GetProperty(partExpression.propertyName), partExpression.expression)).ToArray(); + var mi = Expression.MemberInit(ctor, bindings); var lambda = Expression.Lambda(mi, parameter); var genericMethod = equalityCompareType == null ? Constants.GroupByMethod.MakeGenericMethod(type, keyType) : Constants.GroupByMethodWithEqualityComparer.MakeGenericMethod(type, keyType); //, Activator.CreateInstance(equalityCompareType)); var groupByExpression = equalityCompareType == null ? Expression.Call(genericMethod, query.Expression, lambda) : Expression.Call(genericMethod, query.Expression, lambda, Expression.New(equalityCompareType)); @@ -115,6 +116,72 @@ namespace PoweredSoft.DynamicLinq.Helpers return result; } + public static IQueryable Select(IQueryable query, List<(SelectTypes selectType, string propertyName, string path)> parts, Type destinationType = null) + { + // create parameter. + var queryType = query.ElementType; + var parameter = Expression.Parameter(queryType, "t"); + + // establish which anynomous types we might need to create. + var fields = new List<(Type type, string propertyName)>(); + var partExpressions = new List<(Expression expression, string propertyName)>(); + parts.ForEach(part => + { + var partBodyExpression = CreateSelectExpression(query, parameter, part.selectType, part.path); + fields.Add((partBodyExpression.Type, part.propertyName)); + partExpressions.Add((partBodyExpression, part.propertyName)); + }); + + // type to use. + var typeToCreate = destinationType ?? DynamicClassFactory.CreateType(fields); + var ctor = Expression.New(typeToCreate); + var bindings = partExpressions.Select(t => Expression.Bind(typeToCreate.GetProperty(t.propertyName), t.expression)).ToArray(); + var mi = Expression.MemberInit(ctor, bindings); + var lambda = Expression.Lambda(mi, parameter); + + /* + public static IQueryable Tester(IQueryable data) + { + var source = Expression.Parameter(typeof(TypeOne), "source"); + var selector = Expression.Lambda>( + Expression.MemberInit(Expression.New(typeof(TypeTwo)), + Expression.Bind(typeof(TypeTwo).GetProperty("TwoProp"), Expression.Property(source, "OneProp"))), + source); + return data.Select(selector); + }*/ + + //public static IQueryable Select(this IQueryable source, Expression> selector); + + + var selectExpr = Expression.Call(typeof(Queryable), "Select", new[] { query.ElementType, typeToCreate }, query.Expression, lambda); + var result = query.Provider.CreateQuery(selectExpr); + return result; + } + + private static Expression CreateSelectExpression(IQueryable query, ParameterExpression parameter, SelectTypes selectType, string path) + { + if (selectType == SelectTypes.Key) + { + return ResolvePathForExpression(parameter, path); + } + else if (selectType == SelectTypes.Count) + { + // TODO: check if we need to ensure if grouped before getting second generic argument. + var notGroupedType = parameter.Type.GenericTypeArguments[1]; + var body = Expression.Call(typeof(Enumerable), "Count", new[] { notGroupedType }, parameter); + return body; + } + else if (selectType == SelectTypes.LongCount) + { + // TODO: check if we need to ensure if grouped before getting second generic argument. + var notGroupedType = parameter.Type.GenericTypeArguments[1]; + var body = Expression.Call(typeof(Enumerable), "LongCount", new[] { notGroupedType }, parameter); + return body; + } + + throw new NotSupportedException($"unkown select type {selectType}"); + } + public static IQueryable GroupBy(IQueryable query, Type type, string path) { var parameter = Expression.Parameter(type, "t"); @@ -327,6 +394,8 @@ namespace PoweredSoft.DynamicLinq.Helpers throw new NotSupportedException($"{collectionHandling} is not supported"); } + + public static Expression> CreateFilterExpression(string path, ConditionOperators condition, object value,