public static NavigationExpansionExpression CreateNavigationExpansionRoot( Expression operand, IEntityType entityType, INavigation materializeCollectionNavigation) { var sourceMapping = new SourceMapping { RootEntityType = entityType, }; var navigationTreeRoot = NavigationTreeNode.CreateRoot(sourceMapping, fromMapping: new List <string>(), optional: false); sourceMapping.NavigationTree = navigationTreeRoot; var pendingSelectorParameter = Expression.Parameter(entityType.ClrType); var pendingSelector = Expression.Lambda( new NavigationBindingExpression( pendingSelectorParameter, navigationTreeRoot, entityType, sourceMapping, pendingSelectorParameter.Type), pendingSelectorParameter); return(new NavigationExpansionExpression( operand, new NavigationExpansionExpressionState( pendingSelectorParameter, new List <SourceMapping> { sourceMapping }, pendingSelector, applyPendingSelector: false, new List <(MethodInfo method, LambdaExpression keySelector)>(), pendingIncludeChain: null, pendingCardinalityReducingOperator: null, customRootMappings: new List <List <string> >(), materializeCollectionNavigation), materializeCollectionNavigation?.ClrType ?? operand.Type)); }
public static void CopyIncludeInformation(NavigationTreeNode originalNavigationTree, NavigationTreeNode newNavigationTree, SourceMapping newSourceMapping) { foreach (var child in originalNavigationTree.Children.Where(n => n.IncludeState == NavigationState.Pending)) { var copy = NavigationTreeNode.Create(newSourceMapping, child.Navigation, newNavigationTree, true); CopyIncludeInformation(child, copy, newSourceMapping); } }
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 static Expression CreateCollectionNavigationExpression( NavigationTreeNode navigationTreeNode, ParameterExpression rootParameter, SourceMapping sourceMapping) { var collectionEntityType = navigationTreeNode.Navigation.ForeignKey.DeclaringEntityType; Expression operand; if (navigationTreeNode.IncludeState == NavigationState.Pending || navigationTreeNode.ExpansionState == NavigationState.Pending) { var entityQueryable = (Expression)NullAsyncQueryProvider.Instance.CreateEntityQueryableExpression(collectionEntityType.ClrType); var outerBinding = new NavigationBindingExpression( rootParameter, navigationTreeNode.Parent, navigationTreeNode.Navigation.DeclaringEntityType, sourceMapping, navigationTreeNode.Navigation.DeclaringEntityType.ClrType); var outerKeyAccess = NavigationExpansionHelpers.CreateKeyAccessExpression( outerBinding, navigationTreeNode.Navigation.ForeignKey.PrincipalKey.Properties, addNullCheck: outerBinding.NavigationTreeNode.Optional); var collectionCurrentParameter = Expression.Parameter( collectionEntityType.ClrType, collectionEntityType.ClrType.GenerateParameterName()); var innerKeyAccess = NavigationExpansionHelpers.CreateKeyAccessExpression( collectionCurrentParameter, navigationTreeNode.Navigation.ForeignKey.Properties); var predicate = Expression.Lambda( CreateKeyComparisonExpressionForCollectionNavigationSubquery( outerKeyAccess, innerKeyAccess, outerBinding), collectionCurrentParameter); operand = Expression.Call( LinqMethodHelpers.QueryableWhereMethodInfo.MakeGenericMethod(collectionEntityType.ClrType), entityQueryable, predicate); } else { operand = new NavigationBindingExpression( rootParameter, navigationTreeNode, collectionEntityType, sourceMapping, collectionEntityType.ClrType); } var result = NavigationExpansionHelpers.CreateNavigationExpansionRoot( operand, collectionEntityType, navigationTreeNode.Navigation); // this is needed for cases like: root.Include(r => r.Collection).ThenInclude(c => c.Reference).Select(r => r.Collection) // result should be elements of the collection navigation with their 'Reference' included var newSourceMapping = result.State.SourceMappings.Single(); IncludeHelpers.CopyIncludeInformation(navigationTreeNode, newSourceMapping.NavigationTree, newSourceMapping); return(result); }