private static Expression MakeWhereExpression(Expression filter, Expression collectionParameter, DimensionExpression[] groupBy, Type itemType) { List <Expression> filters = new List <Expression>(); var elementType = GetTypeOfEnumerable(collectionParameter.Type); var genericWhereInfo = typeof(Enumerable).GetMethods().FirstOrDefault(x => x.Name == "Where" && x.IsGenericMethod && x.GetParameters().Length == 2); var whereInfo = genericWhereInfo.MakeGenericMethod(elementType); var mentionParameter = Expression.Parameter(type: elementType, name: GenerateParamName(elementType)); if (groupBy != null) { var collectionFields = groupBy.Where(x => x.Source != null).Select(x => new { x.Source, Member = GetMember(itemType, x.Source) }); collectionFields = collectionFields.Where(x => TypeImplements(GetMemberType(x.Member), typeof(IEnumerable <>))); var count = collectionFields.Count(); if (count == 0 && filter == null) { return(collectionParameter); } filters.AddRange(collectionFields.Select ( x => Expression.ReferenceNotEqual ( left: Expression.MakeMemberAccess(mentionParameter, x.Member), right: Expression.Constant(null) ) ).Cast <Expression>()); } if (filter != null) { filters.Add(ParameterRebinder.ReplaceParameters(filter, "x", mentionParameter)); } if (!filters.Any()) { return(collectionParameter); } Expression sourceCollection = collectionParameter; sourceCollection = AppendAsParallel(typeof(ParallelEnumerable), collectionParameter); var whereBody = ChainAndExpressionCollection(filters); var result = Expression.Call ( method: whereInfo, arguments: new Expression[] { sourceCollection, Expression.Lambda ( delegateType: typeof(Func <,>).MakeGenericType(elementType, typeof(bool)), parameters: mentionParameter, body: whereBody ) } ); //collectionParameter = Expression.Parameter(type: result.Type, name: "c"); return(result); }
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"); }