コード例 #1
0
        private static OrderByBinderResult BindOrderBy <T>(string orderBy, IEdmModel model, ODataQuerySettings querySettings = null, IAssemblyResolver assembliesResolver = null)
        {
            Type          elementType   = typeof(T);
            OrderByClause orderByClause = CreateOrderByNode(orderBy, model, elementType);

            Assert.NotNull(orderByClause);

            querySettings = querySettings ?? _defaultSettings;
            QueryBinderContext context = new QueryBinderContext(model, querySettings, elementType)
            {
                AssembliesResolver = assembliesResolver,
            };

            IOrderByBinder orderByBinder = new OrderByBinder();

            return(orderByBinder.BindOrderBy(orderByClause, context));
        }
コード例 #2
0
        private static Expression BindFilter <T>(string filter, IEdmModel model, ODataQuerySettings querySettings = null, IAssemblyResolver assembliesResolver = null)
        {
            Type         elementType   = typeof(T);
            FilterClause orderByClause = CreateFilterClause(filter, model, elementType);

            Assert.NotNull(orderByClause);

            querySettings = querySettings ?? _defaultSettings;
            QueryBinderContext context = new QueryBinderContext(model, querySettings, elementType)
            {
                AssembliesResolver = assembliesResolver,
            };

            IFilterBinder filterBinder = new FilterBinder();

            return(filterBinder.BindFilter(orderByClause, context));
        }
コード例 #3
0
        public Expression BindSearch(SearchClause searchClause, QueryBinderContext context)
        {
            SearchTermNode node = searchClause.Expression as SearchTermNode;

            if (node != null)
            {
                // Lambda expression methods:
                if (_categories.Contains(node.Text))
                {
                    // Be noted: ToLowerInvariant is not enabled/supported in EF Core.
                    // Switch to use "ToLower()" should work in EF Core.
                    Expression <Func <Product, bool> > exp = p => p.Category.ToString().ToLowerInvariant() == node.Text.ToLowerInvariant();
                    return(exp);
                }
                else if (_colors.Contains(node.Text))
                {
                    Expression <Func <Product, bool> > exp = p => p.Color.ToString().ToLowerInvariant() == node.Text.ToLowerInvariant();
                    return(exp);
                }
                else if (context.ElementClrType == typeof(Sale))
                {
                    try
                    {
                        int year = int.Parse(node.Text);
                        Expression <Func <Sale, bool> > exp = s => s.SaleDate.Year == year;
                        return(exp);
                    }
                    catch
                    {
                        return(Expression.Constant(false, typeof(bool)));
                    }
                }

                throw new InvalidOperationException("Unknown search on product or sale!");
            }
            else
            {
                // Linq expression tree methods:
                Expression exp = BindSingleValueNode(searchClause.Expression, context);

                LambdaExpression lambdaExp = Expression.Lambda(exp, context.CurrentParameter);

                return(lambdaExp);
            }
        }
コード例 #4
0
        public void OrderByBinder_BindsComputedPropertyInDollarOrderBy_FromDollarCompute()
        {
            // Arrange
            QueryClause clause = CreateQueryClause("$orderby=Total&$compute=Price mul Qty as Total", _model, typeof(ComputeCustomer));

            ODataQuerySettings querySettings = new ODataQuerySettings();
            IOrderByBinder     binder        = new OrderByBinder();
            QueryBinderContext context       = new QueryBinderContext(_model, querySettings, typeof(ComputeCustomer));

            if (clause.Compute != null)
            {
                context.AddComputedProperties(clause.Compute.ComputedItems);
            }

            // Act
            OrderByBinderResult orderByResult = binder.BindOrderBy(clause.OrderBy, context);

            // Assert
            Assert.Null(orderByResult.ThenBy);
            Assert.Equal(OrderByDirection.Ascending, orderByResult.Direction);
            string resultExpression = ExpressionStringBuilder.ToString(orderByResult.OrderByExpression);

            Assert.Equal("$it => ($it.Price * Convert($it.Qty))", resultExpression);

            IEnumerable <ComputeCustomer> customers = new[]
            {
                new ComputeCustomer {
                    Id = 1, Qty = 3, Price = 5.99
                },
                new ComputeCustomer {
                    Id = 2, Qty = 5, Price = 2.99
                },
                new ComputeCustomer {
                    Id = 3, Qty = 2, Price = 18.01
                },
                new ComputeCustomer {
                    Id = 4, Qty = 4, Price = 9.99
                },
            };

            var orderedResult = OrderByBinderTests.InvokeOrderBy(customers, orderByResult.OrderByExpression, orderByResult.Direction, false);

            Assert.True(new [] { 2, 1, 3, 4 }.SequenceEqual(orderedResult.Select(a => a.Id))); // ordered id
        }
