Esempio n. 1
0
        public static Type InferClrType(DimensionExpression[] selects, DimensionExpression[] groupBy, Type itemType)
        {
            if (selects == null || !selects.Any()) return itemType;

            var containsAggregates = selects.Any(x => x.IsAggregate);

            Dictionary<string, Type> requiredFields = null;

            if (groupBy != null || containsAggregates)
            {
                requiredFields = InferReduceType(selects, groupBy, itemType);
            }
            else
            {
                requiredFields = InferSelectType(selects, itemType);
            }

            var type = LinqRuntimeTypeBuilder.GetDynamicType(requiredFields);

            return type;

        }
Esempio n. 2
0
        private static Expression MakeGroupByExpressionEx(Expression collectionParameter, DimensionExpression[] groupBy, DimensionExpression[] selects, int depth, Type itemType, Type metadataType, Type groupingType, Type resultType)
        {
            var linqExtensionClassType = !groupBy.Any(x => x.ParallelizedLinq) ? PLinqExtensionClassType : LinqExtensionClassType;
            var linqEnumType = linqExtensionClassType == PLinqExtensionClassType ? PLinqEnumType : LinqEnumType;

            var remDepth = groupBy.Skip(depth).Count();
            var currentDimension = groupBy.ElementAt(depth);
            var currentGroupBy = currentDimension.Source;
            var currentFunction = currentDimension.Function;
            var currentChild = currentDimension.Child;

            if (currentDimension.LinkedSelect != null)
            {
                currentFunction = currentDimension.LinkedSelect.Function;
            }

            if (currentGroupBy == null && currentFunction == null && currentChild == null)
            {
                if (remDepth > 1)
                {
                    var newCollectionParameter = Expression.Parameter(type: typeof(EnumMetadata<>).MakeGenericType(linqEnumType.MakeGenericType(itemType)), name: MakeNumericName("gmd", depth + 1));
                    groupBy.ElementAt(depth + 1).GroupingEnumParameter = newCollectionParameter;
                    return MakeGroupByExpressionEx(collectionParameter, groupBy, selects, depth + 1, itemType, metadataType, groupingType, resultType);
                }
            }

            var sourceCollection = collectionParameter.Type.GetGenericTypeDefinition() == typeof(EnumMetadata<>)
                ? Expression.MakeMemberAccess(collectionParameter, collectionParameter.Type.GetField("Enum"))
                : collectionParameter;

            if (linqEnumType == PLinqEnumType && !TypeImplements(GetTypeOfEnumerable(sourceCollection.Type), PLinqEnumType))
            {
                //.AsParallel()
                var res = AppendAsParallel(linqExtensionClassType, sourceCollection);

                groupBy.ElementAt(depth).ParallelizedLinq = true;

                sourceCollection = res;
            }

            var elementType = GetTypeOfEnumerable(sourceCollection.Type);
            var selectType = resultType;

            Expression groupByExpr = null;

            DimensionExpression sortValueType = null;

            MemberInfo groupByMember;

            Expression currentChildExpression = null;

            if (currentChild != null)
            {
                currentChildExpression = currentChild.CreateExpression(null);
            }

            if (currentGroupBy == null && currentFunction == null && currentChild == null)
            {
                groupByExpr = Expression.NewArrayInit(sourceCollection.Type, sourceCollection);
            }
            else if ((groupByMember = GetMember(itemType, currentGroupBy)) != null && TypeImplements(GetMemberType(groupByMember), typeof(IEnumerable<>)) || (currentChildExpression != null && TypeImplements(currentChildExpression.Type, typeof(IEnumerable<>))))
            {
                //.SelectMany(m => m.Ngrams, (m, t) => new MentionMetadata { Mention = m, Id = t })
                var genericSelectManyInfos = linqExtensionClassType.GetMethods().Where(x => x.Name == "SelectMany" && x.IsGenericMethod && x.GetParameters().Length == 3);
                var extensionIndex = (linqExtensionClassType == typeof(ParallelEnumerable)) ? 0 : 1;
                var genericSelectManyInfo = genericSelectManyInfos.Skip(extensionIndex).FirstOrDefault();

                var collectionSelectorParameter = Expression.Parameter(type: elementType, name: GenerateParamName(elementType, depth));

                var sourcePath = GetSourcePath(currentDimension);

                var collectionGroupingMemberAccess = Expression.MakeMemberAccess(collectionSelectorParameter, GetField(collectionSelectorParameter.Type, sourcePath));

                var collectionSelector = Expression.Lambda
                (
                    delegateType: typeof(Func<,>).MakeGenericType(elementType, collectionGroupingMemberAccess.Type),
                    parameters: collectionSelectorParameter,
                    body: collectionGroupingMemberAccess
                );

                var keyType = GetTypeOfEnumerable(collectionGroupingMemberAccess.Type);

                var selectManyInfo = genericSelectManyInfo.MakeGenericMethod(elementType, keyType, metadataType);

                var resultSelectorParameters = new ParameterExpression[]
                {
                    Expression.Parameter(type: elementType, name: GenerateParamName(elementType, depth) ),
                    Expression.Parameter(type: keyType, name: GenerateParamName(keyType, depth) )
                };

                var memberBindings = new List<MemberBinding>
                {
                    Expression.Bind
                    (
                        member: metadataType.GetField("Item"),
                        expression: resultSelectorParameters[0]
                    )
                };

                string groupByTargetPath = GetTargetPath(keyType);

                var targetField = GetField(metadataType, groupByTargetPath);

                Expression sourceExpression = resultSelectorParameters[1];

                if (IsTupleType(sourceExpression.Type))
                {
                    var sourceItemInfo = sourceExpression.Type.GetProperty("Item2");

                    sourceExpression = Expression.MakeMemberAccess
                    (
                        expression:   sourceExpression,
                        member :sourceItemInfo
                    );
                }

                memberBindings.Add(Expression.Bind
                (
                    member: targetField,
                    expression: sourceExpression
                ));

                var resultSelector = Expression.Lambda
                (
                    delegateType: typeof(Func<,,>).MakeGenericType(elementType, keyType, metadataType),
                    parameters: resultSelectorParameters,
                    body: Expression.MemberInit
                    (
                        newExpression: Expression.New(type: metadataType),
                        bindings: memberBindings
                    )
                );

                var selectManyExpr = Expression.Call
                (
                    method: selectManyInfo,
                    arguments: new Expression[]
                    {
                        sourceCollection,
                        collectionSelector,
                        resultSelector
                    }
                );

                //.GroupBy(md => md.Id, md => md.Mention)
                var keySelectorParameter = Expression.Parameter(type: metadataType, name: "md");
                var resultSelectorParameter = Expression.Parameter(type: metadataType, name: "md");

                var keySelectorBody = Expression.MakeMemberAccess(expression: keySelectorParameter, member: GetField(metadataType, groupByTargetPath));
                var resultSelectorBody = Expression.MakeMemberAccess(expression: resultSelectorParameter, member: metadataType.GetField("Item"));

                var genericGroupByInfos = linqExtensionClassType.GetMethods().Where(x => x.Name == "GroupBy" && x.IsGenericMethod && x.GetParameters().Length == 3);
                var genericGroupByInfo = genericGroupByInfos.ElementAt(1);
                var groupByInfo = genericGroupByInfo.MakeGenericMethod(metadataType, keySelectorBody.Type, resultSelectorBody.Type);

                var keySelectorLambda = Expression.Lambda
                (
                    delegateType: typeof(Func<,>).MakeGenericType(metadataType, keySelectorBody.Type),
                    parameters: keySelectorParameter,
                    body: keySelectorBody
                );

                var resultSelectorLambda = Expression.Lambda
                (
                    delegateType: typeof(Func<,>).MakeGenericType(metadataType, resultSelectorBody.Type),
                    parameters: resultSelectorParameter,
                    body: resultSelectorBody
                );

                groupByExpr = Expression.Call
                (
                    method: groupByInfo,
                    arguments: new Expression[]
                    {
                        selectManyExpr,
                        keySelectorLambda,
                        resultSelectorLambda
                    }
                );
            }
            else
            {
                //.GroupBy(m => m.OccurredOn.Ticks - 28189283)
                var keySelectorParameter = Expression.Parameter(type: itemType, name: "m");

            
                Expression keySelectorBody = null;

                if (currentFunction != null)
                {
                    keySelectorBody = MakeFunctionCallExpression(currentDimension, itemType, keySelectorParameter);
                }
                else if(currentGroupBy != null)
                {
                    var sourcePath = GetSourcePath(currentDimension);
                    var targetMember = GetMember(itemType, sourcePath);
                    keySelectorBody = Expression.MakeMemberAccess(expression: keySelectorParameter, member: targetMember);
                }
                else if (currentChild != null)
                {
                    keySelectorBody = currentChild.CreateExpression(null);
                }
                else
                {
                    throw new BermudaExpressionGenerationException("Don't know how to handle for group by:" + currentDimension);
                }
                
                var genericGroupByInfos = linqExtensionClassType.GetMethods().Where(x => x.Name == "GroupBy" && x.IsGenericMethod && x.GetParameters().Length == 2);
                var genericGroupByInfo = genericGroupByInfos.FirstOrDefault();
                var groupByInfo = genericGroupByInfo.MakeGenericMethod(elementType, keySelectorBody.Type);

                keySelectorBody = ParameterRebinder.ReplaceParameters(keySelectorBody, "x", keySelectorParameter);

                var groupByLambda = Expression.Lambda
                (
                    delegateType: typeof(Func<,>).MakeGenericType(elementType, keySelectorBody.Type),
                    parameters: keySelectorParameter,
                    body: keySelectorBody
                );

                groupByExpr = Expression.Call
                (
                    method: groupByInfo,
                    arguments: new Expression[]
                    {
                        sourceCollection,
                        groupByLambda
                    }
                );
            }




            var inferredParameterType = typeof(EnumMetadata<>).MakeGenericType(GetTypeOfEnumerable(groupByExpr.Type));
            var groupingParameter = Expression.Parameter(type: inferredParameterType, name: MakeNumericName("gmd", depth));

            if (groupBy.Length > depth)
            {
                groupBy.ElementAt(depth).GroupingEnumParameter = groupingParameter;
            }

            //var loldas = temp == groupBy.ElementAt(depth);

            var enumType = GetTypeOfEnumerable(groupByExpr.Type);
            var enumMetadataType = typeof(EnumMetadata<>).MakeGenericType(enumType);


            //.AsParallel()
            //var genericAsParallelInfo = typeof(ParallelEnumerable).GetMethods().FirstOrDefault(x => x.Name == "AsParallel" && x.IsGenericMethod && x.GetParameters().Length == 1);
            //var asParallelInfo0 = genericAsParallelInfo.MakeGenericMethod(enumType);
            //groupByExpr = Expression.Call(method: asParallelInfo0, arg0: groupByExpr);

            //.Select(g => new GroupMetadata { Group = g, Value = g.Whatever() })
            var selectLinqClass = groupByExpr.Type.IsArray ? LinqExtensionClassType : linqExtensionClassType;
            var genericSelectInfos = selectLinqClass.GetMethods().Where(x => x.Name == "Select" && x.IsGenericMethod && x.GetParameters().Length == 2);
            var genericSelectInfo = genericSelectInfos.FirstOrDefault();
            var selectInfo0 = genericSelectInfo.MakeGenericMethod(enumType, enumMetadataType);
            var selectGroupParam = Expression.Parameter(type: enumType, name: "g");

            var selectGroupMemberBindings = new List<MemberBinding>();

            //Group = g, Value
            selectGroupMemberBindings.Add(Expression.Bind(member: enumMetadataType.GetField("Enum"), expression: selectGroupParam));

            //Value = g.Whatever()
            if (currentDimension != null && currentDimension.Ordering != null && currentDimension.Ordering.Function != null)
            {
                sortValueType = currentDimension.Ordering;
                if (sortValueType != null)
                {
                    selectGroupMemberBindings.Add(MakeAggregateFunctionCallExpression(selectGroupParam, sortValueType, null, 0, enumMetadataType, "Value"));
                }
            }

            // = new GroupMetadata{}
            var selectInit = Expression.MemberInit
            (
                newExpression: Expression.New(type: enumMetadataType),
                bindings: selectGroupMemberBindings
            );

            var selectMetadataLambda = Expression.Lambda
            (
                parameters: selectGroupParam,
                body: selectInit
            );

            //.Select(...)
            var selectExpr = Expression.Call
            (
                method: selectInfo0,
                arg0: groupByExpr,
                arg1: selectMetadataLambda
            );

            Expression selectSourceExpression = selectExpr;

            //.OrderByDescending(g => g.Value)
            if (currentDimension != null && currentDimension.Ordering != null && (currentDimension.Ordering.Function != null || currentDimension.Ordering.Source != null))
            {
                var orderFuncName = currentDimension.OrderDescending ? "OrderByDescending" : "OrderBy";
                var genericOrderByInfo = linqExtensionClassType.GetMethods().FirstOrDefault(x => x.Name == orderFuncName && x.IsGenericMethod && x.GetParameters().Length == 2);
                var orderByInfo = genericOrderByInfo.MakeGenericMethod(enumMetadataType, typeof(long));
                var groupOrderParam = Expression.Parameter(type: enumMetadataType, name: "gg");
                var orderByExpr = Expression.Call
                (
                    method: orderByInfo,
                    arg0: selectExpr,
                    arg1: Expression.Lambda(parameters: groupOrderParam, body: Expression.MakeMemberAccess(expression: groupOrderParam, member: enumMetadataType.GetField("Value")))
                );

                selectSourceExpression = orderByExpr;
            }

            //.Take(5)
            if (currentDimension.Take.HasValue)
            {
                selectSourceExpression = AppendTake(linqExtensionClassType, enumMetadataType, selectSourceExpression, currentDimension.Take.Value);
            }

            if (remDepth <= 1)
            {
                //if (currentGroupBy == GroupByTypes.None) return collectionParameter;

                //.Select(gmd2 => new InferredType { Id = gmd.Group.Key, Id2 = gmd2.Group.Key, TargetPath = gmd2.SourcePath })
                var selectParameterType = GetTypeOfEnumerable(selectSourceExpression.Type);
                var selectInfo = genericSelectInfo.MakeGenericMethod(selectParameterType, resultType);

                var selectMemberBindings = new List<MemberAssignment>();

                //gmd2
                var parentGroupBy = groupBy.ElementAt(depth);
                var parentGroupParameter = parentGroupBy.GroupingEnumParameter;

                //gmd.Group
                var actualGroupAccess = Expression.MakeMemberAccess(parentGroupParameter, parentGroupParameter.Type.GetField("Enum"));
                //gmd.Value
                var groupValueAccess = Expression.MakeMemberAccess(parentGroupParameter, parentGroupParameter.Type.GetField("Value"));

                var lastGroupBy = groupBy.LastOrDefault();
                var computedValueForGroup = lastGroupBy.Ordering; // lastGroupBy.IsDateTime ? null : lastGroupBy.OrderBy;

                var countSelect = selects.FirstOrDefault(x => string.Equals(x.Function, CountAggregateString, StringComparison.InvariantCultureIgnoreCase));

                if (countSelect != null)
                {
                    //CountAlias = 
                    var countBinding = MakeAggregateFunctionCallExpression(actualGroupAccess, new DimensionExpression { Function = CountAggregateString, Target = GetTargetPath(countSelect) }, computedValueForGroup != null && computedValueForGroup.Function == CountAggregateString ? groupValueAccess : null, 0, resultType, null);
                    selectMemberBindings.Add(countBinding);
                }
                else if (selects.Any(x => IsCountRequiredForAggregate(x.Function)))
                {
                    //_Count = 
                    var countBinding = MakeAggregateFunctionCallExpression(actualGroupAccess, new DimensionExpression { Function = CountAggregateString, Target = CountTargetPath }, computedValueForGroup != null && computedValueForGroup.Function == CountAggregateString ? groupValueAccess : null, 0, resultType, null);
                    selectMemberBindings.Add(countBinding);
                }

                //Value =
                foreach (var select in selects.Where(x => !x.IsStar && !string.Equals(x.Function, CountAggregateString, StringComparison.InvariantCultureIgnoreCase)))
                {
                    if (select.IsBasedOnGrouping) continue;

                    if (select.IsFunctionCall)
                    {
                        var otherBinding = MakeAggregateFunctionCallExpression(actualGroupAccess, select, computedValueForGroup != null && computedValueForGroup.Equals(select) ? groupValueAccess : null, 0, resultType, null);

                        //it's not an aggregate... that's a problem
                        if (otherBinding == null) throw new BermudaExpressionGenerationException("Non aggregate function call in an aggregate query not allowed: " + select);

                        selectMemberBindings.Add(otherBinding);
                    }
                    else
                    {
                        var targetPath = GetTargetPath(select);

                        //var sourceField = GetMember(itemType, select.Source, false);

                        var targetFieldInfo = GetField(resultType, targetPath);

                        var actualValue = Convert.ChangeType(select.Source, select.SourceType);
                        selectMemberBindings.Add(Expression.Bind
                        (
                            targetFieldInfo, Expression.Constant(actualValue)
                        ));                        
                    }
                }

                //Id = gmd.Group.Key, Id2 = gmd2.Group.Key
                AddStarSelectColumns(groupBy, selects, resultType, selectMemberBindings);

                //gmd => new Datapoint{...}
                var selectLambda = Expression.Lambda
                (
                    parameters: parentGroupParameter,
                    body: Expression.MemberInit
                    (
                        newExpression: Expression.New(type: resultType),
                        bindings: selectMemberBindings
                    )
                );

                //.Select(...)
                var result = Expression.Call
                (
                    method: selectInfo,
                    arg0: selectSourceExpression,
                    arg1: selectLambda
                );

                return result;
            }
            else if (remDepth >= 2)
            {
                //var newCollectionParameter = Expression.Parameter(type: enumMetadataType, name: MakeNumericName("gmd", depth + 1));
                //groupBy.ElementAt(depth).GroupingEnumParameter = newCollectionParameter;

                var currentParameter = groupBy.ElementAt(depth).GroupingEnumParameter;

                var nestedExpression = MakeGroupByExpressionEx(currentParameter, groupBy, selects, depth + 1, itemType, metadataType, groupingType, resultType);

                var nestedExpressionLambda = Expression.Lambda
                (
                    parameters: currentParameter,
                    body: nestedExpression
                );


                //.SelectMany(gmd => Recurse())
                var sourceElementType = GetTypeOfEnumerable(selectSourceExpression.Type);
                var genericSelectManyInfos = linqExtensionClassType.GetMethods().Where(x => x.Name == "SelectMany" && x.IsGenericMethod && x.GetParameters().Length == 2);
                var genericSelectManyInfo = genericSelectManyInfos.Skip(0).FirstOrDefault();
                var selectManyInfo = genericSelectManyInfo.MakeGenericMethod(sourceElementType, selectType);

                var selectManyRecursiveExpr = Expression.Call
                (
                    method: selectManyInfo,
                    arg0: selectSourceExpression,
                    arg1: nestedExpressionLambda
                );

                return selectManyRecursiveExpr;

            }

            throw new Exception("not supposed to happen");
        }
