diff --git a/PoweredSoft.DynamicLinq.ConsoleApp/BetterProto2.cs b/PoweredSoft.DynamicLinq.ConsoleApp/BetterProto2.cs index 999cf90..23397e8 100644 --- a/PoweredSoft.DynamicLinq.ConsoleApp/BetterProto2.cs +++ b/PoweredSoft.DynamicLinq.ConsoleApp/BetterProto2.cs @@ -64,7 +64,7 @@ namespace PoweredSoft.DynamicLinq.ConsoleApp // the builder. var per = new PathExpressionResolver(ep); - per.NullChecking = SelectNullHandling.Handle; + per.NullHandling = SelectNullHandling.Handle; per.Resolve(); // the result expression. @@ -76,7 +76,7 @@ namespace PoweredSoft.DynamicLinq.ConsoleApp public class PathExpressionResolver { - public SelectNullHandling NullChecking { get; set; } = SelectNullHandling.LeaveAsIs; + public SelectNullHandling NullHandling { get; set; } = SelectNullHandling.LeaveAsIs; public SelectCollectionHandling CollectionHandling { get; set; } = SelectCollectionHandling.LeaveAsIs; public ExpressionParser Parser { get; protected set; } @@ -97,8 +97,64 @@ namespace PoweredSoft.DynamicLinq.ConsoleApp // group the piece by common parameters var groups = Parser.GroupBySharedParameters(); - // compiled lambdas. - var groupLambdas = groups.Select(group => group.CompileGroup(NullChecking)).ToList(); + Expression ce = null; + groups.ReversedForEach(group => + { + if (ce == null) + { + var ge = group.CompileGroup(NullHandling); + var gl = Expression.Lambda(ge, group.Parameter); + + if (group.Parent == null) + { + ce = gl; + return; + } + + var parent = group.Parent; + var parentExpression = parent.CompileGroup(NullHandling); + var selectType = parent.GroupEnumerableType(); + var select = Expression.Call(typeof(Enumerable), "Select", + new Type[] { selectType, ge.Type }, + parentExpression, gl); + ce = select; + } + else + { + if (group.Parent == null) + { + ce = Expression.Lambda(ce, group.Parameter); + return; + } + + var parent = group.Parent; + var parentExpression = parent.CompileGroup(NullHandling); + var selectType = parent.GroupEnumerableType(); + var currentExpressionLambda = Expression.Lambda(ce, group.Parameter); + ce = Expression.Call(typeof(Enumerable), "Select", + new Type[] { selectType, ce.Type }, + parentExpression, currentExpressionLambda); + } + }); + } + + private Expression JoinGroups(IEnumerable groups, Expression result = null) + { + var lastGroup = groups.Last(); + var lastGroupExpression = lastGroup.CompileGroup(NullHandling); + var lastGroupExpressionLambda = Expression.Lambda(lastGroupExpression, lastGroup.Parameter); + + if (lastGroup.Parent == null) + return lastGroupExpressionLambda; + + var parent = lastGroup.Parent; + var parentExpression = parent.CompileGroup(NullHandling); + var selectType = parent.GroupEnumerableType(); + + var select = Expression.Call(typeof(Enumerable), "Select", + new Type[] { selectType, lastGroupExpression.Type }, + parentExpression, lastGroupExpressionLambda); + return select; } protected Expression RecursiveSelect(List pieces) diff --git a/PoweredSoft.DynamicLinq/Parser/ExpressionParser.cs b/PoweredSoft.DynamicLinq/Parser/ExpressionParser.cs index 9629b4f..013d03e 100644 --- a/PoweredSoft.DynamicLinq/Parser/ExpressionParser.cs +++ b/PoweredSoft.DynamicLinq/Parser/ExpressionParser.cs @@ -62,10 +62,11 @@ namespace PoweredSoft.DynamicLinq.Parser }); } - private ExpressionParserPieceGroup CreateAndAddGroup(List groups, ParameterExpression parameter) + private ExpressionParserPieceGroup CreateAndAddGroup(List groups, ParameterExpression parameter, ExpressionParserPieceGroup parent) { var group = new ExpressionParserPieceGroup(); - group.ParameterExpression = parameter; + group.Parameter = parameter; + group.Parent = parent; groups.Add(group); return group; } @@ -74,12 +75,12 @@ namespace PoweredSoft.DynamicLinq.Parser { var groups = new List(); - var group = CreateAndAddGroup(groups, Parameter); + var group = CreateAndAddGroup(groups, Parameter, null); Pieces.ForEach(piece => { group.Pieces.Add(piece); if (piece.IsGenericEnumerable) - group = CreateAndAddGroup(groups, Expression.Parameter(piece.EnumerableType)); + group = CreateAndAddGroup(groups, Expression.Parameter(piece.EnumerableType), group); }); return groups; diff --git a/PoweredSoft.DynamicLinq/Parser/ExpressionParserPieceGroup.cs b/PoweredSoft.DynamicLinq/Parser/ExpressionParserPieceGroup.cs index 73f349e..79d1980 100644 --- a/PoweredSoft.DynamicLinq/Parser/ExpressionParserPieceGroup.cs +++ b/PoweredSoft.DynamicLinq/Parser/ExpressionParserPieceGroup.cs @@ -9,10 +9,11 @@ namespace PoweredSoft.DynamicLinq.Parser public class ExpressionParserPieceGroup { public List Pieces { get; set; } = new List(); - public ParameterExpression ParameterExpression { get; set; } + public ParameterExpression Parameter { get; set; } + public ExpressionParserPieceGroup Parent { get; set; } #if DEBUG - public override string ToString() => $"{ParameterExpression?.ToString()} is {ParameterExpression?.Type} | {(Pieces == null ? "" : string.Join(" -> ", Pieces.Select(t2 => t2.ToString())))}"; + public override string ToString() => $"{Parameter?.ToString()} is {Parameter?.Type} | {(Pieces == null ? "" : string.Join(" -> ", Pieces.Select(t2 => t2.ToString())))}"; public object CompileSimpleExpress(SelectNullHandling nullHandling) { diff --git a/PoweredSoft.DynamicLinq/Parser/ParserExtensions.cs b/PoweredSoft.DynamicLinq/Parser/ParserExtensions.cs index 8c377f9..5fdb7ac 100644 --- a/PoweredSoft.DynamicLinq/Parser/ParserExtensions.cs +++ b/PoweredSoft.DynamicLinq/Parser/ParserExtensions.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Linq.Expressions; using System.Text; @@ -13,16 +14,19 @@ namespace PoweredSoft.DynamicLinq.Parser return result; } - public static LambdaExpression CompileGroup(this ExpressionParserPieceGroup group, SelectNullHandling NullHandling) + public static Expression CompileGroup(this ExpressionParserPieceGroup group, SelectNullHandling NullHandling) { - var expr = group.ParameterExpression as Expression; + var expr = group.Parameter as Expression; group.Pieces.ForEach(piece => { expr = Expression.PropertyOrField(expr, piece.Name); }); + return expr; + } - var ret = Expression.Lambda(expr, group.ParameterExpression); - return ret; + public static Type GroupEnumerableType(this ExpressionParserPieceGroup group) + { + return group.Pieces.Last().EnumerableType; } } }