public static IQueryable Bind(IQueryable queryable, ODataQuerySettings settings, SelectExpandQueryOption selectExpandQuery) { Contract.Assert(queryable != null); SelectExpandBinder binder = new SelectExpandBinder(settings, selectExpandQuery); return binder.Bind(queryable); }
public static object Bind(object entity, ODataQuerySettings settings, SelectExpandQueryOption selectExpandQuery) { Contract.Assert(entity != null); SelectExpandBinder binder = new SelectExpandBinder(settings, selectExpandQuery); return binder.Bind(entity); }
/// <summary> /// Initialize a new instance of the <see cref="ODataQuerySettings"/> class based on an existing one. /// </summary> /// <param name="settings">The setting to copy from.</param> public ODataQuerySettings(ODataQuerySettings settings) { EnsureStableOrdering = settings.EnsureStableOrdering; EnableConstantParameterization = settings.EnableConstantParameterization; HandleNullPropagation = settings.HandleNullPropagation; PageSize = settings.PageSize; }
public SelectExpandBinder(ODataQuerySettings settings, SelectExpandQueryOption selectExpandQuery) { Contract.Assert(settings != null); Contract.Assert(selectExpandQuery != null); Contract.Assert(selectExpandQuery.Context != null); Contract.Assert(selectExpandQuery.Context.Model != null); Contract.Assert(settings.HandleNullPropagation != HandleNullPropagationOption.Default); _selectExpandQuery = selectExpandQuery; _context = selectExpandQuery.Context; _model = _context.Model; _modelID = ModelContainer.GetModelID(_model); _settings = settings; }
public SelectExpandBinderTest() { _settings = new ODataQuerySettings { HandleNullPropagation = HandleNullPropagationOption.False }; _model = new CustomersModelWithInheritance(); _model.Model.SetAnnotationValue<ClrTypeAnnotation>(_model.Customer, new ClrTypeAnnotation(typeof(Customer))); _model.Model.SetAnnotationValue<ClrTypeAnnotation>(_model.SpecialCustomer, new ClrTypeAnnotation(typeof(SpecialCustomer))); _context = new ODataQueryContext(_model.Model, typeof(Customer)); _binder = new SelectExpandBinder(_settings, new SelectExpandQueryOption("*", "", _context)); Customer customer = new Customer(); Order order = new Order { Customer = customer }; customer.Orders.Add(order); _queryable = new[] { customer }.AsQueryable(); }
internal AggregationBinder(ODataQuerySettings settings, IAssembliesResolver assembliesResolver, Type elementType, IEdmModel model, TransformationNode transformation) : base(model, assembliesResolver, settings) { Contract.Assert(elementType != null); Contract.Assert(transformation != null); _elementType = elementType; _transformation = transformation; this._lambdaParameter = Expression.Parameter(this._elementType, "$it"); switch (transformation.Kind) { case TransformationNodeKind.Aggregate: var aggregateClause = this._transformation as AggregateTransformationNode; _aggregateExpressions = aggregateClause.Expressions; ResultClrType = AggregationDynamicTypeProvider.GetResultType<DynamicTypeWrapper>(_model, null, _aggregateExpressions); break; case TransformationNodeKind.GroupBy: var groupByClause = this._transformation as GroupByTransformationNode; _groupingProperties = groupByClause.GroupingProperties; if (groupByClause.ChildTransformations != null) { if (groupByClause.ChildTransformations.Kind == TransformationNodeKind.Aggregate) { _aggregateExpressions = ((AggregateTransformationNode)groupByClause.ChildTransformations).Expressions; } else { throw new NotImplementedException(); } } _groupByClrType = AggregationDynamicTypeProvider.GetResultType<DynamicTypeWrapper>(_model, _groupingProperties, null); ResultClrType = AggregationDynamicTypeProvider.GetResultType<DynamicTypeWrapper>(_model, _groupingProperties, _aggregateExpressions); break; default: throw new NotSupportedException(String.Format(CultureInfo.InvariantCulture, SRResources.NotSupportedTransformationKind, transformation.Kind)); } _groupByClrType = _groupByClrType ?? typeof(DynamicTypeWrapper); }
/// <summary> /// Enables a controller action to support OData query parameters. /// </summary> public EnableQueryAttribute() { _validationSettings = new ODataValidationSettings(); _querySettings = new ODataQuerySettings(); }
public static ODataQuerySettings UpdateQuerySettings(this ODataQueryContext context, ODataQuerySettings querySettings, IQueryable query) { ODataQuerySettings updatedSettings = context.RequestContainer.GetRequiredService <ODataQuerySettings>(); updatedSettings.CopyFrom(querySettings); if (updatedSettings.HandleNullPropagation == HandleNullPropagationOption.Default) { updatedSettings.HandleNullPropagation = query != null ? HandleNullPropagationOptionHelper.GetDefaultHandleNullPropagationOption(query) : HandleNullPropagationOption.True; } return(updatedSettings); }
internal AggregationBinderEFFake(ODataQuerySettings settings, IWebApiAssembliesResolver assembliesResolver, Type elementType, IEdmModel model, TransformationNode transformation) : base(settings, assembliesResolver, elementType, model, transformation) { }
/// <summary> /// Creates a new instance of <see cref="StorageDomainManager{TData}"/> /// </summary> /// <param name="connectionStringName"> /// Name of the storage connection string /// </param> /// <param name="tableName"> /// Name of table /// </param> /// <param name="request"> /// An instance of <see cref="HttpRequestMessage"/> /// </param> /// <param name="querySettings"> /// An instance of <see cref="ODataQuerySettings"/> /// </param> /// <param name="validationSettings"> /// An instance of <see cref="ODataValidationSettings"/> /// </param> /// <param name="enableSoftDelete"> /// Determines whether rows are hard deleted or marked as deleted. /// </param> public StorageDomainManager(string connectionStringName, string tableName, HttpRequestMessage request, ODataValidationSettings validationSettings, ODataQuerySettings querySettings, bool enableSoftDelete) : base(request, enableSoftDelete) { if (connectionStringName == null) { throw new ArgumentNullException("connectionStringName"); } if (tableName == null) { throw new ArgumentNullException("tableName"); } if (validationSettings == null) { throw new ArgumentNullException("validationSettings"); } if (querySettings == null) { throw new ArgumentNullException("querySettings"); } this.validationSettings = validationSettings; this.querySettings = querySettings; this.StorageAccount = CloudStorageAccounts.GetOrAdd(connectionStringName, this.GetCloudStorageAccount); this.TableName = tableName; this.TableClient = this.StorageAccount.CreateCloudTableClient(); this.Table = this.TableClient.GetTableReference(this.TableName); this.systemPropertyMap = typeof(StorageData).IsAssignableFrom(typeof(TData)) ? StorageDataSystemPropertyMap : null; }
private static IQueryable AddOrderByQueryForProperty <T>(ODataQuery <T> query, ODataQuerySettings querySettings, OrderByClause orderbyClause, IQueryable querySoFar, OrderByDirection direction, bool alreadyOrdered) { //Context.UpdateQuerySettings(querySettings, query); LambdaExpression orderByExpression = FilterBinder.Bind(query, orderbyClause, typeof(T), query.ServiceProvider); querySoFar = ExpressionHelpers.OrderBy(querySoFar, orderByExpression, direction, typeof(T), alreadyOrdered); return(querySoFar); }
public static IQueryable <object> ApplyQuery(this IQueryable query, int recordLimit, IDictionary <string, string> queryString) { Check.NotNull(query, nameof(query)); Check.NotNull(queryString, nameof(queryString)); var defaultOdataQuerySettings = new ODataQuerySettings(); var settings = new ODataValidationSettings { MaxTop = recordLimit }; //Build odata context var modelBuilder = ODataQueryExtensions.ConfigureConventionBuilder(); modelBuilder.AddEntityType(query.ElementType); modelBuilder.OnModelCreating += builder => AddCompoundKeyConvention(builder); var edmModel = modelBuilder.GetEdmModel(); var odataPath = new ODataPath(); var odataQueryConext = new ODataQueryContext(edmModel, query.ElementType, odataPath); var options = odataQueryConext.CreateOptionsFromQueryString(queryString); //set default record top limit if (options.Top == null && options.Count?.Value != true) { queryString.Add("$top", recordLimit.ToString(CultureInfo.InvariantCulture)); options = odataQueryConext.CreateOptionsFromQueryString(queryString); } options.Validate(settings); IQueryable <dynamic> retList; if (options.OrderBy == null) { //Optimize odata filter ordering IQueryable results = options.ApplyTo(query, defaultOdataQuerySettings, AllowedQueryOptions.OrderBy); retList = results as IQueryable <dynamic>; } else { //Optimize odata filter ordering IQueryable results = options.OrderBy.ApplyTo(query, defaultOdataQuerySettings); results = options.ApplyTo(results, defaultOdataQuerySettings, AllowedQueryOptions.OrderBy); retList = results as IQueryable <dynamic>; } if (options.Count?.Value == true) { IList <dynamic> count = new List <dynamic>(); CountTotal countObj = new CountTotal { Count = retList.Count() }; count.Add(countObj); return(count.AsQueryable()); } return(retList); }
protected ExpressionBinderBase(IEdmModel model, IAssembliesResolver assembliesResolver, ODataQuerySettings querySettings) : this(model, querySettings) { _assembliesResolver = assembliesResolver; }
/// <summary> /// Apply the queryOptions to the query. /// This method handles nested order-by statements the the current ASP.NET web api does not yet support. /// </summary> /// <param name="queryable"></param> /// <param name="queryOptions"></param> /// <param name="querySettings"></param> /// <returns></returns> public static IQueryable ApplyQuery(IQueryable queryable, ODataQueryOptions queryOptions, ODataQuerySettings querySettings) { // HACK: this is a hack because on a bug in querySettings.EnsureStableOrdering = true that overrides // any existing order by clauses, instead of appending to them. querySettings.EnsureStableOrdering = false; // Basic idea here is the current WebApi OData cannot support the following operations // 1) "orderby" with nested properties // 2) "select" with complex types // 3) "selects" of "nested" properties unless accompanied by the appropriate "expand". // i.e. can't do Customers.select("orders") unless we also add expand("orders") // The workaround here is to bypass "select" and "orderBy" processing under these conditions // This involves removing the "offending" queryOptions before asking the WebApi2 OData processing to do its thing // and then post processing the resulting IQueryable. // We actually do this with all selects because it's easier than trying to determine if they are actually problematic. // Another approach that DOESN'T work is to let WebApi2 OData try to do it stuff and then only handle the cases where it throws an exception. // This doesn't work because WebApi2 OData will actually just skip the portions of the query that it can't process and return what it can ( under some conditions). var expandQueryString = queryOptions.RawValues.Expand; var orderByQueryString = queryOptions.RawValues.OrderBy; var selectQueryString = queryOptions.RawValues.Select; ODataQueryOptions newQueryOptions = queryOptions; if (selectQueryString != null) { newQueryOptions = QueryHelper.RemoveSelectAndExpand(newQueryOptions); } if (orderByQueryString != null && orderByQueryString.IndexOf('/') >= 0) { newQueryOptions = QueryHelper.RemoveSelectAndExpand(newQueryOptions); newQueryOptions = QueryHelper.RemoveExtendedOps(newQueryOptions); } if (newQueryOptions == queryOptions) { return(queryOptions.ApplyTo(queryable, querySettings)); } else { // apply default processing first with "unsupported" stuff removed. var q = newQueryOptions.ApplyTo(queryable, querySettings); // then apply unsupported stuff. var qh = new QueryHelper(querySettings); if (orderByQueryString != null && orderByQueryString.IndexOf('/') >= 0) { q = qh.ApplyNestedOrderBy(q, queryOptions); } if (selectQueryString != null) { q = qh.ApplySelect(q, selectQueryString); } else if (expandQueryString != null) { // don't bother doing an expand if there was already a select ( this code doesn't need it) q = qh.ApplyExpand(q, queryOptions.RawValues.Expand); } return(q); } }
internal static Expression <Func <TEntityType, bool> > Bind <TEntityType>(FilterClause filterClause, IEdmModel model, IWebApiAssembliesResolver assembliesResolver, ODataQuerySettings querySettings) { return(Bind(filterClause, typeof(TEntityType), model, assembliesResolver, querySettings) as Expression <Func <TEntityType, bool> >); }
internal Expression CreatePropertyValueExpressionWithFilter(IEdmEntityType elementType, IEdmProperty property, Expression source, FilterClause filterClause) { Contract.Assert(elementType != null); Contract.Assert(property != null); Contract.Assert(source != null); IEdmEntityType declaringType = property.DeclaringType as IEdmEntityType; Contract.Assert(declaringType != null, "only entity types are projected."); // derived property using cast if (elementType != declaringType) { Type castType = EdmLibHelpers.GetClrType(declaringType, _model); if (castType == null) { throw new ODataException(Error.Format(SRResources.MappingDoesNotContainResourceType, declaringType.FullName())); } source = Expression.TypeAs(source, castType); } string propertyName = EdmLibHelpers.GetClrPropertyName(property, _model); Expression propertyValue = Expression.Property(source, propertyName); Type nullablePropertyType = TypeHelper.ToNullable(propertyValue.Type); Expression nullablePropertyValue = ExpressionHelpers.ToNullable(propertyValue); if (filterClause != null) { bool isCollection = property.Type.IsCollection(); IEdmTypeReference edmElementType = (isCollection ? property.Type.AsCollection().ElementType() : property.Type); Type clrElementType = EdmLibHelpers.GetClrType(edmElementType, _model); if (clrElementType == null) { throw new ODataException(Error.Format(SRResources.MappingDoesNotContainResourceType, edmElementType.FullName())); } Expression filterResult = nullablePropertyValue; ODataQuerySettings querySettings = new ODataQuerySettings() { HandleNullPropagation = HandleNullPropagationOption.True, }; if (isCollection) { Expression filterSource = typeof(IEnumerable).IsAssignableFrom(source.Type.GetProperty(propertyName).PropertyType) ? Expression.Call( ExpressionHelperMethods.QueryableAsQueryable.MakeGenericMethod(clrElementType), nullablePropertyValue) : nullablePropertyValue; // TODO: Implement proper support for $select/$expand after $apply Expression filterPredicate = FilterBinder.Bind(null, filterClause, clrElementType, _context, querySettings); filterResult = Expression.Call( ExpressionHelperMethods.QueryableWhereGeneric.MakeGenericMethod(clrElementType), filterSource, filterPredicate); nullablePropertyType = filterResult.Type; } else if (_settings.HandleReferenceNavigationPropertyExpandFilter) { LambdaExpression filterLambdaExpression = FilterBinder.Bind(null, filterClause, clrElementType, _context, querySettings) as LambdaExpression; if (filterLambdaExpression == null) { throw new ODataException(Error.Format(SRResources.ExpandFilterExpressionNotLambdaExpression, property.Name, "LambdaExpression")); } ParameterExpression filterParameter = filterLambdaExpression.Parameters.First(); Expression predicateExpression = new ReferenceNavigationPropertyExpandFilterVisitor(filterParameter, nullablePropertyValue).Visit(filterLambdaExpression.Body); // create expression similar to: 'predicateExpression == true ? nullablePropertyValue : null' filterResult = Expression.Condition( test: predicateExpression, ifTrue: nullablePropertyValue, ifFalse: Expression.Constant(value: null, type: nullablePropertyType)); } if (_settings.HandleNullPropagation == HandleNullPropagationOption.True) { // create expression similar to: 'nullablePropertyValue == null ? null : filterResult' nullablePropertyValue = Expression.Condition( test: Expression.Equal(nullablePropertyValue, Expression.Constant(value: null)), ifTrue: Expression.Constant(value: null, type: nullablePropertyType), ifFalse: filterResult); } else { nullablePropertyValue = filterResult; } } if (_settings.HandleNullPropagation == HandleNullPropagationOption.True) { // create expression similar to: 'source == null ? null : propertyValue' propertyValue = Expression.Condition( test: Expression.Equal(source, Expression.Constant(value: null)), ifTrue: Expression.Constant(value: null, type: nullablePropertyType), ifFalse: nullablePropertyValue); } else { // need to cast this to nullable as EF would fail while materializing if the property is not nullable and source is null. propertyValue = nullablePropertyValue; } return(propertyValue); }
/// <summary> /// Apply the queryOptions to the query. /// This method handles nested order-by statements the the current ASP.NET web api does not yet support. /// </summary> /// <param name="queryable"></param> /// <param name="queryOptions"></param> /// <param name="querySettings"></param> /// <returns></returns> public IQueryable ApplyQuery(IQueryable queryable, ODataQueryOptions queryOptions, ODataQuerySettings querySettings) { // HACK: this is a hack because on a bug in querySettings.EnsureStableOrdering = true that overrides // any existing order by clauses, instead of appending to them. querySettings.EnsureStableOrdering = false; // Basic idea here is the current WebApi OData cannot support the following operations // 1) "orderby" with nested properties // 2) "select" with complex types // 3) "selects" of "nested" properties unless accompanied by the appropriate "expand". // i.e. can't do Customers.select("orders") unless we also add expand("orders") // The workaround here is to bypass "select" and "orderBy" processing under these conditions // This involves removing the "offending" queryOptions before asking the WebApi2 OData processing to do its thing // and then post processing the resulting IQueryable. // We actually do this with all selects because it's easier than trying to determine if they are actually problematic. // Another approach that DOESN'T work is to let WebApi2 OData try to do it stuff and then only handle the cases where it throws an exception. // This doesn't work because WebApi2 OData will actually just skip the portions of the query that it can't process and return what it can ( under some conditions). var expandQueryString = queryOptions.RawValues.Expand; var orderByQueryString = queryOptions.RawValues.OrderBy; var selectQueryString = queryOptions.RawValues.Select; ODataQueryOptions newQueryOptions = queryOptions; if (!string.IsNullOrWhiteSpace(selectQueryString)) { newQueryOptions = QueryHelper.RemoveSelectExpandOrderBy(newQueryOptions); } else if ((!string.IsNullOrWhiteSpace(orderByQueryString)) && orderByQueryString.IndexOf('/') >= 0) { newQueryOptions = QueryHelper.RemoveSelectExpandOrderBy(newQueryOptions); } else if (ManuallyExpand && !string.IsNullOrWhiteSpace(expandQueryString)) { newQueryOptions = QueryHelper.RemoveSelectExpandOrderBy(newQueryOptions); } if (newQueryOptions == queryOptions) { return(queryOptions.ApplyTo(queryable, querySettings)); } else { // remove inlinecount or it will be executed two times if (newQueryOptions.InlineCount != null) { newQueryOptions = QueryHelper.RemoveInlineCount(newQueryOptions); } // apply default processing first with "unsupported" stuff removed. var q = newQueryOptions.ApplyTo(queryable, querySettings); // then apply unsupported stuff. string inlinecountString = queryOptions.RawValues.InlineCount; if (!string.IsNullOrWhiteSpace(inlinecountString)) { if (inlinecountString == "allpages") { var inlineCount = (Int64)Queryable.Count((dynamic)q); queryOptions.Request.SetInlineCount(inlineCount); } } q = ApplyOrderBy(q, queryOptions); var q2 = ApplySelect(q, queryOptions); if (q2 == q) { q2 = ApplyExpand(q, queryOptions); } return(q2); } }
protected EntitySetController(IContainerMetadata containerMetadata, ODataValidationSettings queryValidationSettings, ODataQuerySettings querySettings) { Contract.Requires<ArgumentNullException>(containerMetadata != null); Contract.Requires<ArgumentNullException>(queryValidationSettings != null); Contract.Requires<ArgumentNullException>(querySettings != null); _entitySetMetadata = containerMetadata.GetEntitySetFor(typeof(TEntity)); if (_entitySetMetadata == null) { throw new InvalidOperationException("EntitySet not found in containerMetadata for type " + typeof(TEntity)); } _queryValidationSettings = queryValidationSettings; _querySettings = querySettings; }
private static FilterBinder GetOrCreateFilterBinder(ODataQueryContext context, ODataQuerySettings querySettings) { FilterBinder binder = null; if (context.RequestContainer != null) { binder = context.RequestContainer.GetRequiredService <FilterBinder>(); if (binder != null && binder.Model != context.Model && binder.Model == EdmCoreModel.Instance) { binder.Model = context.Model; } } return(binder ?? new FilterBinder(querySettings, WebApiAssembliesResolver.Default, context.Model)); }
internal FilterBinder(ODataQuerySettings settings, IWebApiAssembliesResolver assembliesResolver, IEdmModel model) : base(model, assembliesResolver, settings) { }
public void CanApplyOrderBy_WithCollectionCount(string orderby) { // Arrange var model = new ODataModelBuilder() .Add_Order_EntityType() .Add_Customer_EntityType_With_Address() .Add_CustomerOrders_Relationship() .Add_Customer_EntityType_With_CollectionProperties() .Add_Customers_EntitySet() .GetEdmModel(); var parser = new ODataQueryOptionParser( model, model.FindType("Microsoft.AspNetCore.OData.Tests.Models.Customer"), model.FindDeclaredNavigationSource("Default.Container.Customers"), new Dictionary <string, string> { { "$orderby", orderby } }); var orderByOption = new OrderByQueryOption(orderby, new ODataQueryContext(model, typeof(Customer)), parser); var customers = (new List <Customer> { new Customer { Id = 1, Name = "Andy", Orders = new List <Order> { new Order { OrderId = 1 }, new Order { OrderId = 2 } }, Addresses = new List <Address> { new Address { City = "1" }, new Address { City = "2" } }, Aliases = new List <string> { "1", "2" } }, new Customer { Id = 2, Name = "Aaron", Orders = new List <Order> { new Order { OrderId = 3 } }, Addresses = new List <Address> { new Address { City = "3" } }, Aliases = new List <string> { "3" } }, new Customer { Id = 3, Name = "Alex" } }).AsQueryable(); // Act ODataQuerySettings settings = new ODataQuerySettings { HandleNullPropagation = HandleNullPropagationOption.True }; var results = orderByOption.ApplyTo(customers, settings).ToArray(); // Assert Assert.Equal(3, results[0].Id); Assert.Equal(2, results[1].Id); Assert.Equal(1, results[2].Id); }
internal ExpressionBinderBase(IEdmModel model, IAssemblyProvider assemblyProvider, ODataQuerySettings querySettings) : this(model, querySettings) { AssemblyProvider = assemblyProvider; }
internal TransformationBinderBase(ODataQuerySettings settings, IAssemblyResolver assembliesResolver, Type elementType, IEdmModel model) : base(model, assembliesResolver, settings) { Contract.Assert(elementType != null); LambdaParameter = Expression.Parameter(elementType, "$it"); }
public QueryHelper(ODataQuerySettings querySettings) { this.querySettings = querySettings; }
public static IOrderedQueryable OrderApplyToCore <T>(ODataQuery <T> query, ODataQuerySettings querySettings, ICollection <OrderByNode> nodes, IEdmModel model) { bool alreadyOrdered = false; IQueryable querySoFar = query; HashSet <object> propertiesSoFar = new HashSet <object>(); HashSet <string> openPropertiesSoFar = new HashSet <string>(); bool orderByItSeen = false; foreach (OrderByNode node in nodes) { OrderByPropertyNode propertyNode = node as OrderByPropertyNode; OrderByOpenPropertyNode openPropertyNode = node as OrderByOpenPropertyNode; 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(query, querySettings, propertyNode.OrderByClause, querySoFar, direction, alreadyOrdered); } else { querySoFar = ExpressionHelpers.OrderByProperty(querySoFar, model, edmPropertyWithPath.Property, direction, typeof(T), 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(query, querySettings, openPropertyNode.OrderByClause, querySoFar, openPropertyNode.Direction, 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, typeof(T), alreadyOrdered); alreadyOrdered = true; orderByItSeen = true; } } return(querySoFar as IOrderedQueryable); }
public QueryHelper(bool enableConstantParameterization, bool ensureStableOrdering, HandleNullPropagationOption handleNullPropagation, int?pageSize) { this.querySettings = NewODataQuerySettings(enableConstantParameterization, ensureStableOrdering, handleNullPropagation, pageSize); }
/// <summary> /// Creates a new instance of <see cref="StorageDomainManager{TData}"/> /// </summary> /// <param name="connectionStringName"> /// Name of the storage connection string /// </param> /// <param name="tableName"> /// Name of table /// </param> /// <param name="request"> /// An instance of <see cref="HttpRequestMessage"/> /// </param> /// <param name="querySettings"> /// An instance of <see cref="ODataQuerySettings"/> /// </param> /// <param name="validationSettings"> /// An instance of <see cref="ODataValidationSettings"/> /// </param> public StorageDomainManager(string connectionStringName, string tableName, HttpRequestMessage request, ODataValidationSettings validationSettings, ODataQuerySettings querySettings) : this(connectionStringName, tableName, request, validationSettings, querySettings, enableSoftDelete : false) { }
/// <summary> /// Apply the queryOptions to the query. /// This method handles nested order-by statements the the current ASP.NET web api does not yet support. /// </summary> /// <param name="queryable"></param> /// <param name="queryOptions"></param> /// <param name="querySettings"></param> /// <returns></returns> public static IQueryable ApplyQuery(IQueryable queryable, ODataQueryOptions queryOptions, ODataQuerySettings querySettings) { // if we see an extended order by we also need to process any skip/take operators as well. var orderByQueryString = queryOptions.RawValues.OrderBy; if (orderByQueryString == null || orderByQueryString.IndexOf('/') < 0) { // Just let the default implementation handle it. return(queryOptions.ApplyTo(queryable, querySettings)); } var newQueryOptions = QueryHelper.RemoveExtendedOps(queryOptions); var result = QueryHelper.ApplyQuery(queryable, newQueryOptions, querySettings); var elementType = TypeFns.GetElementType(queryable.GetType()); var orderByClauses = orderByQueryString.Split(',').ToList(); var isThenBy = false; orderByClauses.ForEach(obc => { var func = QueryBuilder.BuildOrderByFunc(isThenBy, elementType, obc); result = func(result); isThenBy = true; }); var skipQueryString = queryOptions.RawValues.Skip; if (!string.IsNullOrWhiteSpace(skipQueryString)) { var count = int.Parse(skipQueryString); var method = TypeFns.GetMethodByExample((IQueryable <String> q) => Queryable.Skip <String>(q, 999), elementType); var func = BuildIQueryableFunc(elementType, method, count); result = func(result); } var topQueryString = queryOptions.RawValues.Top; if (!string.IsNullOrWhiteSpace(topQueryString)) { var count = int.Parse(topQueryString); var method = TypeFns.GetMethodByExample((IQueryable <String> q) => Queryable.Take <String>(q, 999), elementType); var func = BuildIQueryableFunc(elementType, method, count); result = func(result); } return(result); }
public NHQueryHelper(ODataQuerySettings querySettings) : base(querySettings) { }
internal ExpressionBinderBase(IEdmModel model, IAssembliesResolver assembliesResolver, ODataQuerySettings querySettings) : this(model, querySettings) { this.AssembliesResolver = assembliesResolver; }
public ReadOnlyDbSetController(Lazy <TDbContext> lazyDbContext, IContainerMetadata <TDbContext> containerMetadata, ODataValidationSettings queryValidationSettings, ODataQuerySettings querySettings) : base(containerMetadata, queryValidationSettings, querySettings) { Contract.Requires <ArgumentNullException>(lazyDbContext != null); _lazyDb = lazyDbContext; }
public static IQueryable <T> ApplyOptions <T>(this IQueryable <T> queryable, ODataQueryOptions <T> options, ODataQuerySettings querySettings) { return((IQueryable <T>)options.ApplyTo(queryable, querySettings)); }
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext) { if (actionExecutedContext == null) { throw Error.ArgumentNull("actionExecutedContext"); } HttpRequestMessage request = actionExecutedContext.Request; if (request == null) { throw Error.Argument("actionExecutedContext", SRResources.ActionExecutedContextMustHaveRequest); } HttpConfiguration configuration = request.GetConfiguration(); if (configuration == null) { throw Error.Argument("actionExecutedContext", SRResources.RequestMustContainConfiguration); } if (actionExecutedContext.ActionContext == null) { throw Error.Argument("actionExecutedContext", SRResources.ActionExecutedContextMustHaveActionContext); } HttpActionDescriptor actionDescriptor = actionExecutedContext.ActionContext.ActionDescriptor; if (actionDescriptor == null) { throw Error.Argument("actionExecutedContext", SRResources.ActionContextMustHaveDescriptor); } HttpResponseMessage response = actionExecutedContext.Response; IEnumerable query; IQueryable queryable = null; if (response != null && response.IsSuccessStatusCode && response.TryGetContentValue(out query)) { if (request.RequestUri != null && !String.IsNullOrWhiteSpace(request.RequestUri.Query)) { ValidateQuery(request); try { ODataQueryContext queryContext; Type originalQueryType = query.GetType(); Type entityClrType = TypeHelper.GetImplementedIEnumerableType(originalQueryType); if (entityClrType == null) { // The element type cannot be determined because the type of the content // is not IEnumerable<T> or IQueryable<T>. throw Error.InvalidOperation( SRResources.FailedToRetrieveTypeToBuildEdmModel, this.GetType().Name, actionDescriptor.ActionName, actionDescriptor.ControllerDescriptor.ControllerName, originalQueryType.FullName); } // Primitive types do not construct an EDM model and deal only with the CLR Type if (TypeHelper.IsQueryPrimitiveType(entityClrType)) { queryContext = new ODataQueryContext(entityClrType); } else { // Get model for the entire app IEdmModel model = configuration.GetEdmModel(); if (model == null) { // user has not configured anything, now let's create one just for this type // and cache it in the action descriptor model = actionDescriptor.GetEdmModel(entityClrType); Contract.Assert(model != null); } // parses the query from request uri queryContext = new ODataQueryContext(model, entityClrType); } ODataQueryOptions queryOptions = new ODataQueryOptions(queryContext, request); // Filter and OrderBy require entity sets. Top and Skip may accept primitives. if (queryContext.IsPrimitiveClrType && (queryOptions.Filter != null || queryOptions.OrderBy != null)) { // An attempt to use a query option not allowed for primitive types // generates a BadRequest with a general message that avoids information disclosure. actionExecutedContext.Response = request.CreateErrorResponse( HttpStatusCode.BadRequest, SRResources.OnlySkipAndTopSupported); return; } // apply the query queryable = query as IQueryable; if (queryable == null) { queryable = query.AsQueryable(); } ODataQuerySettings querySettings = new ODataQuerySettings { EnsureStableOrdering = EnsureStableOrdering, HandleNullPropagation = HandleNullPropagation }; queryable = queryOptions.ApplyTo(queryable, querySettings); Contract.Assert(queryable != null); // we don't support shape changing query composition ((ObjectContent)response.Content).Value = queryable; } catch (ODataException e) { actionExecutedContext.Response = request.CreateErrorResponse( HttpStatusCode.BadRequest, SRResources.UriQueryStringInvalid, e); return; } } } }
public static Uri GetNextLink(Uri currentRequestUri, long?totalResultCount, object queryParameters, ODataQueryOptions options, ODataQuerySettings settings, int?semVerLevelKey = null) { if (!totalResultCount.HasValue || totalResultCount.Value <= MaxPageSize || totalResultCount.Value == 0) { return(null); // no need for a next link if there are no additional results on this page } var skipCount = (options.Skip != null ? options.Skip.Value : 0) + Math.Min(totalResultCount.Value, (settings.PageSize != null ? settings.PageSize.Value : SearchAdaptor.MaxPageSize)); if (totalResultCount.Value <= skipCount) { return(null); // no need for a next link if there are no additional results in the result set } var queryBuilder = new StringBuilder(); var queryParametersCollection = new RouteValueDictionary(queryParameters); foreach (var queryParameter in queryParametersCollection) { queryBuilder.Append(Uri.EscapeDataString(queryParameter.Key)); queryBuilder.Append("="); if (queryParameter.Value != null) { if (queryParameter.Value is string) { queryBuilder.Append(Uri.EscapeDataString("'" + queryParameter.Value + "'")); } else if (queryParameter.Value is bool) { queryBuilder.Append(queryParameter.Value.ToString().ToLowerInvariant()); } else { queryBuilder.Append(queryParameter.Value.ToString().ToLowerInvariant()); } } queryBuilder.Append("&"); } if (options.SelectExpand != null) { if (!string.IsNullOrEmpty(options.SelectExpand.RawSelect)) { queryBuilder.Append("$select="); queryBuilder.Append(options.SelectExpand.RawSelect); queryBuilder.Append("&"); } if (!string.IsNullOrEmpty(options.SelectExpand.RawExpand)) { queryBuilder.Append("$expand="); queryBuilder.Append(options.SelectExpand.RawExpand); queryBuilder.Append("&"); } } if (options.Filter != null) { queryBuilder.Append("$filter="); queryBuilder.Append(options.Filter.RawValue); queryBuilder.Append("&"); } if (options.OrderBy != null) { queryBuilder.Append("$orderby="); queryBuilder.Append(options.OrderBy.RawValue); queryBuilder.Append("&"); } if (skipCount > 0) { queryBuilder.Append("$skip="); queryBuilder.Append(skipCount); queryBuilder.Append("&"); } if (options.Top != null) { queryBuilder.Append("$top="); queryBuilder.Append(options.Top.RawValue); queryBuilder.Append("&"); } if (semVerLevelKey != null) { if (semVerLevelKey == SemVerLevelKey.SemVer2) { queryBuilder.Append("semVerLevel="); queryBuilder.Append(SemVerLevelKey.SemVerLevel2); queryBuilder.Append("&"); } } var queryString = queryBuilder.ToString().TrimEnd('&'); var builder = new UriBuilder(currentRequestUri); builder.Query = queryString; return(builder.Uri); }
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++; } }
internal static Expression Bind(FilterClause filterClause, Type filterType, IEdmModel model, IWebApiAssembliesResolver assembliesResolver, ODataQuerySettings querySettings) { if (filterClause == null) { throw Error.ArgumentNull("filterClause"); } if (filterType == null) { throw Error.ArgumentNull("filterType"); } if (model == null) { throw Error.ArgumentNull("model"); } if (assembliesResolver == null) { throw Error.ArgumentNull("assembliesResolver"); } FilterBinder binder = new FilterBinder(model, assembliesResolver, querySettings, filterType); return(BindFilterClause(binder, filterClause, filterType)); }