Exemple #1
0
        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);
        }
Exemple #2
0
        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);
        }
Exemple #4
0
 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);
     }
 }