private MethodCallExpression ApplyAggregate(Expression source, AggregateTransformationNode transformation) { Type sourceType = OeExpressionHelper.GetCollectionItemType(source.Type); ParameterExpression sourceParameter = Expression.Parameter(sourceType); ParameterExpression lambdaParameter = sourceParameter; var expressions = new List <Expression>(); bool isGroupBy = sourceType.GetGenericTypeDefinition() == typeof(IGrouping <,>); if (isGroupBy) { PropertyInfo keyProperty = sourceType.GetProperty(nameof(IGrouping <Object, Object> .Key)); MemberExpression key = Expression.Property(sourceParameter, keyProperty); expressions.Add(key); lambdaParameter = Expression.Parameter(sourceType.GetGenericArguments()[1]); } var visitor = CreateVisitor(lambdaParameter); foreach (AggregateExpressionBase aggExpressionBase in transformation.AggregateExpressions) { if (aggExpressionBase is AggregateExpression aggExpression) { MethodCallExpression aggCallExpression; if (aggExpression.Method == AggregationMethod.VirtualPropertyCount) { aggCallExpression = CountExpression(sourceParameter); } else { Expression expression = visitor.TranslateNode(aggExpression.Expression); if (isGroupBy && expression is MemberExpression propertyExpression) { MemberExpression?keyPropertyExpression = FindInGroupByKey(source, expressions[0], propertyExpression); if (keyPropertyExpression != null) { expression = keyPropertyExpression; } } LambdaExpression aggLambda = Expression.Lambda(expression, lambdaParameter); aggCallExpression = AggCallExpression(aggExpression.Method, sourceParameter, aggLambda); } expressions.Add(aggCallExpression); _aggProperties.Add(CreateEdmProperty(aggCallExpression.Type, aggExpression.Alias, false)); } else { throw new NotSupportedException("Unknown aggregate expression type " + aggExpressionBase.GetType().Name); } } NewExpression newExpression = OeExpressionHelper.CreateTupleExpression(expressions); MethodInfo selectMethodInfo = OeMethodInfoHelper.GetSelectMethodInfo(sourceType, newExpression.Type); LambdaExpression lambda = Expression.Lambda(newExpression, sourceParameter); return(Expression.Call(selectMethodInfo, source, lambda)); }
private static LambdaExpression GetSelectManyCollectionSelector(LambdaExpression groupJoinResultSelect) { ParameterExpression parameter = Expression.Parameter(groupJoinResultSelect.ReturnType); MemberExpression innerSource = Expression.Property(parameter, parameter.Type.GetProperty(nameof(Tuple <Object, Object> .Item2))); Type innerType = OeExpressionHelper.GetCollectionItemType(innerSource.Type); MethodCallExpression defaultIfEmptyCall = Expression.Call(OeMethodInfoHelper.GetDefaultIfEmptyMethodInfo(innerType), innerSource); return(Expression.Lambda(defaultIfEmptyCall, parameter)); }
public Expression Build(Expression source, IReadOnlyList <OeSkipTokenNameValue> skipTokenNameValues, OrderByClause uniqueOrderBy) { OrderProperty[] orderProperties = CreateOrderProperies(source, skipTokenNameValues, uniqueOrderBy); Expression filter = CreateFilterExpression(_isDatabaseNullHighestValue, orderProperties); LambdaExpression lambda = Expression.Lambda(filter, _visitor.Parameter); MethodInfo whereMethodInfo = OeMethodInfoHelper.GetWhereMethodInfo(_visitor.Parameter.Type); return(Expression.Call(whereMethodInfo, source, lambda)); }
private static MethodCallExpression CreateWhereExpression(ParameterExpression sourceParameter, Expression subquery, IEdmNavigationProperty edmNavigationProperty) { Type subqueryType = OeExpressionHelper.GetCollectionItemType(subquery.Type); var subqueryParameter = Expression.Parameter(subqueryType, subqueryType.Name); BinaryExpression joinExpression = GetJoinExpression(sourceParameter, subqueryParameter, edmNavigationProperty); LambdaExpression predicate = Expression.Lambda(joinExpression, subqueryParameter); MethodInfo whereMethodInfo = OeMethodInfoHelper.GetWhereMethodInfo(subqueryType); return(Expression.Call(whereMethodInfo, subquery, predicate)); }
private static MethodCallExpression CountDistinctExpression(ParameterExpression sourceParameter, LambdaExpression lambda) { MethodInfo selectMetodInfo = OeMethodInfoHelper.GetSelectMethodInfo(lambda.Parameters[0].Type, lambda.ReturnType); MethodCallExpression selectCall = Expression.Call(selectMetodInfo, sourceParameter, lambda); MethodInfo distinctMethodInfo = OeMethodInfoHelper.GetDistinctMethodInfo(lambda.ReturnType); MethodCallExpression distinctCall = Expression.Call(distinctMethodInfo, selectCall); MethodInfo countMethodInfo = OeMethodInfoHelper.GetCountMethodInfo(lambda.ReturnType); return(Expression.Call(countMethodInfo, distinctCall)); }
private MethodCallExpression ApplyGroupBy(Expression source, GroupByTransformationNode transformation) { Type sourceType = OeExpressionHelper.GetCollectionItemType(source.Type); ParameterExpression sourceParameter = Expression.Parameter(sourceType); var visitor = CreateVisitor(sourceParameter); var expressions = new List <Expression>(); foreach (GroupByPropertyNode node in transformation.GroupingProperties) { if (node.ChildTransformations != null && node.ChildTransformations.Count > 0) { for (int i = 0; i < node.ChildTransformations.Count; i++) { Expression e = visitor.TranslateNode(node.ChildTransformations[i].Expression); expressions.Add(e); String aliasName = node.Name + "_" + node.ChildTransformations[i].Name; _aggProperties.Add(CreateEdmProperty(visitor.EdmModel, e.Type, aliasName, true)); } } else { Expression e = visitor.TranslateNode(node.Expression); expressions.Add(e); _aggProperties.Add(CreateEdmProperty(visitor.EdmModel, e.Type, node.Name, true)); } } NewExpression newExpression = OeExpressionHelper.CreateTupleExpression(expressions); LambdaExpression lambda = Expression.Lambda(newExpression, sourceParameter); MethodInfo groupByMethodInfo = OeMethodInfoHelper.GetGroupByMethodInfo(sourceType, newExpression.Type); MethodCallExpression groupByCall = Expression.Call(groupByMethodInfo, source, lambda); var aggTransformation = (AggregateTransformationNode)transformation.ChildTransformations; if (aggTransformation == null) { expressions.Clear(); sourceType = OeExpressionHelper.GetCollectionItemType(groupByCall.Type); sourceParameter = Expression.Parameter(sourceType); expressions.Add(Expression.Property(sourceParameter, nameof(IGrouping <Object, Object> .Key))); newExpression = OeExpressionHelper.CreateTupleExpression(expressions); MethodInfo selectMethodInfo = OeMethodInfoHelper.GetSelectMethodInfo(sourceType, newExpression.Type); lambda = Expression.Lambda(newExpression, sourceParameter); return(Expression.Call(selectMethodInfo, groupByCall, lambda)); } return(ApplyAggregate(groupByCall, aggTransformation)); }
private static MethodInfo GetOrderByMethodInfo(Expression source, OrderByDirection direction, Type sourceType, Type keyType) { if (source.Type.GetGenericTypeDefinition() == typeof(IOrderedEnumerable <>)) { return(direction == OrderByDirection.Ascending ? OeMethodInfoHelper.GetThenByMethodInfo(sourceType, keyType) : OeMethodInfoHelper.GetThenByDescendingMethodInfo(sourceType, keyType)); } else { return(direction == OrderByDirection.Ascending ? OeMethodInfoHelper.GetOrderByMethodInfo(sourceType, keyType) : OeMethodInfoHelper.GetOrderByDescendingMethodInfo(sourceType, keyType)); } }
private static MethodCallExpression AggCallExpression(AggregationMethod aggMethod, ParameterExpression sourceParameter, LambdaExpression lambda) { String methodName; switch (aggMethod) { case AggregationMethod.Average: methodName = nameof(Enumerable.Average); break; case AggregationMethod.CountDistinct: return(CountDistinctExpression(sourceParameter, lambda)); case AggregationMethod.Max: methodName = nameof(Enumerable.Max); break; case AggregationMethod.Min: methodName = nameof(Enumerable.Min); break; case AggregationMethod.Sum: methodName = nameof(Enumerable.Sum); break; case AggregationMethod.VirtualPropertyCount: return(CountExpression(sourceParameter)); default: throw new NotSupportedException(); } MethodInfo closeMethod; MethodInfo openMethod = OeMethodInfoHelper.GetAggMethodInfo(methodName, lambda.ReturnType); if (openMethod.GetGenericArguments().Length == 1) { closeMethod = openMethod.MakeGenericMethod(lambda.Parameters[0].Type); } else { closeMethod = openMethod.MakeGenericMethod(lambda.Parameters[0].Type, lambda.ReturnType); } return(Expression.Call(closeMethod, sourceParameter, lambda)); }
public static MethodCallExpression Build(Expression outer, Expression inner, ExpandedNavigationSelectItem item, ODataPath odataPath, OeExpressionBuilder expressionBuilder) { var segment = (NavigationPropertySegment)item.PathToNavigationProperty.LastSegment; Type outerType = OeExpressionHelper.GetCollectionItemType(outer.Type); var outerParameter = Expression.Parameter(outerType, outerType.Name); Expression subquery = CreateWhereExpression(outerParameter, inner, segment.NavigationProperty); subquery = expressionBuilder.ApplyOrderBy(subquery, item.OrderByOption); subquery = expressionBuilder.ApplySkip(subquery, item.SkipOption, odataPath); subquery = expressionBuilder.ApplyTake(subquery, item.TopOption, odataPath); Type innerType = OeExpressionHelper.GetCollectionItemType(inner.Type); MethodInfo selectManyMethdoInfo = OeMethodInfoHelper.GetSelectManyMethodInfo(outerType, innerType); return(Expression.Call(selectManyMethdoInfo, outer, Expression.Lambda(subquery, outerParameter))); }
private MethodCallExpression Build(Expression outerSource, Expression innerSource, IReadOnlyList <IEdmNavigationProperty> joinPath, IEdmNavigationProperty navigationProperty, bool manyToMany) { Type outerType = OeExpressionHelper.GetCollectionItemType(outerSource.Type); Type innerType = OeExpressionHelper.GetCollectionItemType(innerSource.Type); (LambdaExpression groupJoinOuterKeySelector, LambdaExpression groupJoinInnerKeySelector) = GetJoinKeySelector(outerType, innerType, joinPath, navigationProperty); LambdaExpression groupJoinResultSelect = GetGroupJoinResultSelector(outerType, innerType, manyToMany); MethodInfo groupJoinMethodInfo = OeMethodInfoHelper.GetGroupJoinMethodInfo(outerType, innerType, groupJoinOuterKeySelector.ReturnType, groupJoinResultSelect.ReturnType); MethodCallExpression groupJoinCall = Expression.Call(groupJoinMethodInfo, outerSource, innerSource, groupJoinOuterKeySelector, groupJoinInnerKeySelector, groupJoinResultSelect); LambdaExpression selectManySource = GetSelectManyCollectionSelector(groupJoinResultSelect); LambdaExpression selectManyResultSelector = GetSelectManyResultSelector(groupJoinResultSelect); MethodInfo selectManyMethodInfo = OeMethodInfoHelper.GetSelectManyMethodInfo(groupJoinResultSelect.ReturnType, innerType, selectManyResultSelector.ReturnType); return(Expression.Call(selectManyMethodInfo, groupJoinCall, selectManySource, selectManyResultSelector)); }
private MethodCallExpression ApplyFilter(Expression source, FilterTransformationNode transformation) { Type sourceType = OeExpressionHelper.GetCollectionItemType(source.Type); ParameterExpression sourceParameter = Expression.Parameter(sourceType); var visitor = CreateVisitor(sourceParameter); if (_aggProperties.Count > 0) { visitor.TuplePropertyByAliasName = GetTuplePropertyByAliasName; } Expression e = visitor.TranslateNode(transformation.FilterClause.Expression); MethodInfo whereMethodInfo = OeMethodInfoHelper.GetWhereMethodInfo(sourceParameter.Type); LambdaExpression lambda = Expression.Lambda(e, sourceParameter); return(Expression.Call(whereMethodInfo, source, lambda)); }
protected override Expression VisitNew(NewExpression node) { var arguments = new Expression[node.Arguments.Count]; for (int i = 0; i < arguments.Length; i++) { Expression argument = base.Visit(node.Arguments[i]); if (argument is MethodCallExpression call && call.Type.GetTypeInfo().IsGenericType&& call.Type.GetGenericTypeDefinition() == typeof(IOrderedEnumerable <>)) { Type type = call.Type.GetGenericArguments()[0]; MethodInfo selectMethodInfo = OeMethodInfoHelper.GetSelectMethodInfo(type, type); ParameterExpression parameter = Expression.Parameter(type); argument = Expression.Call(selectMethodInfo, call, Expression.Lambda(parameter, parameter)); } arguments[i] = argument; } return(OeExpressionHelper.CreateTupleExpression(arguments)); }
internal MethodCallExpression BuildExpandCount(Expression outer, Expression inner, OeNavigationSelectItem navigationItem) { IEdmNavigationProperty navigationProperty = navigationItem.EdmProperty; if (navigationItem.EdmProperty.ContainsTarget) { throw new InvalidOperationException("Expand cout for many-tomany not supported"); } Type outerType = OeExpressionHelper.GetCollectionItemType(outer.Type); Type innerType = OeExpressionHelper.GetCollectionItemType(inner.Type); ParameterExpression outerParameter = Expression.Parameter(outerType, outerType.Name); IReadOnlyList <IEdmNavigationProperty> joinPath = navigationItem.Parent == null?Array.Empty <IEdmNavigationProperty>() : navigationItem.Parent.GetJoinPath(); Expression?joinPropertyExpression = _expressionBuilder.JoinBuilder.GetJoinPropertyExpression(outerParameter, joinPath); if (joinPropertyExpression == null) { throw new InvalidOperationException("Outer source not found"); } MethodCallExpression subquery = CreateWhereExpression(joinPropertyExpression, inner, navigationProperty); MethodInfo methodInfo = OeMethodInfoHelper.GetCountMethodInfo(innerType); subquery = Expression.Call(methodInfo, subquery); List <Expression> expressions = new List <Expression>(); if (OeExpressionHelper.IsTupleType(outerParameter.Type)) { expressions.AddRange(OeExpressionHelper.GetPropertyExpressions(outerParameter)); } else { expressions.Add(outerParameter); } expressions.Add(OeExpressionHelper.CreateTupleExpression(new[] { subquery })); NewExpression newTuple = OeExpressionHelper.CreateTupleExpression(expressions); methodInfo = OeMethodInfoHelper.GetSelectMethodInfo(outerType, newTuple.Type); return(Expression.Call(methodInfo, outer, Expression.Lambda(newTuple, outerParameter))); }
private MethodCallExpression ApplyCompute(Expression source, ComputeTransformationNode transformation) { var expressions = new List <Expression>(); Type sourceType = OeExpressionHelper.GetCollectionItemType(source.Type); ParameterExpression sourceParameter = Expression.Parameter(sourceType); if (_aggProperties.Count > 0) { var callExpression = (MethodCallExpression)source; source = callExpression.Arguments[0]; var aggLambda = (LambdaExpression)callExpression.Arguments[1]; expressions.AddRange(((NewExpression)aggLambda.Body).Arguments); sourceType = OeExpressionHelper.GetCollectionItemType(source.Type); sourceParameter = aggLambda.Parameters[0]; } OeQueryNodeVisitor visitor = CreateVisitor(sourceParameter); if (_aggProperties.Count > 0) { visitor.TuplePropertyByAliasName = new ComputeAliasNameResolver(_aggProperties, expressions).GetTuplePropertyByAliasName; } foreach (ComputeExpression computeExpression in transformation.Expressions) { Expression expression = visitor.TranslateNode(computeExpression.Expression); expressions.Add(expression); _aggProperties.Add(CreateEdmProperty(visitor.EdmModel, expression.Type, computeExpression.Alias, false)); } NewExpression newExpression = OeExpressionHelper.CreateTupleExpression(expressions); MethodInfo selectMethodInfo = OeMethodInfoHelper.GetSelectMethodInfo(sourceParameter.Type, newExpression.Type); LambdaExpression lambda = Expression.Lambda(newExpression, sourceParameter); return(Expression.Call(selectMethodInfo, source, lambda)); }
private static Expression SelectStructuralProperties(Expression source, OeNavigationSelectItem root) { if (!root.HasNavigationItems()) { return(source); } ParameterExpression parameter = Expression.Parameter(OeExpressionHelper.GetCollectionItemType(source.Type)); IReadOnlyList <MemberExpression> joins = OeExpressionHelper.GetPropertyExpressions(parameter); var newJoins = new Expression[joins.Count]; List <OeNavigationSelectItem> navigationItems = FlattenNavigationItems(root, false); bool isNavigationNullable = false; for (int i = 0; i < navigationItems.Count; i++) { newJoins[i] = joins[i]; isNavigationNullable |= i > 0 && navigationItems[i].EdmProperty.Type.IsNullable; if (!navigationItems[i].AllSelected) { IReadOnlyList <OeStructuralSelectItem> structuralItems = navigationItems[i].GetStructuralItemsWithNotSelected(); if (structuralItems.Count > 0) { var properties = new Expression[structuralItems.Count]; for (int j = 0; j < structuralItems.Count; j++) { if (structuralItems[j].EdmProperty is ComputeProperty computeProperty) { properties[j] = new ReplaceParameterVisitor(joins[i]).Visit(computeProperty.Expression); } else { PropertyInfo property = joins[i].Type.GetPropertyIgnoreCase(structuralItems[j].EdmProperty); properties[j] = Expression.Property(joins[i], property); } } Expression newTupleExpression = OeExpressionHelper.CreateTupleExpression(properties); if (isNavigationNullable) { UnaryExpression nullConstant = Expression.Convert(OeConstantToVariableVisitor.NullConstantExpression, newTupleExpression.Type); newTupleExpression = Expression.Condition(Expression.Equal(joins[i], OeConstantToVariableVisitor.NullConstantExpression), nullConstant, newTupleExpression); } newJoins[i] = newTupleExpression; } } } NewExpression newSelectorBody = OeExpressionHelper.CreateTupleExpression(newJoins); MethodInfo selectMethodInfo = OeMethodInfoHelper.GetSelectMethodInfo(parameter.Type, newSelectorBody.Type); LambdaExpression newSelector = Expression.Lambda(newSelectorBody, parameter); //Quirk EF Core 2.1.1 bug Take/Skip must be last in expression tree var skipTakeExpressions = new List <MethodCallExpression>(); while (source is MethodCallExpression callExpression && (callExpression.Method.Name == nameof(Enumerable.Skip) || callExpression.Method.Name == nameof(Enumerable.Take))) { skipTakeExpressions.Add(callExpression); source = callExpression.Arguments[0]; } source = Expression.Call(selectMethodInfo, source, newSelector); for (int i = skipTakeExpressions.Count - 1; i >= 0; i--) { MethodInfo skipTakeMethodInfo = skipTakeExpressions[i].Method.GetGenericMethodDefinition().MakeGenericMethod(newSelector.ReturnType); source = Expression.Call(skipTakeMethodInfo, source, skipTakeExpressions[i].Arguments[1]); } return(source); }
private static MethodCallExpression AggCallExpression(AggregationMethod aggMethod, ParameterExpression sourceParameter, LambdaExpression lambda) { String methodName; switch (aggMethod) { case AggregationMethod.Average: methodName = nameof(Enumerable.Average); break; case AggregationMethod.CountDistinct: return(CountDistinctExpression(sourceParameter, lambda)); case AggregationMethod.Max: methodName = nameof(Enumerable.Max); break; case AggregationMethod.Min: methodName = nameof(Enumerable.Min); break; case AggregationMethod.Sum: methodName = nameof(Enumerable.Sum); break; case AggregationMethod.VirtualPropertyCount: return(CountExpression(sourceParameter)); default: throw new NotSupportedException(); } MethodInfo closeMethod; MethodInfo openMethod = OeMethodInfoHelper.GetAggMethodInfo(methodName, lambda.ReturnType); if (openMethod == null) { Func <IEnumerable <Object>, Func <Object, Object>, Object> aggFunc; switch (aggMethod) { case AggregationMethod.Max: aggFunc = Enumerable.Max; break; case AggregationMethod.Min: aggFunc = Enumerable.Min; break; default: throw new InvalidOperationException($"Enumerable.{methodName} not found");; } openMethod = aggFunc.GetMethodInfo().GetGenericMethodDefinition(); closeMethod = openMethod.MakeGenericMethod(lambda.Parameters[0].Type, lambda.ReturnType); } else { closeMethod = openMethod.MakeGenericMethod(lambda.Parameters[0].Type); } return(Expression.Call(closeMethod, sourceParameter, lambda)); }