Esempio n. 1
0
 public NavigationExpansionExpression(
     Expression operand,
     NavigationExpansionExpressionState state,
     Type returnType)
 {
     Operand     = operand;
     State       = state;
     _returnType = returnType;
 }
        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);
        }
Esempio n. 3
0
 public virtual NavigationExpansionExpression Update(Expression operand, NavigationExpansionExpressionState state)
 => operand != Operand || state != State
     ? new NavigationExpansionExpression(operand, state, Type)
     : this;