コード例 #5
0
        public Expression BindSearchTerm(SearchTermNode node, QueryBinderContext context)
        {
            // Source is from context;
            Expression source = context.CurrentParameter;

            string text = node.Text.ToLowerInvariant();

            if (_categories.Contains(text))
            {
                // $it.Category
                Expression categoryProperty = Expression.Property(source, "Category");

                // $it.Category.Name
                Expression categoryName = Expression.Property(categoryProperty, "Name");

                // string.Equals($it.Category.Name, text, StringComparison.OrdinalIgnoreCase);
                return(Expression.Call(null, StringEqualsMethodInfo,
                                       categoryName, Expression.Constant(text, typeof(string)), Expression.Constant(StringComparison.OrdinalIgnoreCase, typeof(StringComparison))));
            }
            else if (_colors.Contains(text))
            {
                // $it.Color
                Expression colorProperty = Expression.Property(source, "Color");

                // $it.Color.ToString()
                Expression colorPropertyString = Expression.Call(colorProperty, "ToString", typeArguments: null, arguments: null);

                // string.Equals($it.Color.ToString(), text, StringComparison.OrdinalIgnoreCase);
                return(Expression.Call(null, StringEqualsMethodInfo,
                                       colorPropertyString, Expression.Constant(text, typeof(string)), Expression.Constant(StringComparison.OrdinalIgnoreCase, typeof(StringComparison))));
            }
            else
            {
                return(Expression.Constant(false, typeof(bool)));
            }
        }
コード例 #6
0
        public override Expression BindBinaryOperatorNode(BinaryOperatorNode binaryOperatorNode, QueryBinderContext context)
        {
            Expression left = Bind(binaryOperatorNode.Left, context);

            Expression right = Bind(binaryOperatorNode.Right, context);

            if (BinaryOperatorMapping.TryGetValue(binaryOperatorNode.OperatorKind, out ExpressionType binaryExpressionType))
            {
                return(Expression.MakeBinary(binaryExpressionType, left, right));
            }

            throw new NotImplementedException($"Binary operator '{binaryOperatorNode.OperatorKind}' is not supported!");
        }
コード例 #7
0
        public void SelectExpandBinder_BindsComputedPropertyInExpand_FromDollarCompute()
        {
            // Arrange
            QueryClause clause = CreateQueryClause("$expand=Orders($select=Title,Tax;$compute=Amount mul TaxRate as Tax)", _model, typeof(ComputeCustomer));

            ODataQuerySettings querySettings = new ODataQuerySettings();
            SelectExpandBinder binder        = new SelectExpandBinder();
            QueryBinderContext context       = new QueryBinderContext(_model, querySettings, typeof(ComputeCustomer));

            if (clause.Compute != null)
            {
                context.AddComputedProperties(clause.Compute.ComputedItems);
            }

            // Act
            Expression selectExp = binder.BindSelectExpand(clause.SelectExpand, context);

            // Assert
            Assert.NotNull(selectExp);
            string resultExpression = ExpressionStringBuilder.ToString(selectExp);

            Assert.Equal("$it => new SelectAllAndExpand`1() {Model = Microsoft.OData.Edm.EdmModel, " +
                         "Instance = $it, UseInstanceForProperties = True, " +
                         "Container = new NamedPropertyWithNext0`1() " +
                         "{" +
                         "Name = Orders, " +
                         "Value = $it.Orders.Select($it => new SelectSome`1() " +
                         "{" +
                         "Model = Microsoft.OData.Edm.EdmModel, " +
                         "Container = new NamedPropertyWithNext1`1() " +
                         "{" +
                         "Name = Title, " +
                         "Value = $it.Title, " +
                         "Next0 = new NamedProperty`1() {Name = Tax, Value = (Convert($it.Amount) * $it.TaxRate), }, " +
                         "Next1 = new AutoSelectedNamedProperty`1() " +
                         "{" +
                         "Name = Id, Value = Convert($it.Id), " +
                         "}, " +
                         "}, " +
                         "}), " +
                         "Next0 = new NamedProperty`1() {Name = Dynamics, Value = $it.Dynamics, }, }, }", resultExpression);

            ComputeCustomer customer = new ComputeCustomer
            {
                Orders = new List <ComputeOrder>
                {
                    new ComputeOrder {
                        Title = "Kerry", Amount = 4, TaxRate = 0.35
                    },
                    new ComputeOrder {
                        Title = "WU", Amount = 6, TaxRate = 0.5
                    },
                    new ComputeOrder {
                        Title = "XU", Amount = 5, TaxRate = 0.12
                    },
                }
            };
            IDictionary <string, object> result = SelectExpandBinderTest.InvokeSelectExpand(customer, selectExp);

            Assert.Equal(8, result.Count); // Because it's select-all

            int idx         = 0;
            var ordersValue = result["Orders"] as IEnumerable;

            foreach (var order in ordersValue)
            {
                SelectExpandWrapper itemWrapper = order as SelectExpandWrapper;

                var orderDic = itemWrapper.ToDictionary();
                Assert.Equal(customer.Orders[idx].Title, orderDic["Title"]);
                Assert.Equal(customer.Orders[idx].Amount * customer.Orders[idx].TaxRate, orderDic["Tax"]);
                idx++;
            }
        }