Esempio n. 3
0
        public static Expression GetMapreduceExpression(Expression filter, DimensionExpression[] selects, DimensionExpression[] groupBy, Type itemType, GetExpression get, Expression source, string paramname)
        {
            var hash = (filter == null ? 0 : filter.ToString().GetHashCode()) + "|" + (selects == null ? "" : string.Join(",", selects.Select(x => x.GetChecksum()))) + "|" + (groupBy == null ? "" : string.Join(",", groupBy.Select(x => x.GetChecksum()))) + "|" + (get == null ? null : get.Take);
            Expression expr;

            var containsAggregates = selects != null && selects.Any(x => x.IsAggregate);

            LinkDimensions(groupBy, selects);

            var resultType = InferClrType(selects, groupBy, itemType);

            if (expressionCache.TryGetValue(hash, out expr))
            {
                return expr;
            }

            //a flat select transformation
            if (selects != null && groupBy == null && !containsAggregates)
            {
                expr = MakeSelectExpression(filter, selects, itemType, resultType, get, source, paramname);
            }
            else
            {
                //a non-grouping aggregate like "SELECT COUNT(*) FROM table"
                if (groupBy == null || !groupBy.Any()) groupBy = new DimensionExpression[] { new DimensionExpression { Source = null, Function = null } };

                expr = MakeMapreduceExpression(filter, selects, groupBy, itemType, resultType, get, source, paramname);
            }

            expressionCache[hash] = expr;

            return expr;
        }
