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