コード例 #8
0
        private static IQueryable AddOrderByQueryForProperty(IOrderByBinder orderByBinder,
                                                             OrderByClause orderbyClause, IQueryable querySoFar, QueryBinderContext binderContext, bool alreadyOrdered)
        {
            // Remove Thenby (make Thenby == null) to make sure we only apply the top orderby
            // TODO: need to refactor it later.
            orderbyClause = new OrderByClause(null, orderbyClause.Expression, orderbyClause.Direction, orderbyClause.RangeVariable);

            querySoFar = orderByBinder.ApplyBind(querySoFar, orderbyClause, binderContext, alreadyOrdered);

            return(querySoFar);
        }
コード例 #9
0
        private IOrderedQueryable ApplyToCore(IQueryable query, ODataQuerySettings querySettings)
        {
            if (Context.ElementClrType == null)
            {
                throw Error.NotSupported(SRResources.ApplyToOnUntypedQueryOption, "ApplyTo");
            }

            ICollection <OrderByNode> nodes = OrderByNodes;

            bool       alreadyOrdered = false;
            IQueryable querySoFar     = query;

            HashSet <object> propertiesSoFar     = new HashSet <object>();
            HashSet <string> openPropertiesSoFar = new HashSet <string>();
            bool             orderByItSeen       = false;

            IOrderByBinder     binder        = Context.GetOrderByBinder();
            QueryBinderContext binderContext = new QueryBinderContext(Context.Model, querySettings, Context.ElementClrType);

            if (Compute != null)
            {
                binderContext.AddComputedProperties(Compute.ComputeClause.ComputedItems);
            }

            foreach (OrderByNode node in nodes)
            {
                OrderByPropertyNode     propertyNode     = node as OrderByPropertyNode;
                OrderByOpenPropertyNode openPropertyNode = node as OrderByOpenPropertyNode;
                OrderByCountNode        countNode        = node as OrderByCountNode;

                if (propertyNode != null)
                {
                    // Use autonomy class to achieve value equality for HasSet.
                    var edmPropertyWithPath    = new { propertyNode.Property, propertyNode.PropertyPath };
                    OrderByDirection direction = propertyNode.Direction;

                    // This check prevents queries with duplicate properties (e.g. $orderby=Id,Id,Id,Id...) from causing stack overflows
                    if (propertiesSoFar.Contains(edmPropertyWithPath))
                    {
                        throw new ODataException(Error.Format(SRResources.OrderByDuplicateProperty, edmPropertyWithPath.PropertyPath));
                    }

                    propertiesSoFar.Add(edmPropertyWithPath);

                    if (propertyNode.OrderByClause != null)
                    {
                        querySoFar = AddOrderByQueryForProperty(binder, propertyNode.OrderByClause, querySoFar, binderContext, alreadyOrdered);
                    }
                    else
                    {
                        // could have ensure stable orderby property added
                        querySoFar = ExpressionHelpers.OrderByProperty(querySoFar, Context.Model, edmPropertyWithPath.Property, direction, Context.ElementClrType, alreadyOrdered);
                    }

                    alreadyOrdered = true;
                }
                else if (openPropertyNode != null)
                {
                    // This check prevents queries with duplicate properties (e.g. $orderby=Id,Id,Id,Id...) from causing stack overflows
                    if (openPropertiesSoFar.Contains(openPropertyNode.PropertyName))
                    {
                        throw new ODataException(Error.Format(SRResources.OrderByDuplicateProperty, openPropertyNode.PropertyPath));
                    }

                    openPropertiesSoFar.Add(openPropertyNode.PropertyName);
                    Contract.Assert(openPropertyNode.OrderByClause != null);
                    querySoFar     = AddOrderByQueryForProperty(binder, openPropertyNode.OrderByClause, querySoFar, binderContext, alreadyOrdered);
                    alreadyOrdered = true;
                }
                else if (countNode != null)
                {
                    Contract.Assert(countNode.OrderByClause != null);
                    querySoFar     = AddOrderByQueryForProperty(binder, countNode.OrderByClause, querySoFar, binderContext, alreadyOrdered);
                    alreadyOrdered = true;
                }
                else
                {
                    // This check prevents queries with duplicate nodes (e.g. $orderby=$it,$it,$it,$it...) from causing stack overflows
                    if (orderByItSeen)
                    {
                        throw new ODataException(Error.Format(SRResources.OrderByDuplicateIt));
                    }

                    querySoFar     = ExpressionHelpers.OrderByIt(querySoFar, node.Direction, Context.ElementClrType, alreadyOrdered);
                    alreadyOrdered = true;
                    orderByItSeen  = true;
                }
            }

            return(querySoFar as IOrderedQueryable);
        }