Esempio n. 4
0
        private static Expression GetMergeExpression(Type elementType, ParameterExpression selectGroupParam, DimensionExpression[] selects, DimensionExpression[] groupBy)
        {
            //if (select.Aggregate == "First")
            //{
            //    var genericFirstOrDefaultInfos = typeof(Enumerable).GetMethods().Where(x => x.Name == "FirstOrDefault" && x.IsGenericMethod && x.GetParameters().Length == 1);
            //    var genericFirstOrDefaultInfo = genericFirstOrDefaultInfos.FirstOrDefault();
            //    var firstOrDefaultInfo = genericFirstOrDefaultInfo.MakeGenericMethod(elementType);
            //    var mergeExpr = Expression.Call(method: firstOrDefaultInfo, arg0: selectGroupParam);
            //    return mergeExpr;
            //}

            //if ((groupBy == null || !groupBy.Any()) && !selects.All(x => ReduceExpressionGeneration.IsAggregateFunction(x.Function) || x.IsStar)) return null;

            var memberBindigs = new List<MemberAssignment>();

            /*
            //group by all the non-select fields
            foreach (var x in elementType.GetFields())
            {
                if (selects.Any(s => string.Equals(x.Name, GetTargetPath(s), StringComparison.InvariantCultureIgnoreCase))) continue;

                var keyAccess = Expression.MakeMemberAccess
                (
                    member: selectGroupParam.Type.GetProperty("Key"),
                    expression: selectGroupParam
                );

                var memberAccess = Expression.Bind
                (
                    member: x,
                    expression: Expression.MakeMemberAccess
                    (
                        member: x,
                        expression: keyAccess
                    )
                );

                memberBindigs.Add(memberAccess);
            }*/

            var fields = elementType.GetFields();
            foreach (var sel in fields)
            {
                //if (sel.IsStar) continue;

                //var tar = GetTargetPath(sel);
                //if (fields.Any(x => string.Equals(x.Name, tar, StringComparison.InvariantCultureIgnoreCase))) continue;

                if (selects.Any(x => string.Equals(GetTargetPath(x), sel.Name, StringComparison.InvariantCultureIgnoreCase))) continue;
                if (string.Equals(sel.Name, CountTargetPath, StringComparison.InvariantCultureIgnoreCase)) continue;

                //var targetMember = fields.FirstOrDefault(x => string.Equals(x.Name, tar));
                var targetMember = sel;

                var keyAccess = Expression.MakeMemberAccess
                (
                    member: selectGroupParam.Type.GetProperty("Key"),
                    expression: selectGroupParam
                );

                var memberAccess = Expression.Bind
                (
                    member: targetMember,
                    expression: Expression.MakeMemberAccess
                    (
                        member: targetMember,
                        expression: keyAccess
                    )
                );

                memberBindigs.Add(memberAccess);
            }

            var actualSelects = selects.ToList();

            if (!actualSelects.Any(x => string.Equals(x.Function, CountAggregateString, StringComparison.InvariantCultureIgnoreCase)) && actualSelects.Any(x => IsCountRequiredForAggregate(x.Function) ))
            {
                actualSelects.Add(new DimensionExpression { Function = CountAggregateString, Target = CountTargetPath } );
            }

            foreach(var g in groupBy)
            {
                if (g.IsAutoSelect && g.LinkedSelect == null)
                {
                    var groupByTargetPath = GetTargetPath(g);
                    var selectTargetPath = g.LinkedSelect == null ? null : GetTargetPath(g.LinkedSelect);
                    actualSelects.Add(new DimensionExpression
                    {
                        Source = groupByTargetPath,
                        Target = selectTargetPath ?? groupByTargetPath,
                        IsBasedOnGrouping = true
                    });
                }
                //else
                //{
                //    var groupByTargetPath = GetTargetPath(g);
                //    if( g.LinkedSelect. != null ) groupByTargetPath = GetTargetPath(g.LinkedSelect);
                //    var matchingField = fields.FirstOrDefault(x => string.Equals(x.Name, groupByTargetPath, StringComparison.InvariantCultureIgnoreCase));
 
                    
                //}
            }

            foreach (var sel in actualSelects)
            {
                if (sel.IsStar) continue;

                var targetPath = GetTargetPath(sel);

                var targetField = GetField(elementType, targetPath);

                if (sel.IsBasedOnGrouping)
                {
                    var keyPropertyInfo = selectGroupParam.Type.GetProperty("Key");
                    var keyMemberAccess = Expression.MakeMemberAccess(selectGroupParam, keyPropertyInfo);
                    var keyPropertyAccess = Expression.MakeMemberAccess(keyMemberAccess, targetField);

                    memberBindigs.Add(Expression.Bind
                    (
                        member: targetField,
                        expression: keyPropertyAccess
                    ));
                }
                else
                {
                    //if( targetField.FieldType != ty

                    var genericSumInfos = typeof(Enumerable).GetMethods().Where(x => x.Name == "Sum" && x.IsGenericMethod && x.GetParameters().Length == 2 && x.ReturnType == targetField.FieldType);
                    var genericSumInfo = genericSumInfos.FirstOrDefault();
                    var sumInfo = genericSumInfo.MakeGenericMethod(elementType);

                    var genericSumInfos2 = typeof(Enumerable).GetMethods().Where(x => x.Name == "Sum" && x.IsGenericMethod && x.GetParameters().Length == 2 && x.ReturnType == typeof(long));
                    var genericSumInfo2 = genericSumInfos2.FirstOrDefault();
                    var sumInfo2 = genericSumInfo2.MakeGenericMethod(elementType);

                    ////TargetField = g.Sum(p => p.TargetField * p._Count) / g.Sum(p => p._Count)
                    if ( string.Equals(sel.Function, "Average", StringComparison.InvariantCultureIgnoreCase) )
                    {
                        var actualCountTargetPath = CountTargetPath;
                        var countSelect = actualSelects.FirstOrDefault(x => string.Equals(x.Function, CountAggregateString, StringComparison.InvariantCultureIgnoreCase));

                        if (countSelect == null) throw new Exception("The provided type has no required count field");

                        actualCountTargetPath = GetTargetPath(countSelect);

                        var pointParameter = Expression.Parameter(elementType, "p1");
                        var countAccess = Expression.MakeMemberAccess(pointParameter, GetField(elementType, actualCountTargetPath));
                        var targetPathAccess = Expression.MakeMemberAccess(pointParameter, GetField(elementType, targetPath));
                        var sumBody = Expression.Multiply
                        (
                            left: (countAccess.Type == targetPathAccess.Type) ? (Expression)countAccess : Expression.Convert(countAccess, targetPathAccess.Type),
                            right: targetPathAccess
                        );

                        var pointParameter2 = Expression.Parameter(elementType, "p2");
                        var sumBody2 = Expression.MakeMemberAccess(pointParameter2, GetField(elementType, actualCountTargetPath));

                        Expression sumOfProducts = Expression.Call(method: sumInfo, arg0: selectGroupParam, arg1: Expression.Lambda(parameters: pointParameter, body: sumBody));
                        Expression sumOfCounts = Expression.Call(method: sumInfo2, arg0: selectGroupParam, arg1: Expression.Lambda(parameters: pointParameter2, body: sumBody2));

                        var division = Expression.Divide
                        (
                            left: sumOfProducts,
                            right: sumOfProducts.Type != sumOfCounts.Type ? Expression.Convert(sumOfCounts, sumOfProducts.Type) : sumOfCounts
                        );

                        memberBindigs.Add(Expression.Bind
                        (
                            member: targetField,
                            expression: division
                        ));
                    }

                    //TargetField = g.Sum(p => p.TargetField)
                    else// (string.Equals(sel.Function, CountAggregateString, StringComparison.InvariantCultureIgnoreCase) || string.Equals(sel.Function, "Sum", StringComparison.InvariantCultureIgnoreCase))
                    {
                        var pointParameter = Expression.Parameter(elementType, "p0");
                        var sumBody = Expression.MakeMemberAccess(pointParameter, GetField(elementType, targetPath));

                        memberBindigs.Add(Expression.Bind
                        (
                            member: targetField,
                            expression: Expression.Call(method: sumInfo, arg0: selectGroupParam, arg1: Expression.Lambda(parameters: pointParameter, body: sumBody)))
                        );
                    }

                }
            }

            var result = Expression.MemberInit
            (
                newExpression: Expression.New(elementType),
                bindings: memberBindigs
            );

            return result;
        }
