private MethodCallExpression CreateSelectExpression(Expression source)
        {
            if (_rootNavigationItem.HasNavigationItems())
            {
                return((MethodCallExpression)source);
            }

            if (_rootNavigationItem.AllSelected)
            {
                return((MethodCallExpression)source);
            }

            ParameterExpression parameter = _joinBuilder.Visitor.Parameter;
            IReadOnlyList <OeStructuralSelectItem> structuralItems = _rootNavigationItem.GetStructuralItemsWithNotSelected();
            var expressions = new Expression[structuralItems.Count];

            for (int i = 0; i < expressions.Length; i++)
            {
                if (structuralItems[i].EdmProperty is ComputeProperty computeProperty)
                {
                    expressions[i] = computeProperty.Expression;
                }
                else
                {
                    PropertyInfo clrProperty = parameter.Type.GetPropertyIgnoreCase(structuralItems[i].EdmProperty);
                    expressions[i] = Expression.MakeMemberAccess(parameter, clrProperty);
                }
            }
            NewExpression newTupleExpression = OeExpressionHelper.CreateTupleExpression(expressions);

            LambdaExpression lambda           = Expression.Lambda(newTupleExpression, parameter);
            MethodInfo       selectMethodInfo = OeMethodInfoHelper.GetSelectMethodInfo(parameter.Type, newTupleExpression.Type);

            return(Expression.Call(selectMethodInfo, source, lambda));
        }
