示例#1
0
        /// <summary>
        /// Applies the $select and $expand query options to the given entity using the given <see cref="ODataQuerySettings"/>.
        /// </summary>
        /// <param name="entity">The original entity.</param>
        /// <param name="settings">The <see cref="ODataQuerySettings"/> that contains all the query application related settings.</param>
        /// <returns>The new entity after the $select and $expand query has been applied to.</returns>
        public object ApplyTo(object entity, ODataQuerySettings settings)
        {
            if (entity == null)
            {
                throw Error.ArgumentNull(nameof(entity));
            }
            if (settings == null)
            {
                throw Error.ArgumentNull(nameof(settings));
            }
            if (Context.ElementClrType == null)
            {
                throw Error.NotSupported(SRResources.ApplyToOnUntypedQueryOption, "ApplyTo");
            }

            ISelectExpandBinder binder = Context.GetSelectExpandBinder();

            QueryBinderContext binderContext = new QueryBinderContext(Context.Model, settings, Context.ElementClrType)
            {
                NavigationSource = Context.NavigationSource,
            };

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

            return(binder.ApplyBind(entity, _selectExpandClause, binderContext));
        }
示例#2
0
        public void FilterBinder_BindsComputedPropertyInDollarFilter_FromDollarCompute()
        {
            // Arrange
            QueryClause clause = CreateQueryClause("$filter=Total lt 30&$compute=Price mul Qty as Total", _model, typeof(ComputeCustomer));

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

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

            // Act
            Expression filterExp = binder.BindFilter(clause.Filter, context);

            // Assert
            string resultExpression = ExpressionStringBuilder.ToString(filterExp);

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

            Assert.True(FilterBinderTests.InvokeFilter(new ComputeCustomer {
                Price = 10, Qty = 2
            }, filterExp));
            Assert.False(FilterBinderTests.InvokeFilter(new ComputeCustomer {
                Price = 10, Qty = 4
            }, filterExp));
        }
        /// <summary>
        /// Apply the filter 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("query");
            }
            if (querySettings == null)
            {
                throw Error.ArgumentNull("querySettings");
            }

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

            FilterClause filterClause = FilterClause;

            Contract.Assert(filterClause != null);

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

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

            IFilterBinder binder = Context.GetFilterBinder();

            return(binder.ApplyBind(query, filterClause, binderContext));
        }
示例#4
0
        public void SelectExpandBinder_BindsComputedPropertyInNestedSelect_FromDollarCompute()
        {
            // Arrange
            QueryClause clause = CreateQueryClause("$select=Location($select=StateCode;$compute=Zipcode div 1000 as StateCode)", _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 SelectSome`1() {" +
                         "Model = Microsoft.OData.Edm.EdmModel, " +
                         "Container = new NamedPropertyWithNext0`1() {" +
                         "Name = Location, " +
                         "Value = new SelectSome`1() {" +
                         "Model = Microsoft.OData.Edm.EdmModel, " +
                         "Container = new NamedProperty`1() {" +
                         "Name = StateCode, " +
                         "Value = ($it.Location.Zipcode / 1000), " +
                         "}, " +
                         "}, " +
                         "Next0 = new AutoSelectedNamedProperty`1() {Name = Id, Value = Convert($it.Id), }, }, }", resultExpression);

            ComputeCustomer customer = new ComputeCustomer
            {
                Location = new ComputeAddress {
                    Zipcode = 98029
                }
            };
            IDictionary <string, object> result = SelectExpandBinderTest.InvokeSelectExpand(customer, selectExp);

            var location = Assert.Single(result);

            Assert.Equal("Location", location.Key);
            SelectExpandWrapper          itemWrapper    = location.Value as SelectExpandWrapper;
            IDictionary <string, object> locationResult = itemWrapper.ToDictionary();

            var stateCode = Assert.Single(locationResult);

            Assert.Equal("StateCode", stateCode.Key);
            Assert.Equal(98, stateCode.Value);
        }
示例#5
0
        public void SelectExpandBinder_BindsComputedPropertyInSelect_FromDollarCompute()
        {
            // Arrange
            QueryClause clause = CreateQueryClause("$select=Name,Total&$compute=Price mul Qty as Total", _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 SelectSome`1() {" +
                         "Model = Microsoft.OData.Edm.EdmModel, " +
                         "Container = new NamedPropertyWithNext1`1() {" +
                         "Name = Name, " +
                         "Value = $it.Name, " +
                         "Next0 = new NamedProperty`1() {" +
                         "Name = Total, " +
                         "Value = ($it.Price * Convert($it.Qty)), " +
                         "}, " +
                         "Next1 = new AutoSelectedNamedProperty`1() {" +
                         "Name = Id, " +
                         "Value = Convert($it.Id), " +
                         "}, }, }", resultExpression);

            ComputeCustomer customer = new ComputeCustomer {
                Name = "Peter", Price = 1.99, Qty = 3
            };
            IDictionary <string, object> result = SelectExpandBinderTest.InvokeSelectExpand(customer, selectExp);

            Assert.Equal(2, result.Count);
            Assert.Collection(result,
                              e =>
            {
                Assert.Equal("Name", e.Key);
                Assert.Equal("Peter", e.Value);
            },
                              e =>
            {
                Assert.Equal("Total", e.Key);
                Assert.Equal(5.97, e.Value);
            });
        }
示例#6
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
        }
示例#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 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);
        }