Esempio n. 5
0
        public static Expression GetMergeInvocationExpression(DimensionExpression[] selects, DimensionExpression[] groupBy, Type elementType)
        {

            if ((groupBy == null || !groupBy.Any()) && !selects.All(x => x.IsAggregate)) return null;

            //var elementType = ReduceExpressionGeneration.GetTypeOfEnumerable( collectionType );
            LinkDimensions(groupBy, selects);

            var collectionType = typeof(IEnumerable<>).MakeGenericType(elementType);

            var collectionParameter = Expression.Parameter(collectionType, "col");

            var genericGroupByInfos = typeof(Enumerable).GetMethods().Where(x => x.Name == "GroupBy" && x.IsGenericMethod && x.GetParameters().Length == 2);
            var genericGroupByInfo = genericGroupByInfos.FirstOrDefault();
            var groupByInfo = genericGroupByInfo.MakeGenericMethod(elementType, elementType);

            var lambdaParam = Expression.Parameter(elementType, "x");

            var groupingDimensionBindings = new List<MemberAssignment>();

            
            var fields = elementType.GetFields();
            foreach (var g in groupBy)
            {
                var tar = GetTargetPath(g);
                var tar2 = g.LinkedSelect == null ? null : GetTargetPath(g.LinkedSelect);
                //fields.Any(x => string.Equals(x.Name, tar, StringComparison.InvariantCultureIgnoreCase))
                //if( !sel.IsBasedOnGrouping )   continue;
                //if (string.Equals(GetTargetPath(sel), CountTargetPath)) continue;

                var targetField = fields.FirstOrDefault
                (
                    x =>  g.LinkedSelect == null 
                    ? string.Equals(x.Name, tar, StringComparison.InvariantCultureIgnoreCase) 
                    : string.Equals(x.Name, tar2, StringComparison.InvariantCultureIgnoreCase 
                ));

                var binding = Expression.Bind(targetField, Expression.MakeMemberAccess(lambdaParam, targetField));
                groupingDimensionBindings.Add(binding);
            }
            

            /*
            foreach (var x in elementType.GetFields())
            {
                if (selects.Any(s => !s.IsBasedOnGrouping && string.Equals(x.Name, GetTargetPath(s), StringComparison.InvariantCultureIgnoreCase)))
                    continue;



                var binding = Expression.Bind(x, Expression.MakeMemberAccess(lambdaParam, GetField(elementType, x.Name)));
                groupingDimensionBindings.Add(binding);
            }*/

            var groupByLambda = Expression.Lambda
            (
                parameters: lambdaParam,
                body: Expression.MemberInit
                (
                    Expression.New(elementType),
                    groupingDimensionBindings
                )
            );

            var pointGroups = Expression.Call(method: groupByInfo, arg0: collectionParameter, arg1: groupByLambda);

            var enumType = pointGroups.Type;
            var groupingType = GetTypeOfEnumerable(enumType);
            var genericSelectInfo = typeof(Enumerable).GetMethods().FirstOrDefault(x => x.Name == "Select" && x.IsGenericMethod && x.GetParameters().Length == 2);
            var selectInfo0 = genericSelectInfo.MakeGenericMethod(groupingType, elementType);

            var selectGroupParam = Expression.Parameter(type: groupingType, name: "g");
            //var selectBody = Expression.Invoke(mergeExpr, selectGroupParam);
            var mergeExpr = GetMergeExpression(elementType, selectGroupParam, selects, groupBy );


            var selectLambda = Expression.Lambda(parameters: selectGroupParam, body: mergeExpr);

            var selectExpr = Expression.Call(method: selectInfo0, arg0: pointGroups, arg1: selectLambda);

            //selectExpr = AppendToArray(elementType, selectExpr);

            var finalLambda = Expression.Lambda(selectExpr, collectionParameter);

            return finalLambda;
        }