コード例 #10
0
        public override Expression BindBinaryOperatorNode(BinaryOperatorNode binaryOperatorNode, QueryBinderContext context)
        {
            Expression left = Bind(binaryOperatorNode.Left, context);

            Expression right = Bind(binaryOperatorNode.Right, context);

            return(ExpressionBinderHelper.CreateBinaryExpression(binaryOperatorNode.OperatorKind, left, right, liftToNull: false, context.QuerySettings));
        }
コード例 #11
0
        /// <summary>
        /// Apply the apply query to the given IQueryable.
        /// </summary>
        /// <remarks>
        /// The <see cref="ODataQuerySettings.HandleNullPropagation"/> property specifies
        /// how this method should handle null propagation.
        /// </remarks>
        /// <param name="query">The original <see cref="IQueryable"/>.</param>
        /// <param name="querySettings">The <see cref="ODataQuerySettings"/> that contains all the query application related settings.</param>
        /// <returns>The new <see cref="IQueryable"/> after the filter query has been applied to.</returns>
        public IQueryable ApplyTo(IQueryable query, ODataQuerySettings querySettings)
        {
            if (query == null)
            {
                throw Error.ArgumentNull(nameof(query));
            }

            if (querySettings == null)
            {
                throw Error.ArgumentNull(nameof(querySettings));
            }

            if (Context.ElementClrType == null)
            {
                throw Error.NotSupported(SRResources.ApplyToOnUntypedQueryOption, "ApplyTo");
            }

            // Linq to SQL not supported for $apply
            if (query.Provider.GetType().Namespace == HandleNullPropagationOptionHelper.Linq2SqlQueryProviderNamespace)
            {
                throw Error.NotSupported(SRResources.ApplyQueryOptionNotSupportedForLinq2SQL);
            }

            ApplyClause applyClause = ApplyClause;

            Contract.Assert(applyClause != null);

            // The IWebApiAssembliesResolver service is internal and can only be injected by WebApi.
            // This code path may be used in cases when the service container is not available
            // and the service container is available but may not contain an instance of IWebApiAssembliesResolver.
            IAssemblyResolver assembliesResolver = AssemblyResolverHelper.Default;

            if (Context.RequestContainer != null)
            {
                IAssemblyResolver injectedResolver = Context.RequestContainer.GetService <IAssemblyResolver>();
                if (injectedResolver != null)
                {
                    assembliesResolver = injectedResolver;
                }
            }

            foreach (var transformation in applyClause.Transformations)
            {
                if (transformation.Kind == TransformationNodeKind.Aggregate || transformation.Kind == TransformationNodeKind.GroupBy)
                {
                    var binder = new AggregationBinder(querySettings, assembliesResolver, ResultClrType, Context.Model, transformation);
                    query = binder.Bind(query);
                    this.ResultClrType = binder.ResultClrType;
                }
                else if (transformation.Kind == TransformationNodeKind.Compute)
                {
                    var binder = new ComputeBinder(querySettings, assembliesResolver, ResultClrType, Context.Model, (ComputeTransformationNode)transformation);
                    query = binder.Bind(query);
                    this.ResultClrType = binder.ResultClrType;
                }
                else if (transformation.Kind == TransformationNodeKind.Filter)
                {
                    var filterTransformation = transformation as FilterTransformationNode;

                    IFilterBinder      binder        = Context.GetFilterBinder();
                    QueryBinderContext binderContext = new QueryBinderContext(Context.Model, querySettings, ResultClrType);

                    query = binder.ApplyBind(query, filterTransformation.FilterClause, binderContext);
                }
            }

            return(query);
        }