private (NavigationExpansionExpression navigationExpression, Expression nullKeyExpression) CreateNullComparisonArguments( NavigationBindingExpression navigationBindingExpression, NavigationExpansionExpression navigationExpansionExpression) { var navigationKeyAccessExpression = NavigationExpansionHelpers.CreateKeyAccessExpression( navigationBindingExpression, new[] { navigationBindingExpression.EntityType.FindPrimaryKey().Properties.First() }, addNullCheck: true); var nullKeyExpression = NavigationExpansionHelpers.CreateNullKeyExpression( navigationKeyAccessExpression.Type, keyCount: 1); var newNavigationExpansionExpressionState = new NavigationExpansionExpressionState( navigationExpansionExpression.State.CurrentParameter, navigationExpansionExpression.State.SourceMappings, Expression.Lambda(navigationKeyAccessExpression, navigationExpansionExpression.State.PendingSelector.Parameters[0]), 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(navigationKeyAccessExpression.Type), navigationExpansionExpression.State.CustomRootMappings, navigationExpansionExpression.State.MaterializeCollectionNavigation); var navigationExpression = new NavigationExpansionExpression( navigationExpansionExpression.Operand, newNavigationExpansionExpressionState, navigationKeyAccessExpression.Type); return(navigationExpression, nullKeyExpression); }
public NavigationExpansionExpression( Expression operand, NavigationExpansionExpressionState state, Type returnType) { Operand = operand; State = state; _returnType = returnType; }
private (Expression Operand, NavigationExpansionExpressionState State) ApplyIncludes( NavigationExpansionExpression navigationExpansionExpression) { var includeVisitor = new PendingSelectorIncludeVisitor(); var rewrittenBody = includeVisitor.Visit(navigationExpansionExpression.State.PendingSelector.Body); if (navigationExpansionExpression.State.PendingSelector.Body != rewrittenBody) { navigationExpansionExpression.State.PendingSelector = Expression.Lambda(rewrittenBody, navigationExpansionExpression.State.PendingSelector.Parameters[0]); navigationExpansionExpression.State.ApplyPendingSelector = true; } if (includeVisitor.PendingIncludes.Count > 0) { var result = (Source : navigationExpansionExpression.Operand, Parameter : navigationExpansionExpression.State.CurrentParameter); foreach (var pendingIncludeNode in includeVisitor.PendingIncludes) { result = NavigationExpansionHelpers.AddNavigationJoin( result.Source, result.Parameter, pendingIncludeNode.SourceMapping, pendingIncludeNode.NavTreeNode, navigationExpansionExpression.State, new List <INavigation>(), include: true); } var pendingSelector = navigationExpansionExpression.State.PendingSelector; if (navigationExpansionExpression.State.CurrentParameter != result.Parameter) { var pendingSelectorBody = new ExpressionReplacingVisitor(navigationExpansionExpression.State.CurrentParameter, result.Parameter).Visit(navigationExpansionExpression.State.PendingSelector.Body); pendingSelector = Expression.Lambda(pendingSelectorBody, result.Parameter); } var newState = new NavigationExpansionExpressionState( result.Parameter, navigationExpansionExpression.State.SourceMappings, pendingSelector, applyPendingSelector: true, navigationExpansionExpression.State.PendingOrderings, navigationExpansionExpression.State.PendingIncludeChain, navigationExpansionExpression.State.PendingCardinalityReducingOperator, navigationExpansionExpression.State.CustomRootMappings, navigationExpansionExpression.State.MaterializeCollectionNavigation); return(Operand : result.Source, newState); } return(navigationExpansionExpression.Operand, navigationExpansionExpression.State); }
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.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 = ReplacingExpressionVisitor.Replace( selectorParameter, navigationExpansionExpression.State.PendingSelector.Body, selector.Body); var binder = new NavigationPropertyBindingVisitor( navigationExpansionExpression.State.CurrentParameter, navigationExpansionExpression.State.SourceMappings); var boundSelectorBody = binder.Visit(remappedSelectorBody); if (boundSelectorBody is NavigationBindingExpression navigationBindingExpression && navigationBindingExpression.NavigationTreeNode.Navigation != null) { if (navigationBindingExpression.NavigationTreeNode.IsCollection) { var lastNavigation = navigationBindingExpression.NavigationTreeNode.Navigation; 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 = ReplacingExpressionVisitor.Replace( innerKeyAccessLambda.Parameters[0], navigationExpansionExpression.State.PendingSelector.Body, innerKeyAccessLambda.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.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.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); }
public static (Expression Source, ParameterExpression Parameter) AddNavigationJoin( Expression sourceExpression, ParameterExpression parameterExpression, SourceMapping sourceMapping, NavigationTreeNode navigationTree, NavigationExpansionExpressionState state, List <INavigation> navigationPath, bool include) { var joinNeeded = include ? navigationTree.IncludeState == NavigationState.Pending : navigationTree.ExpansionState == NavigationState.Pending; if (joinNeeded) { var navigation = navigationTree.Navigation; var sourceType = sourceExpression.Type.GetSequenceType(); var navigationTargetEntityType = navigation.GetTargetType(); var entityQueryable = NullAsyncQueryProvider.Instance.CreateEntityQueryableExpression(navigationTargetEntityType.ClrType); var resultType = typeof(TransparentIdentifier <,>).MakeGenericType(sourceType, navigationTargetEntityType.ClrType); var outerParameter = Expression.Parameter(sourceType, parameterExpression.Name); var outerKeySelectorParameter = outerParameter; var transparentIdentifierAccessorExpression = outerParameter.BuildPropertyAccess(navigationTree.Parent.ToMapping); var outerKeySelectorBody = CreateKeyAccessExpression( transparentIdentifierAccessorExpression, navigation.IsDependentToPrincipal() ? navigation.ForeignKey.Properties : navigation.ForeignKey.PrincipalKey.Properties, addNullCheck: navigationTree.Parent != null && navigationTree.Parent.Optional); var innerKeySelectorParameterType = navigationTargetEntityType.ClrType; var innerKeySelectorParameter = Expression.Parameter( innerKeySelectorParameterType, parameterExpression.Name + "." + navigationTree.Navigation.Name); var innerKeySelectorBody = CreateKeyAccessExpression( innerKeySelectorParameter, navigation.IsDependentToPrincipal() ? navigation.ForeignKey.PrincipalKey.Properties : navigation.ForeignKey.Properties); if (outerKeySelectorBody.Type.IsNullableType() && !innerKeySelectorBody.Type.IsNullableType()) { innerKeySelectorBody = Expression.Convert(innerKeySelectorBody, outerKeySelectorBody.Type); } else if (innerKeySelectorBody.Type.IsNullableType() && !outerKeySelectorBody.Type.IsNullableType()) { outerKeySelectorBody = Expression.Convert(outerKeySelectorBody, innerKeySelectorBody.Type); } var outerKeySelector = Expression.Lambda( outerKeySelectorBody, outerKeySelectorParameter); var innerKeySelector = Expression.Lambda( innerKeySelectorBody, innerKeySelectorParameter); if (!sourceExpression.Type.IsQueryableType()) { var asQueryableMethodInfo = LinqMethodHelpers.AsQueryable.MakeGenericMethod(sourceType); sourceExpression = Expression.Call(asQueryableMethodInfo, sourceExpression); } var joinMethodInfo = navigationTree.Optional ? _leftJoinMethodInfo.MakeGenericMethod( sourceType, navigationTargetEntityType.ClrType, outerKeySelector.Body.Type, resultType) : LinqMethodHelpers.QueryableJoinMethodInfo.MakeGenericMethod( sourceType, navigationTargetEntityType.ClrType, outerKeySelector.Body.Type, resultType); var resultSelectorOuterParameterName = outerParameter.Name; var resultSelectorOuterParameter = Expression.Parameter(sourceType, resultSelectorOuterParameterName); var resultSelectorInnerParameterName = innerKeySelectorParameter.Name; var resultSelectorInnerParameter = Expression.Parameter(navigationTargetEntityType.ClrType, resultSelectorInnerParameterName); var transparentIdentifierCtorInfo = resultType.GetTypeInfo().GetConstructors().Single(); var transparentIdentifierOuterMemberInfo = resultType.GetTypeInfo().GetDeclaredField("Outer"); var transparentIdentifierInnerMemberInfo = resultType.GetTypeInfo().GetDeclaredField("Inner"); var resultSelector = Expression.Lambda( Expression.New( transparentIdentifierCtorInfo, new[] { resultSelectorOuterParameter, resultSelectorInnerParameter }, new[] { transparentIdentifierOuterMemberInfo, transparentIdentifierInnerMemberInfo }), resultSelectorOuterParameter, resultSelectorInnerParameter); var joinMethodCall = Expression.Call( joinMethodInfo, sourceExpression, entityQueryable, outerKeySelector, innerKeySelector, resultSelector); sourceExpression = joinMethodCall; var transparentIdentifierParameterName = resultSelectorInnerParameterName; var transparentIdentifierParameter = Expression.Parameter(resultSelector.ReturnType, transparentIdentifierParameterName); parameterExpression = transparentIdentifierParameter; // remap navigation 'To' paths -> for this navigation prepend "Inner", for every other (already expanded) navigation prepend "Outer" navigationTree.ToMapping.Insert(0, nameof(TransparentIdentifier <object, object> .Inner)); foreach (var mapping in state.SourceMappings) { var nodes = include ? mapping.NavigationTree.Flatten().Where(n => (n.IncludeState == NavigationState.Complete || n.ExpansionState == NavigationState.Complete || n.Navigation.ForeignKey.IsOwnership) && n != navigationTree) : mapping.NavigationTree.Flatten().Where(n => (n.ExpansionState == NavigationState.Complete || n.Navigation.ForeignKey.IsOwnership) && n != navigationTree); foreach (var navigationTreeNode in nodes) { navigationTreeNode.ToMapping.Insert(0, nameof(TransparentIdentifier <object, object> .Outer)); } } foreach (var customRootMapping in state.CustomRootMappings) { customRootMapping.Insert(0, nameof(TransparentIdentifier <object, object> .Outer)); } if (include) { navigationTree.IncludeState = NavigationState.Complete; } else { navigationTree.ExpansionState = NavigationState.Complete; } navigationPath.Add(navigation); } else { navigationPath.Add(navigationTree.Navigation); } var result = (Source : sourceExpression, Parameter : parameterExpression); foreach (var child in navigationTree.Children.Where(n => !n.IsCollection)) { result = AddNavigationJoin( result.Source, result.Parameter, sourceMapping, child, state, navigationPath.ToList(), include); } return(result); }
public virtual NavigationExpansionExpression Update(Expression operand, NavigationExpansionExpressionState state) => operand != Operand || state != State ? new NavigationExpansionExpression(operand, state, Type) : this;