public static NavigationTreeNode Create( [NotNull] SourceMapping sourceMapping, [NotNull] INavigation navigation, [NotNull] NavigationTreeNode parent, bool include) { Check.NotNull(sourceMapping, nameof(sourceMapping)); Check.NotNull(navigation, nameof(navigation)); Check.NotNull(parent, nameof(parent)); var existingChild = parent.Children.Where(c => c.Navigation == navigation).SingleOrDefault(); if (existingChild != null) { if (!navigation.ForeignKey.IsOwnership) { if (include && existingChild.IncludeState == NavigationState.NotNeeded) { existingChild.IncludeState = navigation.IsCollection() ? NavigationState.CollectionPending : NavigationState.ReferencePending; } else if (!include && existingChild.ExpansionState == NavigationState.NotNeeded) { existingChild.ExpansionState = navigation.IsCollection() ? NavigationState.CollectionPending : NavigationState.ReferencePending; } } return(existingChild); } // if (any) parent is optional, all children must be optional also // TODO: what about query filters? var optional = parent.Optional || !navigation.ForeignKey.IsRequired || !navigation.IsDependentToPrincipal(); var result = new NavigationTreeNode(navigation, parent, optional, include); parent.Children.Add(result); return(result); }
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, pendingTags: new List <string>(), customRootMappings: new List <List <string> >(), materializeCollectionNavigation), materializeCollectionNavigation?.ClrType ?? operand.Type)); }
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.Included == NavigationTreeNodeIncludeMode.ReferencePending : navigationTree.ExpansionMode == NavigationTreeNodeExpansionMode.ReferencePending; 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.Included == NavigationTreeNodeIncludeMode.ReferenceComplete || n.ExpansionMode == NavigationTreeNodeExpansionMode.ReferenceComplete || n.Navigation.ForeignKey.IsOwnership) && n != navigationTree) : mapping.NavigationTree.Flatten().Where(n => (n.ExpansionMode == NavigationTreeNodeExpansionMode.ReferenceComplete || 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.Included = NavigationTreeNodeIncludeMode.ReferenceComplete; } else { navigationTree.ExpansionMode = NavigationTreeNodeExpansionMode.ReferenceComplete; } navigationPath.Add(navigation); } else { navigationPath.Add(navigationTree.Navigation); } var result = (source : sourceExpression, parameter : parameterExpression); foreach (var child in navigationTree.Children.Where(n => !n.Navigation.IsCollection())) { result = AddNavigationJoin( result.source, result.parameter, sourceMapping, child, state, navigationPath.ToList(), include); } return(result); }
public static void CopyIncludeInformation(NavigationTreeNode originalNavigationTree, NavigationTreeNode newNavigationTree, SourceMapping newSourceMapping) { foreach (var child in originalNavigationTree.Children.Where(n => n.IncludeState == NavigationState.ReferencePending || n.IncludeState == NavigationState.CollectionPending)) { var copy = NavigationTreeNode.Create(newSourceMapping, child.Navigation, newNavigationTree, true); CopyIncludeInformation(child, copy, newSourceMapping); } }