Exemple #2
0
        public override OeEntryFactory CreateEntryFactory(IEdmEntitySet entitySet, Type clrType, OePropertyAccessor[]?skipTokenAccessors)
        {
            ParameterExpression parameter      = Expression.Parameter(typeof(Object));
            UnaryExpression     typedParameter = Expression.Convert(parameter, clrType);

            if (_rootNavigationItem.HasNavigationItems())
            {
                List <OeNavigationSelectItem>    navigationItems      = OeSelectTranslator.FlattenNavigationItems(_rootNavigationItem, true);
                IReadOnlyList <MemberExpression> navigationProperties = OeExpressionHelper.GetPropertyExpressions(typedParameter);
                int propertyIndex = navigationProperties.Count - 1;

                for (int i = navigationItems.Count - 1; i >= 0; i--)
                {
                    OeNavigationSelectItem navigationItem = navigationItems[i];
                    if (navigationItem.Kind == OeNavigationSelectItemKind.NotSelected)
                    {
                        propertyIndex--;
                        continue;
                    }

                    OePropertyAccessor[]       accessors             = Array.Empty <OePropertyAccessor>();
                    LambdaExpression?          linkAccessor          = null;
                    OeNavigationEntryFactory[] nestedNavigationLinks = Array.Empty <OeNavigationEntryFactory>();
                    if (navigationItem.Kind != OeNavigationSelectItemKind.NextLink)
                    {
                        accessors             = GetAccessors(navigationProperties[propertyIndex].Type, navigationItem);
                        linkAccessor          = Expression.Lambda(navigationProperties[propertyIndex], parameter);
                        nestedNavigationLinks = GetNestedNavigationLinks(navigationItem);
                        propertyIndex--;
                    }

                    if (i == 0)
                    {
                        navigationItem.EntryFactory = new OeEntryFactory(navigationItem.EntitySet, accessors, skipTokenAccessors, nestedNavigationLinks, linkAccessor);
                    }
                    else
                    {
                        navigationItem.EntryFactory = new OeNavigationEntryFactory(
                            navigationItem.EntitySet,
                            accessors,
                            skipTokenAccessors,
                            nestedNavigationLinks,
                            linkAccessor,
                            navigationItem.EdmProperty,
                            navigationItem.NavigationSelectItem,
                            navigationItem.Kind == OeNavigationSelectItemKind.NextLink);
                    }
                }
            }
            else
            {
                var navigationLinks = new OeNavigationEntryFactory[_rootNavigationItem.NavigationItems.Count];
                for (int i = 0; i < _rootNavigationItem.NavigationItems.Count; i++)
                {
                    OeNavigationSelectItem navigationItem = _rootNavigationItem.NavigationItems[i];
                    navigationLinks[i] = new OeNavigationEntryFactory(
                        navigationItem.EntitySet,
                        Array.Empty <OePropertyAccessor>(),
                        null,
                        Array.Empty <OeNavigationEntryFactory>(),
                        null,
                        navigationItem.EdmProperty,
                        navigationItem.NavigationSelectItem,
                        navigationItem.Kind == OeNavigationSelectItemKind.NextLink);
                }

                OePropertyAccessor[] accessors = GetAccessors(clrType, _rootNavigationItem);
                _rootNavigationItem.EntryFactory = new OeEntryFactory(_rootNavigationItem.EntitySet, accessors, skipTokenAccessors, navigationLinks);
            }

            return(_rootNavigationItem.EntryFactory);
        }
        private static Expression SelectStructuralProperties(Expression source, OeNavigationSelectItem root)
        {
            if (!root.HasNavigationItems())
            {
                return(source);
            }

            ParameterExpression parameter          = Expression.Parameter(OeExpressionHelper.GetCollectionItemType(source.Type));
            IReadOnlyList <MemberExpression> joins = OeExpressionHelper.GetPropertyExpressions(parameter);
            var newJoins = new Expression[joins.Count];

            List <OeNavigationSelectItem> navigationItems = FlattenNavigationItems(root, false);
            bool isNavigationNullable = false;

            for (int i = 0; i < navigationItems.Count; i++)
            {
                newJoins[i]           = joins[i];
                isNavigationNullable |= i > 0 && navigationItems[i].EdmProperty.Type.IsNullable;
                if (!navigationItems[i].AllSelected)
                {
                    IReadOnlyList <OeStructuralSelectItem> structuralItems = navigationItems[i].GetStructuralItemsWithNotSelected();
                    if (structuralItems.Count > 0)
                    {
                        var properties = new Expression[structuralItems.Count];
                        for (int j = 0; j < structuralItems.Count; j++)
                        {
                            if (structuralItems[j].EdmProperty is ComputeProperty computeProperty)
                            {
                                properties[j] = new ReplaceParameterVisitor(joins[i]).Visit(computeProperty.Expression);
                            }
                            else
                            {
                                PropertyInfo property = joins[i].Type.GetPropertyIgnoreCase(structuralItems[j].EdmProperty);
                                properties[j] = Expression.Property(joins[i], property);
                            }
                        }
                        Expression newTupleExpression = OeExpressionHelper.CreateTupleExpression(properties);

                        if (isNavigationNullable)
                        {
                            UnaryExpression nullConstant = Expression.Convert(OeConstantToVariableVisitor.NullConstantExpression, newTupleExpression.Type);
                            newTupleExpression = Expression.Condition(Expression.Equal(joins[i], OeConstantToVariableVisitor.NullConstantExpression), nullConstant, newTupleExpression);
                        }
                        newJoins[i] = newTupleExpression;
                    }
                }
            }

            NewExpression    newSelectorBody  = OeExpressionHelper.CreateTupleExpression(newJoins);
            MethodInfo       selectMethodInfo = OeMethodInfoHelper.GetSelectMethodInfo(parameter.Type, newSelectorBody.Type);
            LambdaExpression newSelector      = Expression.Lambda(newSelectorBody, parameter);

            //Quirk EF Core 2.1.1 bug Take/Skip must be last in expression tree
            var skipTakeExpressions = new List <MethodCallExpression>();

            while (source is MethodCallExpression callExpression && (callExpression.Method.Name == nameof(Enumerable.Skip) || callExpression.Method.Name == nameof(Enumerable.Take)))
            {
                skipTakeExpressions.Add(callExpression);
                source = callExpression.Arguments[0];
            }

            source = Expression.Call(selectMethodInfo, source, newSelector);

            for (int i = skipTakeExpressions.Count - 1; i >= 0; i--)
            {
                MethodInfo skipTakeMethodInfo = skipTakeExpressions[i].Method.GetGenericMethodDefinition().MakeGenericMethod(newSelector.ReturnType);
                source = Expression.Call(skipTakeMethodInfo, source, skipTakeExpressions[i].Arguments[1]);
            }

            return(source);
        }
        private static OeEntryFactory CreateEntryFactory(OeNavigationSelectItem root, Type clrType, OePropertyAccessor[] skipTokenAccessors)
        {
            ParameterExpression parameter      = Expression.Parameter(typeof(Object));
            UnaryExpression     typedParameter = Expression.Convert(parameter, clrType);

            if (!root.HasNavigationItems())
            {
                var navigationLinks = new OeEntryFactory[root.NavigationItems.Count];
                for (int i = 0; i < root.NavigationItems.Count; i++)
                {
                    OeNavigationSelectItem navigationItem = root.NavigationItems[i];
                    var nextLinkOptions = new OeEntryFactoryOptions()
                    {
                        Accessors             = Array.Empty <OePropertyAccessor>(),
                        EdmNavigationProperty = navigationItem.EdmProperty,
                        EntitySet             = navigationItem.EntitySet,
                        NavigationSelectItem  = navigationItem.NavigationSelectItem,
                        NextLink = navigationItem.Kind == OeNavigationSelectItemKind.NextLink
                    };
                    navigationLinks[i] = new OeEntryFactory(ref nextLinkOptions);
                }

                var options = new OeEntryFactoryOptions()
                {
                    Accessors          = GetAccessors(clrType, root),
                    EntitySet          = root.EntitySet,
                    NavigationLinks    = navigationLinks,
                    SkipTokenAccessors = skipTokenAccessors
                };
                root.EntryFactory = new OeEntryFactory(ref options);
            }
            else
            {
                List <OeNavigationSelectItem>    navigationItems      = FlattenNavigationItems(root, true);
                IReadOnlyList <MemberExpression> navigationProperties = OeExpressionHelper.GetPropertyExpressions(typedParameter);
                int propertyIndex = navigationProperties.Count - 1;

                for (int i = navigationItems.Count - 1; i >= 0; i--)
                {
                    OeNavigationSelectItem navigationItem = navigationItems[i];
                    if (navigationItem.Kind == OeNavigationSelectItemKind.NotSelected)
                    {
                        propertyIndex--;
                        continue;
                    }

                    OePropertyAccessor[] accessors             = Array.Empty <OePropertyAccessor>();
                    LambdaExpression?    linkAccessor          = null;
                    OeEntryFactory[]     nestedNavigationLinks = Array.Empty <OeEntryFactory>();
                    if (navigationItem.Kind != OeNavigationSelectItemKind.NextLink)
                    {
                        accessors             = GetAccessors(navigationProperties[propertyIndex].Type, navigationItem);
                        linkAccessor          = Expression.Lambda(navigationProperties[propertyIndex], parameter);
                        nestedNavigationLinks = GetNestedNavigationLinks(navigationItem);
                        propertyIndex--;
                    }

                    var options = new OeEntryFactoryOptions()
                    {
                        Accessors             = accessors,
                        EdmNavigationProperty = navigationItem.Parent == null ? null : navigationItem.EdmProperty,
                        EntitySet             = navigationItem.EntitySet,
                        LinkAccessor          = linkAccessor,
                        NavigationLinks       = nestedNavigationLinks,
                        NavigationSelectItem  = navigationItem.Parent == null ? null : navigationItem.NavigationSelectItem,
                        NextLink           = navigationItem.Kind == OeNavigationSelectItemKind.NextLink,
                        SkipTokenAccessors = skipTokenAccessors
                    };
                    navigationItem.EntryFactory = new OeEntryFactory(ref options);
                }
            }

            return(root.EntryFactory);
        }