protected override Expression VisitExtension(Expression extensionExpression) { if (extensionExpression is NavigationBindingExpression navigationBindingExpression && navigationBindingExpression.RootParameter == _rootParameter) { var result = navigationBindingExpression.RootParameter.BuildPropertyAccess(navigationBindingExpression.NavigationTreeNode.ToMapping); return(result.Type != navigationBindingExpression.Type ? Expression.Convert(result, navigationBindingExpression.Type) : result); } if (extensionExpression is CustomRootExpression customRootExpression && customRootExpression.RootParameter == _rootParameter) { var result = _rootParameter.BuildPropertyAccess(customRootExpression.Mapping); return(result.Type != customRootExpression.Type ? Expression.Convert(result, customRootExpression.Type) : result); } if (extensionExpression is NavigationExpansionRootExpression || extensionExpression is NavigationExpansionExpression || extensionExpression is IncludeExpression) { var result = new NavigationExpansionReducingVisitor().Visit(extensionExpression); return(Visit(result)); } return(base.VisitExtension(extensionExpression)); }
protected override Expression VisitExtension(Expression extensionExpression) { switch (extensionExpression) { case NavigationBindingExpression navigationBindingExpression when navigationBindingExpression.RootParameter == _rootParameter: { var node = navigationBindingExpression.NavigationTreeNode; var navigations = new List <INavigation>(); while (node != null) { if (node.Navigation != null) { navigations.Add(node.Navigation); } node = node.Parent; } var result = navigationBindingExpression.RootParameter.BuildPropertyAccess( navigationBindingExpression.NavigationTreeNode.ToMapping, navigations.Count == navigationBindingExpression.NavigationTreeNode.ToMapping.Count ? navigations : null); return(result.Type != navigationBindingExpression.Type ? Expression.Convert(result, navigationBindingExpression.Type) : result); } case CustomRootExpression customRootExpression when customRootExpression.RootParameter == _rootParameter: { var result = _rootParameter.BuildPropertyAccess(customRootExpression.Mapping); return(result.Type != customRootExpression.Type ? Expression.Convert(result, customRootExpression.Type) : result); } case NavigationExpansionRootExpression _: case NavigationExpansionExpression _: { var result = new NavigationExpansionReducingVisitor().Visit(extensionExpression); return(Visit(result)); } default: return(base.VisitExtension(extensionExpression)); } }
private Expression ProcessMemberPushdown( Expression source, NavigationExpansionExpression navigationExpansionExpression, bool efProperty, MemberInfo memberInfo, string propertyName, Type resultType) { // in case of nested FirstOrDefaults, we need to dig into the inner most - that's where the member finally gets pushed down to if (navigationExpansionExpression.State.PendingSelector.Body is NavigationExpansionExpression navigationExpansionPendingSelector && navigationExpansionPendingSelector.State.PendingCardinalityReducingOperator != null) { var newPendingSelector = (NavigationExpansionExpression)ProcessMemberPushdown(source, navigationExpansionPendingSelector, efProperty, memberInfo, propertyName, resultType); var newStateNested = new NavigationExpansionExpressionState( navigationExpansionExpression.State.CurrentParameter, navigationExpansionExpression.State.SourceMappings, Expression.Lambda(newPendingSelector, navigationExpansionExpression.State.CurrentParameter), applyPendingSelector: true, navigationExpansionExpression.State.PendingOrderings, navigationExpansionExpression.State.PendingIncludeChain, // we need to remap cardinality reducing operator since it's source type has now changed navigationExpansionExpression.State.PendingCardinalityReducingOperator.GetGenericMethodDefinition().MakeGenericMethod(newPendingSelector.Type), navigationExpansionExpression.State.PendingTags, navigationExpansionExpression.State.CustomRootMappings, navigationExpansionExpression.State.MaterializeCollectionNavigation); return(new NavigationExpansionExpression( navigationExpansionExpression.Operand, newStateNested, resultType)); } var selectorParameter = Expression.Parameter(source.Type, navigationExpansionExpression.State.CurrentParameter.Name); var selectorBody = efProperty ? (Expression)Expression.Call(EF.PropertyMethod.MakeGenericMethod(resultType), selectorParameter, Expression.Constant(propertyName)) : Expression.MakeMemberAccess(selectorParameter, memberInfo); if (navigationExpansionExpression.State.PendingCardinalityReducingOperator.MethodIsClosedFormOf(LinqMethodHelpers.QueryableFirstOrDefaultMethodInfo) || navigationExpansionExpression.State.PendingCardinalityReducingOperator.MethodIsClosedFormOf(LinqMethodHelpers.QueryableFirstOrDefaultPredicateMethodInfo) || navigationExpansionExpression.State.PendingCardinalityReducingOperator.MethodIsClosedFormOf(LinqMethodHelpers.QueryableSingleOrDefaultMethodInfo) || navigationExpansionExpression.State.PendingCardinalityReducingOperator.MethodIsClosedFormOf(LinqMethodHelpers.QueryableSingleOrDefaultPredicateMethodInfo) || navigationExpansionExpression.State.PendingCardinalityReducingOperator.MethodIsClosedFormOf(LinqMethodHelpers.EnumerableFirstOrDefaultMethodInfo) || navigationExpansionExpression.State.PendingCardinalityReducingOperator.MethodIsClosedFormOf(LinqMethodHelpers.EnumerableFirstOrDefaultPredicateMethodInfo) || navigationExpansionExpression.State.PendingCardinalityReducingOperator.MethodIsClosedFormOf(LinqMethodHelpers.EnumerableSingleOrDefaultMethodInfo) || navigationExpansionExpression.State.PendingCardinalityReducingOperator.MethodIsClosedFormOf(LinqMethodHelpers.EnumerableSingleOrDefaultPredicateMethodInfo)) { if (!selectorBody.Type.IsNullableType()) { selectorBody = Expression.Convert(selectorBody, selectorBody.Type.MakeNullable()); } } var selector = Expression.Lambda(selectorBody, selectorParameter); var remappedSelectorBody = ExpressionExtensions.CombineAndRemap(selector.Body, selectorParameter, navigationExpansionExpression.State.PendingSelector.Body); var binder = new NavigationPropertyBindingVisitor( navigationExpansionExpression.State.CurrentParameter, navigationExpansionExpression.State.SourceMappings); var boundSelectorBody = binder.Visit(remappedSelectorBody); if (boundSelectorBody is NavigationBindingExpression navigationBindingExpression && navigationBindingExpression.NavigationTreeNode.Navigation is INavigation lastNavigation && lastNavigation != null) { if (lastNavigation.IsCollection()) { var collectionNavigationElementType = lastNavigation.ForeignKey.DeclaringEntityType.ClrType; var entityQueryable = NullAsyncQueryProvider.Instance.CreateEntityQueryableExpression(collectionNavigationElementType); var outerParameter = Expression.Parameter(collectionNavigationElementType, collectionNavigationElementType.GenerateParameterName()); var outerKeyAccess = NavigationExpansionHelpers.CreateKeyAccessExpression( outerParameter, lastNavigation.ForeignKey.Properties); var innerParameter = Expression.Parameter(navigationExpansionExpression.Type); var innerKeyAccessLambda = Expression.Lambda( NavigationExpansionHelpers.CreateKeyAccessExpression( innerParameter, lastNavigation.ForeignKey.PrincipalKey.Properties), innerParameter); var combinedKeySelectorBody = ExpressionExtensions.CombineAndRemap(innerKeyAccessLambda.Body, innerKeyAccessLambda.Parameters[0], navigationExpansionExpression.State.PendingSelector.Body); if (outerKeyAccess.Type != combinedKeySelectorBody.Type) { if (combinedKeySelectorBody.Type.IsNullableType()) { outerKeyAccess = Expression.Convert(outerKeyAccess, combinedKeySelectorBody.Type); } else { combinedKeySelectorBody = Expression.Convert(combinedKeySelectorBody, outerKeyAccess.Type); } } var rewrittenState = new NavigationExpansionExpressionState( navigationExpansionExpression.State.CurrentParameter, navigationExpansionExpression.State.SourceMappings, Expression.Lambda(combinedKeySelectorBody, navigationExpansionExpression.State.CurrentParameter), applyPendingSelector: true, navigationExpansionExpression.State.PendingOrderings, navigationExpansionExpression.State.PendingIncludeChain, // we need to remap cardinality reducing operator since it's source type has now changed navigationExpansionExpression.State.PendingCardinalityReducingOperator.GetGenericMethodDefinition().MakeGenericMethod(combinedKeySelectorBody.Type), navigationExpansionExpression.State.PendingTags, navigationExpansionExpression.State.CustomRootMappings, materializeCollectionNavigation: null); var rewrittenNavigationExpansionExpression = new NavigationExpansionExpression(navigationExpansionExpression.Operand, rewrittenState, combinedKeySelectorBody.Type); var inner = new NavigationExpansionReducingVisitor().Visit(rewrittenNavigationExpansionExpression); var predicate = Expression.Lambda( Expression.Equal(outerKeyAccess, inner), outerParameter); var whereMethodInfo = LinqMethodHelpers.QueryableWhereMethodInfo.MakeGenericMethod(collectionNavigationElementType); var rewritten = Expression.Call( whereMethodInfo, entityQueryable, predicate); var entityType = lastNavigation.ForeignKey.DeclaringEntityType; return(NavigationExpansionHelpers.CreateNavigationExpansionRoot(rewritten, entityType, materializeCollectionNavigation: null)); } else { return(ProcessSelectCore( navigationExpansionExpression.Operand, navigationExpansionExpression.State, selector, selectorBody.Type)); } } var newState = new NavigationExpansionExpressionState( navigationExpansionExpression.State.CurrentParameter, navigationExpansionExpression.State.SourceMappings, Expression.Lambda(boundSelectorBody, navigationExpansionExpression.State.CurrentParameter), applyPendingSelector: true, navigationExpansionExpression.State.PendingOrderings, navigationExpansionExpression.State.PendingIncludeChain, // we need to remap cardinality reducing operator since it's source type has now changed navigationExpansionExpression.State.PendingCardinalityReducingOperator.GetGenericMethodDefinition().MakeGenericMethod(boundSelectorBody.Type), navigationExpansionExpression.State.PendingTags, navigationExpansionExpression.State.CustomRootMappings, navigationExpansionExpression.State.MaterializeCollectionNavigation); // TODO: expand navigations var result = new NavigationExpansionExpression( navigationExpansionExpression.Operand, newState, selectorBody.Type); return(resultType != result.Type ? (Expression)Expression.Convert(result, resultType) : result); }