/// <summary>Sorts a query like a SQL ORDER BY clause does.</summary> /// <param name="service">Service with data and configuration.</param> /// <param name="source">Original source for query.</param> /// <param name="orderingInfo">Ordering definition to compose.</param> /// <returns>The composed query.</returns> internal static IQueryable OrderBy(IDataService service, IQueryable source, OrderingInfo orderingInfo) { Debug.Assert(service != null, "service != null"); Debug.Assert(source != null, "source != null"); Debug.Assert(orderingInfo != null, "orderingInfo != null"); Expression queryExpr = source.Expression; string methodAsc = "OrderBy"; string methodDesc = "OrderByDescending"; foreach (OrderingExpression o in orderingInfo.OrderingExpressions) { LambdaExpression selectorLambda = (LambdaExpression)o.Expression; Type selectorType = selectorLambda.Body.Type; service.Provider.CheckIfOrderedType(selectorType); queryExpr = Expression.Call( typeof(Queryable), o.IsAscending ? methodAsc : methodDesc, new Type[] { source.ElementType, selectorType }, queryExpr, Expression.Quote(selectorLambda)); methodAsc = "ThenBy"; methodDesc = "ThenByDescending"; } return source.Provider.CreateQuery(queryExpr); }
internal ExpandSegment(string name, Expression filter, int maxResultsExpected, ResourceSetWrapper container, ResourceType targetResourceType, ResourceProperty expandedProperty, System.Data.Services.Providers.OrderingInfo orderingInfo) { WebUtil.CheckArgumentNull <string>(name, "name"); CheckFilterType(filter); this.name = name; this.filter = filter; this.container = container; this.maxResultsExpected = maxResultsExpected; this.expandedProperty = expandedProperty; this.orderingInfo = orderingInfo; this.targetResourceType = targetResourceType; }
internal ExpandSegment(string name, Expression filter, int maxResultsExpected, ResourceSetWrapper container, ResourceType targetResourceType, ResourceProperty expandedProperty, System.Data.Services.Providers.OrderingInfo orderingInfo) { WebUtil.CheckArgumentNull<string>(name, "name"); CheckFilterType(filter); this.name = name; this.filter = filter; this.container = container; this.maxResultsExpected = maxResultsExpected; this.expandedProperty = expandedProperty; this.orderingInfo = orderingInfo; this.targetResourceType = targetResourceType; }
internal static ICollection<ResourceProperty> CollectSkipTokenProperties(OrderingInfo orderingInfo, ResourceType rt) { List<ResourceProperty> list = new List<ResourceProperty>(); foreach (OrderingExpression expression in orderingInfo.OrderingExpressions) { LambdaExpression expression2 = (LambdaExpression) expression.Expression; NeedSkipTokenVisitor visitor = new NeedSkipTokenVisitor(rt); visitor.Visit(expression2.Body); if (visitor.NeedSkipToken) { return null; } list.Add(visitor.Property); } return list; }
internal static bool IsSkipTokenRequired(OrderingInfo orderingInfo) { if ((orderingInfo != null) && orderingInfo.IsPaged) { foreach (OrderingExpression expression in orderingInfo.OrderingExpressions) { LambdaExpression expression2 = (LambdaExpression) expression.Expression; NeedSkipTokenVisitor visitor = new NeedSkipTokenVisitor(); visitor.Visit(expression2.Body); if (visitor.NeedSkipToken) { return true; } } } return false; }
private RequestQueryProcessor(IDataService service, RequestDescription description) { this.service = service; this.description = description; this.orderApplied = false; this.skipCount = null; this.topCount = null; this.queryExpression = description.RequestExpression; this.filterQueryApplicable = (((description.TargetKind == RequestTargetKind.Resource) || (description.TargetKind == RequestTargetKind.OpenProperty)) || (description.TargetKind == RequestTargetKind.ComplexObject)) || (description.CountOption == RequestQueryCountOption.ValueOnly); this.setQueryApplicable = ((description.TargetKind == RequestTargetKind.Resource) && !description.IsSingleResult) || (description.CountOption == RequestQueryCountOption.ValueOnly); this.pagingApplicable = (((description.TargetKind == RequestTargetKind.Resource) && !description.IsSingleResult) && ((description.CountOption != RequestQueryCountOption.ValueOnly) && !description.IsRequestForEnumServiceOperation)) && ((service.OperationContext.Host.HttpVerb == HttpVerbs.GET) || (description.SegmentInfos[0].TargetSource == RequestTargetSource.ServiceOperation)); this.appliedCustomPaging = false; this.expandPaths = null; this.expandPathsAsText = null; this.rootProjectionNode = null; this.orderingParser = null; this.topLevelOrderingInfo = null; }
/// <summary>Creates new instance of node representing expanded navigation property.</summary> /// <param name="propertyName">The name of the property to project and expand.</param> /// <param name="property">The <see cref="ResourceProperty"/> for this property. Can only be null for the root node.</param> /// <param name="resourceSetWrapper">The resource set to which the expansion leads.</param> /// <param name="orderingInfo">The ordering info for this node. null means no ordering to be applied.</param> /// <param name="filter">The filter for this node. null means no filter to be applied.</param> /// <param name="skipCount">Number of results to skip. null means no results to be skipped.</param> /// <param name="takeCount">Maximum number of results to return. null means return all available results.</param> /// <param name="maxResultsExpected">Maximum number of expected results. Hint that the provider should return /// at least maxResultsExpected + 1 results (if available).</param> internal ExpandedProjectionNode( string propertyName, ResourceProperty property, ResourceSetWrapper resourceSetWrapper, OrderingInfo orderingInfo, Expression filter, int?skipCount, int?takeCount, int?maxResultsExpected) : base(propertyName, property) { Debug.Assert(resourceSetWrapper != null, "resourceSetWrapper != null"); Debug.Assert(property != null || propertyName.Length == 0, "We don't support open navigation properties."); this.resourceSetWrapper = resourceSetWrapper; this.orderingInfo = orderingInfo; this.filter = filter; this.skipCount = skipCount; this.takeCount = takeCount; this.maxResultsExpected = maxResultsExpected; this.nodes = new List <ProjectionNode>(); }
/// <summary>Creates new root node for the projection tree.</summary> /// <param name="resourceSetWrapper">The resource set of the root level of the query.</param> /// <param name="orderingInfo">The ordering info for this node. null means no ordering to be applied.</param> /// <param name="filter">The filter for this node. null means no filter to be applied.</param> /// <param name="skipCount">Number of results to skip. null means no results to be skipped.</param> /// <param name="takeCount">Maximum number of results to return. null means return all available results.</param> /// <param name="maxResultsExpected">Maximum number of expected results. Hint that the provider should return /// at least maxResultsExpected + 1 results (if available).</param> /// <param name="expandPaths">The list of expanded paths.</param> /// <param name="baseResourceType">The resource type for all entities in this query.</param> internal RootProjectionNode( ResourceSetWrapper resourceSetWrapper, OrderingInfo orderingInfo, Expression filter, int? skipCount, int? takeCount, int? maxResultsExpected, List<ExpandSegmentCollection> expandPaths, ResourceType baseResourceType) : base( String.Empty, null, resourceSetWrapper, orderingInfo, filter, skipCount, takeCount, maxResultsExpected) { Debug.Assert(baseResourceType != null, "baseResourceType != null"); this.expandPaths = expandPaths; this.baseResourceType = baseResourceType; }
/// <summary>Creates new root node for the projection tree.</summary> /// <param name="resourceSetWrapper">The resource set of the root level of the query.</param> /// <param name="orderingInfo">The ordering info for this node. null means no ordering to be applied.</param> /// <param name="filter">The filter for this node. null means no filter to be applied.</param> /// <param name="skipCount">Number of results to skip. null means no results to be skipped.</param> /// <param name="takeCount">Maximum number of results to return. null means return all available results.</param> /// <param name="maxResultsExpected">Maximum number of expected results. Hint that the provider should return /// at least maxResultsExpected + 1 results (if available).</param> /// <param name="expandPaths">The list of expanded paths.</param> /// <param name="baseResourceType">The resource type for all entities in this query.</param> internal RootProjectionNode( ResourceSetWrapper resourceSetWrapper, OrderingInfo orderingInfo, Expression filter, int?skipCount, int?takeCount, int?maxResultsExpected, List <ExpandSegmentCollection> expandPaths, ResourceType baseResourceType) : base( String.Empty, null, resourceSetWrapper, orderingInfo, filter, skipCount, takeCount, maxResultsExpected) { Debug.Assert(baseResourceType != null, "baseResourceType != null"); this.expandPaths = expandPaths; this.baseResourceType = baseResourceType; }
internal RootProjectionNode(ResourceSetWrapper resourceSetWrapper, OrderingInfo orderingInfo, Expression filter, int? skipCount, int? takeCount, int? maxResultsExpected, List<ExpandSegmentCollection> expandPaths, System.Data.Services.Providers.ResourceType baseResourceType) : base(string.Empty, null, null, resourceSetWrapper, orderingInfo, filter, skipCount, takeCount, maxResultsExpected) { this.expandPaths = expandPaths; this.baseResourceType = baseResourceType; }
/// <summary> /// Obtains a collection of resource properties that are needed for skip token generation /// </summary> /// <param name="orderingInfo">Input orderingInfo.</param> /// <param name="rt">Resource type for which to collect the skip token properties</param> /// <returns>Collection of resource properties used in $skiptoken</returns> internal static ICollection<ResourceProperty> CollectSkipTokenProperties(OrderingInfo orderingInfo, ResourceType rt) { Debug.Assert(orderingInfo != null, "Must have valid ordering information to collect skip token properties"); Debug.Assert(orderingInfo.IsPaged, "Must have paging enabled to collection skip token properties"); List<ResourceProperty> resourceProperties = new List<ResourceProperty>(); foreach (OrderingExpression o in orderingInfo.OrderingExpressions) { LambdaExpression l = (LambdaExpression)o.Expression; NeedSkipTokenVisitor visitor = new NeedSkipTokenVisitor(rt); visitor.Visit(l.Body); if (visitor.NeedSkipToken) { return null; } else { Debug.Assert(visitor.Property != null, "Must have a valid property if skip token is not needed"); resourceProperties.Add(visitor.Property); } } return resourceProperties; }
/// <summary>Makes the expression that is used as a filter corresponding to skip token.</summary> /// <param name="topLevelOrderingInfo">Ordering expression.</param> /// <param name="k">The provided skip token.</param> /// <returns>LambdaExpression corresponding to the skip token filter.</returns> internal LambdaExpression BuildSkipTokenFilter(OrderingInfo topLevelOrderingInfo, KeyInstance k) { ParameterExpression element = Expression.Parameter(this.typeForIt.InstanceType, "element"); Expression lastCondition = Expression.Constant(true, typeof(bool)); Expression lastMatch = Expression.Constant(false, typeof(bool)); foreach (var v in WebUtil.Zip(topLevelOrderingInfo.OrderingExpressions, k.PositionalValues, (x, y) => new { Order = x, Value = y })) { Token comparisonExp = v.Order.IsAscending ? Token.GreaterThan : Token.LessThan; Expression fixedLambda = System.Data.Services.Client.ParameterReplacerVisitor.Replace( ((LambdaExpression)v.Order.Expression).Body, ((LambdaExpression)v.Order.Expression).Parameters[0], element); Expression comparison = GenerateLogicalAnd( lastCondition, this.GenerateNullAwareComparison(fixedLambda, (String)v.Value, comparisonExp)); lastMatch = GenerateLogicalOr(lastMatch, comparison); lastCondition = GenerateLogicalAnd( lastCondition, this.GenerateComparison(fixedLambda, (String)v.Value, Token.EqualsTo)); } lastMatch = PrepareExpressionForWhere(lastMatch); Debug.Assert(lastMatch.Type == typeof(bool), "Skip token should generate boolean expression."); return Expression.Lambda(lastMatch, element); }
/// <summary>Creates new instance of node representing expanded navigation property.</summary> /// <param name="propertyName">The name of the property to project and expand.</param> /// <param name="property">The <see cref="ResourceProperty"/> for this property. Can only be null for the root node.</param> /// <param name="resourceSetWrapper">The resource set to which the expansion leads.</param> /// <param name="orderingInfo">The ordering info for this node. null means no ordering to be applied.</param> /// <param name="filter">The filter for this node. null means no filter to be applied.</param> /// <param name="skipCount">Number of results to skip. null means no results to be skipped.</param> /// <param name="takeCount">Maximum number of results to return. null means return all available results.</param> /// <param name="maxResultsExpected">Maximum number of expected results. Hint that the provider should return /// at least maxResultsExpected + 1 results (if available).</param> internal ExpandedProjectionNode( string propertyName, ResourceProperty property, ResourceSetWrapper resourceSetWrapper, OrderingInfo orderingInfo, Expression filter, int? skipCount, int? takeCount, int? maxResultsExpected) : base(propertyName, property) { Debug.Assert(resourceSetWrapper != null, "resourceSetWrapper != null"); Debug.Assert(property != null || propertyName.Length == 0, "We don't support open navigation properties."); this.resourceSetWrapper = resourceSetWrapper; this.orderingInfo = orderingInfo; this.filter = filter; this.skipCount = skipCount; this.takeCount = takeCount; this.maxResultsExpected = maxResultsExpected; this.nodes = new List<ProjectionNode>(); }
private void UpdateOrderingInfoWithSkipTokenWrapper(Type resultWrapperType) { OrderingInfo info = new OrderingInfo(true); ParameterExpression expression = Expression.Parameter(resultWrapperType, "w"); foreach (OrderingExpression expression2 in this.topLevelOrderingInfo.OrderingExpressions) { LambdaExpression expression3 = (LambdaExpression) expression2.Expression; Expression body = ParameterReplacerVisitor.Replace(expression3.Body, expression3.Parameters[0], Expression.MakeMemberAccess(expression, resultWrapperType.GetProperty("ExpandedElement"))); info.Add(new OrderingExpression(Expression.Lambda(body, new ParameterExpression[] { expression }), expression2.IsAscending)); } this.topLevelOrderingInfo = info; }
private void ObtainOrderingExpressions() { StringBuilder builder = new StringBuilder(this.service.OperationContext.Host.GetQueryStringItem("$orderby")); if (builder.Length > 0) { this.CheckSetQueryApplicable(); } ResourceType targetResourceType = this.description.TargetResourceType; this.topLevelOrderingInfo = new OrderingInfo(this.IsStandardPaged); if ((this.IsStandardPaged || this.topCount.HasValue) || this.skipCount.HasValue) { string str = (builder.Length > 0) ? "," : string.Empty; foreach (ResourceProperty property in this.description.TargetResourceSet.GetKeyPropertiesForOrderBy()) { builder.Append(str).Append(property.Name).Append(' ').Append("asc"); str = ","; } } string str2 = builder.ToString(); if (!string.IsNullOrEmpty(str2)) { ParameterExpression parameterForIt = Expression.Parameter(targetResourceType.InstanceType, "element"); this.orderingParser = new RequestQueryParser.ExpressionParser(this.service, this.description, parameterForIt, str2); foreach (OrderingExpression expression2 in this.orderingParser.ParseOrdering()) { this.topLevelOrderingInfo.Add(new OrderingExpression(Expression.Lambda(expression2.Expression, new ParameterExpression[] { parameterForIt }), expression2.IsAscending)); } if (this.IsStandardPaged) { this.description.SkipTokenExpressionCount = this.topLevelOrderingInfo.OrderingExpressions.Count; this.description.SkipTokenProperties = NeedSkipTokenVisitor.CollectSkipTokenProperties(this.topLevelOrderingInfo, targetResourceType); } } }
private ExpandSegmentCollection CheckSingleExpandPath(List<string> path) { ResourceType targetResourceType = this.description.TargetResourceType; ResourceSetWrapper targetContainer = this.description.LastSegmentInfo.TargetContainer; ExpandSegmentCollection segments = new ExpandSegmentCollection(path.Count); bool flag = false; bool previousSegmentIsTypeSegment = false; for (int i = 0; i < path.Count; i++) { string propertyName = path[i]; ResourcePropertyKind stream = ResourcePropertyKind.Stream; ResourceProperty navigationProperty = targetResourceType.TryResolvePropertyName(propertyName, stream); if (navigationProperty == null) { ResourceType type2 = WebUtil.ResolveTypeIdentifier(this.service.Provider, propertyName, targetResourceType, previousSegmentIsTypeSegment); if (type2 == null) { if (targetResourceType.IsOpenType) { throw DataServiceException.CreateBadRequestError(System.Data.Services.Strings.OpenNavigationPropertiesNotSupportedOnOpenTypes(propertyName)); } throw DataServiceException.CreateSyntaxError(System.Data.Services.Strings.RequestUriProcessor_PropertyNotFound(targetResourceType.FullName, propertyName)); } this.description.VerifyProtocolVersion(RequestDescription.Version3Dot0, this.service); targetResourceType = type2; previousSegmentIsTypeSegment = true; } else { previousSegmentIsTypeSegment = false; if (navigationProperty.TypeKind == ResourceTypeKind.EntityType) { targetContainer = this.service.Provider.GetContainer(targetContainer, targetResourceType, navigationProperty); if (targetContainer == null) { throw DataServiceException.CreateBadRequestError(System.Data.Services.Strings.BadRequest_InvalidPropertyNameSpecified(navigationProperty.Name, targetResourceType.FullName)); } bool singleResult = navigationProperty.Kind == ResourcePropertyKind.ResourceReference; DataServiceConfiguration.CheckResourceRightsForRead(targetContainer, singleResult); Expression filter = DataServiceConfiguration.ComposeQueryInterceptors(this.service, targetContainer); if (((targetContainer.PageSize != 0) && !singleResult) && !this.IsCustomPaged) { OrderingInfo orderingInfo = new OrderingInfo(true); ParameterExpression expression = Expression.Parameter(targetContainer.ResourceType.InstanceType, "p"); foreach (ResourceProperty property2 in targetContainer.GetKeyPropertiesForOrderBy()) { Expression expression3; if (property2.CanReflectOnInstanceTypeProperty) { expression3 = Expression.Property(expression, targetContainer.ResourceType.GetPropertyInfo(property2)); } else { expression3 = Expression.Convert(Expression.Call(null, DataServiceProviderMethods.GetValueMethodInfo, expression, Expression.Constant(property2)), property2.Type); } orderingInfo.Add(new OrderingExpression(Expression.Lambda(expression3, new ParameterExpression[] { expression }), true)); } segments.Add(new ExpandSegment(navigationProperty.Name, filter, targetContainer.PageSize, targetContainer, targetResourceType, navigationProperty, orderingInfo)); this.description.VerifyProtocolVersion(RequestDescription.Version2Dot0, this.service); this.description.VerifyAndRaiseResponseVersion(RequestDescription.Version2Dot0, this.service); } else { if (!singleResult && this.IsCustomPaged) { this.CheckAndApplyCustomPaging(null); } segments.Add(new ExpandSegment(navigationProperty.Name, filter, this.service.Configuration.MaxResultsPerCollection, targetContainer, targetResourceType, navigationProperty, null)); } this.description.UpdateAndCheckEpmFeatureVersion(targetContainer, this.service); this.description.UpdateVersions(this.service.OperationContext.Host.RequestAccept, targetContainer, this.service); flag = false; targetResourceType = navigationProperty.ResourceType; } else { flag = true; } } } if (previousSegmentIsTypeSegment) { throw DataServiceException.CreateBadRequestError(System.Data.Services.Strings.RequestQueryProcessor_QueryParametersPathCannotEndInTypeIdentifier("$expand", targetResourceType.FullName)); } if (!flag) { return segments; } return null; }
/// <summary>Initializes a new <see cref="ExpandSegment"/> instance.</summary> /// <param name="name">Segment name.</param> /// <param name="filter">Filter expression for segment, possibly null.</param> /// <param name="maxResultsExpected"> /// Expand providers may choose to return at most MaxResultsExpected + 1 elements to allow the /// data service to detect a failure to meet this constraint. /// </param> /// <param name="container">Container to which the segment belongs; possibly null.</param> /// <param name="expandedProperty">Property expanded by this expand segment</param> /// <param name="orderingInfo">Collection of ordering information for this segment, used for paging</param> internal ExpandSegment( string name, Expression filter, int maxResultsExpected, ResourceSetWrapper container, ResourceProperty expandedProperty, OrderingInfo orderingInfo) { WebUtil.CheckArgumentNull(name, "name"); CheckFilterType(filter); this.name = name; this.filter = filter; this.container = container; this.maxResultsExpected = maxResultsExpected; this.expandedProperty = expandedProperty; this.orderingInfo = orderingInfo; }
internal RootProjectionNode(ResourceSetWrapper resourceSetWrapper, OrderingInfo orderingInfo, Expression filter, int?skipCount, int?takeCount, int?maxResultsExpected, List <ExpandSegmentCollection> expandPaths, System.Data.Services.Providers.ResourceType baseResourceType) : base(string.Empty, null, null, resourceSetWrapper, orderingInfo, filter, skipCount, takeCount, maxResultsExpected) { this.expandPaths = expandPaths; this.baseResourceType = baseResourceType; }