/// <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>Sorts a query like a SQL ORDER BY clause does.</summary> /// <param name="source">Original source for query.</param> /// <param name="orderingInfo">Ordering definition to compose.</param> /// <returns>The composed query.</returns> internal static Expression OrderBy(Expression source, OrderingInfo orderingInfo) { Debug.Assert(source != null, "source != null"); Debug.Assert(orderingInfo != null, "orderingInfo != null"); Expression queryExpr = source; bool useOrderBy = true; foreach (OrderingExpression o in orderingInfo.OrderingExpressions) { LambdaExpression selectorLambda = (LambdaExpression)o.Expression; Type selectorType = selectorLambda.Body.Type; Debug.Assert(selectorType != null, "type != null"); // ensure either the expression type is orderable (ie, primitive) or its an open expression. if (!WebUtil.IsPrimitiveType(selectorType) && !OpenTypeMethods.IsOpenExpression(selectorLambda.Body)) { throw DataServiceException.CreateBadRequestError(Strings.RequestQueryParser_OrderByDoesNotSupportType(WebUtil.GetTypeName(selectorType))); } if (useOrderBy) { queryExpr = o.IsAscending ? queryExpr.QueryableOrderBy(selectorLambda) : queryExpr.QueryableOrderByDescending(selectorLambda); } else { queryExpr = o.IsAscending ? queryExpr.QueryableThenBy(selectorLambda) : queryExpr.QueryableThenByDescending(selectorLambda); } useOrderBy = false; } return(queryExpr); }
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; }
/// <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 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); }
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); }
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>Makes the expression that is used as a filter corresponding to skip token.</summary> /// <param name="topLevelOrderingInfo">Ordering expression.</param> /// <param name="skipToken">The provided skip token.</param> /// <param name="parameterType">The parameter type of the lambda.</param> /// <returns>LambdaExpression corresponding to the skip token filter.</returns> internal LambdaExpression BuildSkipTokenFilter(OrderingInfo topLevelOrderingInfo, IList <object> skipToken, Type parameterType) { ParameterExpression element = Expression.Parameter(parameterType, "element"); Expression lastCondition = Expression.Constant(true, typeof(bool)); Expression lastMatch = Expression.Constant(false, typeof(bool)); foreach (var v in WebUtil.Zip(topLevelOrderingInfo.OrderingExpressions, skipToken, (x, y) => new { Order = x, Value = y })) { BinaryOperatorKind comparisonExp = v.Order.IsAscending ? BinaryOperatorKind.GreaterThan : BinaryOperatorKind.LessThan; Expression fixedLambda = ParameterReplacerVisitor.Replace( ((LambdaExpression)v.Order.Expression).Body, ((LambdaExpression)v.Order.Expression).Parameters[0], element); // TODO: this will be an EnumNode if $skiptoken contains enum constant. ConstantNode node; var lexer = new ExpressionLexer((string)v.Value); bool success = TokenToQueryNodeTranslator.TryCreateLiteral(lexer.CurrentToken, out node); Debug.Assert(success, "Was not a literal"); node = this.EnsureCorrectTypeAndPrecisionForLFDM(node, fixedLambda.Type); Expression right = this.nodeToExpressionTranslator.TranslateNode(node); Expression comparison = ExpressionGenerator.GenerateLogicalAnd( lastCondition, this.GenerateNullAwareComparison(fixedLambda, right, comparisonExp)); lastMatch = ExpressionGenerator.GenerateLogicalOr(lastMatch, comparison); lastCondition = ExpressionGenerator.GenerateLogicalAnd( lastCondition, this.GenerateComparisonExpression(fixedLambda, right, BinaryOperatorKind.Equal)); } lastMatch = ExpressionUtils.EnsurePredicateExpressionIsBoolean(lastMatch); Debug.Assert(lastMatch.Type == typeof(bool), "Skip token should generate boolean expression."); return(Expression.Lambda(lastMatch, element)); }
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); }