/// <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)); }
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)); }
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); }
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); }); }
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 }
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++; } }
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); }