Code:
/ Dotnetfx_Vista_SP2 / Dotnetfx_Vista_SP2 / 8.0.50727.4016 / DEVDIV / depot / DevDiv / releases / Orcas / QFE / ndp / fx / src / DataWeb / Server / System / Data / Services / RequestQueryProcessor.cs / 1 / RequestQueryProcessor.cs
//---------------------------------------------------------------------- //// Copyright (c) Microsoft Corporation. All rights reserved. // //// Provides a class capable of processing query arguments. // // // @owner [....] //--------------------------------------------------------------------- namespace System.Data.Services { using System; using System.Collections; using System.Collections.Generic; using System.Data.Services.Client; using System.Data.Services.Parsing; using System.Data.Services.Providers; using System.Diagnostics; using System.Globalization; using System.Linq; using System.Linq.Expressions; using System.Reflection; ///Use this class to process a web data service request URI. internal struct RequestQueryProcessor { #region Private fields. ///Original description over which query composition takes place. private readonly RequestDescription description; ///Whether the $filter query option can be applied to the request. private readonly bool filterQueryApplicable; ///Service with data and configuration. private readonly IDataService service; ///Whether the $orderby, $skip, $take options can be applied to the request. private readonly bool setQueryApplicable; ///List of paths to be expanded. private ListexpandPaths; /// List of paths to be expanded, before resolving the identifiers private List> expandPathsAsText; ///
Whether any order has been applied. private bool orderApplied; ///Query being composed. private IQueryable query; ///Query results to be returned. private IEnumerable queryResults; #endregion Private fields. ///Initializes a new /// Service with data and configuration. /// Description for request processed so far. private RequestQueryProcessor(IDataService service, RequestDescription description) { this.service = service; this.description = description; this.orderApplied = false; this.query = (IQueryable)description.RequestEnumerable; this.filterQueryApplicable = description.TargetKind == RequestTargetKind.Resource || #if ASTORIA_OPEN_OBJECT description.TargetKind == RequestTargetKind.OpenProperty || #endif description.TargetKind == RequestTargetKind.ComplexObject; this.setQueryApplicable = ( #if ASTORIA_OPEN_OBJECT description.TargetKind == RequestTargetKind.OpenProperty || #endif description.TargetKind == RequestTargetKind.Resource) && !description.IsSingleResult; this.expandPaths = null; this.expandPathsAsText = null; this.queryResults = null; } ///instance. Checks that no query arguments were sent in the request. /// Service to check. ////// Regular processing checks argument applicability, but for /// service operations that return an IEnumerable this is part /// of the contract on service operations, rather than a structural /// check on the request. /// internal static void CheckEmptyQueryArguments(IDataService service) { Debug.Assert(service != null, "service != null"); IDataServiceHost host = service.Host; if (!String.IsNullOrEmpty(host.GetQueryStringItem(XmlConstants.HttpQueryStringExpand)) || !String.IsNullOrEmpty(host.GetQueryStringItem(XmlConstants.HttpQueryStringFilter)) || !String.IsNullOrEmpty(host.GetQueryStringItem(XmlConstants.HttpQueryStringOrderBy)) || !String.IsNullOrEmpty(host.GetQueryStringItem(XmlConstants.HttpQueryStringSkip)) || !String.IsNullOrEmpty(host.GetQueryStringItem(XmlConstants.HttpQueryStringTop))) { // 400: Bad Request throw DataServiceException.CreateBadRequestError(Strings.RequestQueryProcessor_QueryNoOptionsApplicable); } } ////// Composes a property navigation with the appropriate filter lamba, as appropriate. /// /// Member access expression to compose. /// Lambda expression used for the filter. /// Whether null propagation is required on the. /// The composed expression. internal static Expression ComposePropertyNavigation( Expression expression, LambdaExpression filterLambda, bool propagateNull) { Debug.Assert(expression != null, "expression != null"); Debug.Assert(filterLambda != null, "filterLambda != null"); Expression fixedFilter = ParameterReplacerVisitor.Replace( filterLambda.Body, filterLambda.Parameters[0], expression); Expression test; if (propagateNull) { Expression nullConstant = System.Data.Services.Parsing.RequestQueryParser.NullLiteral; test = Expression.AndAlso(Expression.NotEqual(expression, nullConstant), fixedFilter); } else { test = fixedFilter; } Expression conditionTrue = expression; Expression conditionFalse = Expression.Constant(null, conditionTrue.Type); return Expression.Condition(test, conditionTrue, conditionFalse); } ////// Processes query arguments and returns a request description for /// the resulting query. /// /// Service with data and configuration information. /// Description for request processed so far. ///A new internal static RequestDescription ProcessQuery(IDataService service, RequestDescription description) { Debug.Assert(service != null, "service != null"); // When the request doesn't produce an IQueryable result, // we can short-circuit all further processing. if (!(description.RequestEnumerable is IQueryable)) { RequestQueryProcessor.CheckEmptyQueryArguments(service); return description; } else { return new RequestQueryProcessor(service, description).ProcessQuery(); } } ///. Generic method to invoke an OrderBy or OrderByDescending method on an IQueryable source. ///Element type of the source. ///Result type of the key. /// Source query. /// Lambda expression that turns TSource into TKey. /// Whether the ordering should be ascending or not. /// Whether this is the first-level order; false if this is an inner-level order-by. ///A new query that sorts TSource by TKey. private static IQueryable InvokeOrderBy(IQueryable source, LambdaExpression keySelector, bool ascending, bool firstLevelOrder) { Debug.Assert(source != null, "source != null"); Debug.Assert(keySelector != null, "keySelector != null"); Expression > typedKeySelector = (Expression >)keySelector; if (firstLevelOrder) { IQueryable typedSource = (IQueryable )source; if (ascending) { return Queryable.OrderBy (typedSource, typedKeySelector); } else { return Queryable.OrderByDescending (typedSource, typedKeySelector); } } else { IOrderedQueryable typedSource = (IOrderedQueryable )source; if (ascending) { return Queryable.ThenBy (typedSource, typedKeySelector); } else { return Queryable.ThenByDescending (typedSource, typedKeySelector); } } } /// Generic method to invoke a Skip method on an IQueryable source. ///Element type of the source. /// Source query. /// Element count to skip. ///A new query that skips private static IQueryable InvokeSkipelements of . (IQueryable source, int count) { Debug.Assert(source != null, "source != null"); IQueryable typedSource = (IQueryable )source; return Queryable.Skip (typedSource, count); } /// Generic method to invoke a Take method on an IQueryable source. ///Element type of the source. /// Source query. /// Element count to take. ///A new query that takes private static IQueryable InvokeTakeelements of . (IQueryable source, int count) { Debug.Assert(source != null, "source != null"); IQueryable typedSource = (IQueryable )source; return Queryable.Take (typedSource, count); } /// Composes an OrderBy operation on the specified query. /// Query to compose over. /// Property to order by. /// Whether the ordering should be ascending or descending. /// Whether this is the first-level order; false if this is an inner-level order-by. ///A query that orders the specified query. private static IQueryable OrderBy(IQueryable query, PropertyInfo property, bool ascending, bool firstLevelOrder) { Debug.Assert(query != null, "query != null"); Debug.Assert(property != null, "property != null"); ParameterExpression parameter = Expression.Parameter(query.ElementType, "element"); MemberExpression body = Expression.Property(parameter, property); LambdaExpression selector = Expression.Lambda(body, parameter); MethodInfo method = typeof(RequestQueryProcessor).GetMethod("InvokeOrderBy", BindingFlags.Static | BindingFlags.NonPublic); method = method.MakeGenericMethod(query.ElementType, body.Type); return (IQueryable)method.Invoke(null, new object[] { query, selector, ascending, firstLevelOrder }); } ///Reads an $expand clause. /// Value to read. ///A list of paths, each of which is a list of identifiers. private static List> ReadExpand(string expand) { Debug.Assert(!String.IsNullOrEmpty(expand), "!String.IsNullOrEmpty(expand)"); List
> result = new List
>(); List
currentPath = null; ExpressionLexer lexer = new ExpressionLexer(expand); while (lexer.CurrentToken.Id != TokenId.End) { string identifier = lexer.ReadDottedIdentifier(); if (currentPath == null) { currentPath = new List (); result.Add(currentPath); } currentPath.Add(identifier); // Check whether we're at the end, whether we're drilling in, // or whether we're finishing with this path. TokenId tokenId = lexer.CurrentToken.Id; if (tokenId != TokenId.End) { if (tokenId == TokenId.Comma) { currentPath = null; } else { lexer.ValidateToken(TokenId.Slash); } lexer.NextToken(); } } return result; } /// Composes a Skip operation on the specified query. /// Query to compose over. /// Number of elements to skip. ///A query that skips the specified number of elements. private static IQueryable Skip(IQueryable query, int count) { Debug.Assert(query != null, "query != null"); MethodInfo method = typeof(RequestQueryProcessor).GetMethod("InvokeSkip", BindingFlags.Static | BindingFlags.NonPublic); method = method.MakeGenericMethod(query.ElementType); return (IQueryable)method.Invoke(null, new object[] { query, count }); } ///Composes a Take operation on the specified query. /// Query to compose over. /// Number of elements to take. ///A query that takes up to a specific number of elements. private static IQueryable Take(IQueryable query, int count) { Debug.Assert(query != null, "query != null"); MethodInfo method = typeof(RequestQueryProcessor).GetMethod("InvokeTake", BindingFlags.Static | BindingFlags.NonPublic); method = method.MakeGenericMethod(query.ElementType); return (IQueryable)method.Invoke(null, new object[] { query, count }); } ///Applies a default order, if possible. private void ApplyDefaultOrder() { // Check that default ordering should be applied. if (this.description.TargetKind == RequestTargetKind.Resource && (this.description.TargetSource == RequestTargetSource.EntitySet || this.description.TargetSource == RequestTargetSource.Property)) { ResourceType resourceType = this.service.Provider.GetResourceType(this.description.TargetElementType); Debug.Assert(resourceType != null, "resourceType != null"); bool firstLevelOrder = true; foreach (ResourceProperty keyProperty in resourceType.KeyProperties) { this.query = OrderBy(this.query, keyProperty.PropertyInfo, true /* ascending */, firstLevelOrder); firstLevelOrder = false; } } this.orderApplied = true; } ///Checks and resolved all textual expand paths and removes unnecessary paths. private void CheckExpandPaths() { Debug.Assert(this.expandPathsAsText != null, "this.expandPaths != null"); if (this.expandPathsAsText.Count > 0 && this.query == null) { throw DataServiceException.CreateBadRequestError(Strings.RequestQueryProcessor_QueryExpandOptionNotApplicable); } this.expandPaths = new List(this.expandPathsAsText.Count); for (int i = this.expandPathsAsText.Count - 1; i >= 0; i--) { Type type = this.query.ElementType; ResourceType resourceType = this.service.Provider.GetResourceType(type); List path = this.expandPathsAsText[i]; ExpandSegmentCollection segments = new ExpandSegmentCollection(path.Count); bool ignorePath = false; for (int j = 0; j < path.Count; j++) { string pathSegment = path[j]; #if ASTORIA_OPEN_OBJECT if (resourceType.IsOpenType) { ignorePath = false; segments.Add(new ExpandSegment(pathSegment, null)); continue; } #endif ResourceProperty property = resourceType.TryResolvePropertyName(pathSegment); if (property == null) { throw DataServiceException.CreateSyntaxError(Strings.RequestUriProcessor_PropertyNotFound(resourceType.FullName, pathSegment)); } Expression filter = null; if (property.ResourceContainer != null) { bool singleResult = property.Kind == ResourcePropertyKind.ResourceReference; this.service.Configuration.CheckResourceRightsForRead(property.ResourceContainer, singleResult); filter = DataServiceConfiguration.ComposeQueryInterceptors(this.service, property.ResourceContainer); } int expected = this.service.Configuration.MaxResultsPerCollection; segments.Add(new ExpandSegment(property.Name, filter, expected, property.ResourceContainer)); resourceType = property.ResourceType; ignorePath = ignorePath || property.TypeKind != ResourceTypeKind.EntityType; } // Even though the path might be correct, we might need to // ignore because it's a primitive. if (ignorePath) { this.expandPathsAsText.RemoveAt(i); } else { this.expandPaths.Add(segments); } } } /// Checks that all resolved expand paths are valid. private void CheckExpandUpdatedPaths() { Debug.Assert(this.expandPaths != null, "this.expandPaths != null"); for (int i = this.expandPaths.Count - 1; i >= 0; i--) { Type type = this.query.ElementType; ResourceType resourceType = this.service.Provider.GetResourceType(type); ExpandSegmentCollection path = this.expandPaths[i]; for (int j = 0; j < path.Count; j++) { string pathSegment = path[j].Name; #if ASTORIA_OPEN_OBJECT if (resourceType.IsOpenType) { break; } #endif ResourceProperty property = resourceType.TryResolvePropertyName(pathSegment); if (property == null) { throw DataServiceException.CreateSyntaxError(Strings.RequestUriProcessor_PropertyNotFound(resourceType.FullName, pathSegment)); } resourceType = property.ResourceType; } // No need to strip primitive types for object context; the framework will throw, but the user code // should know not to add them. } } ///Checks that the query option is applicable to this request. private void CheckFilterQueryApplicable() { if (!this.filterQueryApplicable) { throw DataServiceException.CreateBadRequestError(Strings.RequestQueryProcessor_QueryFilterOptionNotApplicable); } } ///Checks that set query options are applicable to this request. private void CheckSetQueryApplicable() { if (!this.setQueryApplicable) { throw DataServiceException.CreateBadRequestError(Strings.RequestQueryProcessor_QuerySetOptionsNotApplicable); } } ///Processes the $expand argument of the request. private void ProcessExpand() { string expand = this.service.Host.GetQueryStringItem(XmlConstants.HttpQueryStringExpand); if (!String.IsNullOrEmpty(expand)) { this.expandPathsAsText = ReadExpand(expand); } else { this.expandPathsAsText = new List>(); } this.CheckExpandPaths(); this.service.InternalApplyingExpansions(this.query, this.expandPaths); this.CheckExpandUpdatedPaths(); if (this.expandPaths.Count > 0) { this.queryResults = ((IExpandProvider)this.service.Provider).ApplyExpansions(this.query, this.expandPaths); } else { this.queryResults = this.query; } Debug.Assert(this.queryResults != null, "this.queryResults != null"); } ///
Processes the $filter argument of the request. private void ProcessFilter() { string filter = this.service.Host.GetQueryStringItem(XmlConstants.HttpQueryStringFilter); if (!String.IsNullOrEmpty(filter)) { this.CheckFilterQueryApplicable(); this.query = RequestQueryParser.Where(this.service, this.query, filter); } } ///Processes the $orderby argument of the request. private void ProcessOrderBy() { string orderBy = this.service.Host.GetQueryStringItem(XmlConstants.HttpQueryStringOrderBy); if (!String.IsNullOrEmpty(orderBy)) { this.CheckSetQueryApplicable(); IQueryable orderedQuery = RequestQueryParser.OrderBy(this.service, this.query, orderBy); if (orderedQuery != this.query) { this.query = orderedQuery; this.orderApplied = true; } } } ////// Processes query arguments and returns a request description for /// the resulting query. /// ///A modified private RequestDescription ProcessQuery() { // NOTE: The order set by ProcessOrderBy may be reset by other operations other than skip/top, // so filtering needs to occur first. this.ProcessFilter(); this.ProcessOrderBy(); this.ProcessSkip(); this.ProcessTop(); // NOTE: expand goes last, as it may be reset by other operations. this.ProcessExpand(); Debug.Assert(this.queryResults != null, "this.queryResults != null -- otherwise ProcessExpand didn't set it"); return new RequestDescription(this.description, this.queryResults, this.expandPaths); } ///that includes query information. Processes the $skip argument of the request. private void ProcessSkip() { int count; if (this.ProcessSkipOrTopArgument(XmlConstants.HttpQueryStringSkip, out count)) { this.CheckSetQueryApplicable(); this.query = Skip(this.query, count); } } ////// Checks whether the specified argument should be processed and what /// its value is. /// /// Name of the query item, $top or $skip. /// The value for the query item. ///true if the argument should be processed; false otherwise. private bool ProcessSkipOrTopArgument(string queryItem, out int count) { Debug.Assert(queryItem != null, "queryItem != null"); string itemText = this.service.Host.GetQueryStringItem(queryItem); if (String.IsNullOrEmpty(itemText)) { count = 0; return false; } if (!this.orderApplied) { this.ApplyDefaultOrder(); } if (!Int32.TryParse(itemText, NumberStyles.Integer, CultureInfo.InvariantCulture, out count)) { throw DataServiceException.CreateSyntaxError( Strings.RequestQueryProcessor_IncorrectArgumentFormat(queryItem, itemText)); } return true; } ///Processes the $top argument of the request. private void ProcessTop() { int count; if (this.ProcessSkipOrTopArgument(XmlConstants.HttpQueryStringTop, out count)) { this.CheckSetQueryApplicable(); this.query = Take(this.query, count); } } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. //---------------------------------------------------------------------- //// Copyright (c) Microsoft Corporation. All rights reserved. // //// Provides a class capable of processing query arguments. // // // @owner [....] //--------------------------------------------------------------------- namespace System.Data.Services { using System; using System.Collections; using System.Collections.Generic; using System.Data.Services.Client; using System.Data.Services.Parsing; using System.Data.Services.Providers; using System.Diagnostics; using System.Globalization; using System.Linq; using System.Linq.Expressions; using System.Reflection; ///Use this class to process a web data service request URI. internal struct RequestQueryProcessor { #region Private fields. ///Original description over which query composition takes place. private readonly RequestDescription description; ///Whether the $filter query option can be applied to the request. private readonly bool filterQueryApplicable; ///Service with data and configuration. private readonly IDataService service; ///Whether the $orderby, $skip, $take options can be applied to the request. private readonly bool setQueryApplicable; ///List of paths to be expanded. private ListexpandPaths; /// List of paths to be expanded, before resolving the identifiers private List> expandPathsAsText; ///
Whether any order has been applied. private bool orderApplied; ///Query being composed. private IQueryable query; ///Query results to be returned. private IEnumerable queryResults; #endregion Private fields. ///Initializes a new /// Service with data and configuration. /// Description for request processed so far. private RequestQueryProcessor(IDataService service, RequestDescription description) { this.service = service; this.description = description; this.orderApplied = false; this.query = (IQueryable)description.RequestEnumerable; this.filterQueryApplicable = description.TargetKind == RequestTargetKind.Resource || #if ASTORIA_OPEN_OBJECT description.TargetKind == RequestTargetKind.OpenProperty || #endif description.TargetKind == RequestTargetKind.ComplexObject; this.setQueryApplicable = ( #if ASTORIA_OPEN_OBJECT description.TargetKind == RequestTargetKind.OpenProperty || #endif description.TargetKind == RequestTargetKind.Resource) && !description.IsSingleResult; this.expandPaths = null; this.expandPathsAsText = null; this.queryResults = null; } ///instance. Checks that no query arguments were sent in the request. /// Service to check. ////// Regular processing checks argument applicability, but for /// service operations that return an IEnumerable this is part /// of the contract on service operations, rather than a structural /// check on the request. /// internal static void CheckEmptyQueryArguments(IDataService service) { Debug.Assert(service != null, "service != null"); IDataServiceHost host = service.Host; if (!String.IsNullOrEmpty(host.GetQueryStringItem(XmlConstants.HttpQueryStringExpand)) || !String.IsNullOrEmpty(host.GetQueryStringItem(XmlConstants.HttpQueryStringFilter)) || !String.IsNullOrEmpty(host.GetQueryStringItem(XmlConstants.HttpQueryStringOrderBy)) || !String.IsNullOrEmpty(host.GetQueryStringItem(XmlConstants.HttpQueryStringSkip)) || !String.IsNullOrEmpty(host.GetQueryStringItem(XmlConstants.HttpQueryStringTop))) { // 400: Bad Request throw DataServiceException.CreateBadRequestError(Strings.RequestQueryProcessor_QueryNoOptionsApplicable); } } ////// Composes a property navigation with the appropriate filter lamba, as appropriate. /// /// Member access expression to compose. /// Lambda expression used for the filter. /// Whether null propagation is required on the. /// The composed expression. internal static Expression ComposePropertyNavigation( Expression expression, LambdaExpression filterLambda, bool propagateNull) { Debug.Assert(expression != null, "expression != null"); Debug.Assert(filterLambda != null, "filterLambda != null"); Expression fixedFilter = ParameterReplacerVisitor.Replace( filterLambda.Body, filterLambda.Parameters[0], expression); Expression test; if (propagateNull) { Expression nullConstant = System.Data.Services.Parsing.RequestQueryParser.NullLiteral; test = Expression.AndAlso(Expression.NotEqual(expression, nullConstant), fixedFilter); } else { test = fixedFilter; } Expression conditionTrue = expression; Expression conditionFalse = Expression.Constant(null, conditionTrue.Type); return Expression.Condition(test, conditionTrue, conditionFalse); } ////// Processes query arguments and returns a request description for /// the resulting query. /// /// Service with data and configuration information. /// Description for request processed so far. ///A new internal static RequestDescription ProcessQuery(IDataService service, RequestDescription description) { Debug.Assert(service != null, "service != null"); // When the request doesn't produce an IQueryable result, // we can short-circuit all further processing. if (!(description.RequestEnumerable is IQueryable)) { RequestQueryProcessor.CheckEmptyQueryArguments(service); return description; } else { return new RequestQueryProcessor(service, description).ProcessQuery(); } } ///. Generic method to invoke an OrderBy or OrderByDescending method on an IQueryable source. ///Element type of the source. ///Result type of the key. /// Source query. /// Lambda expression that turns TSource into TKey. /// Whether the ordering should be ascending or not. /// Whether this is the first-level order; false if this is an inner-level order-by. ///A new query that sorts TSource by TKey. private static IQueryable InvokeOrderBy(IQueryable source, LambdaExpression keySelector, bool ascending, bool firstLevelOrder) { Debug.Assert(source != null, "source != null"); Debug.Assert(keySelector != null, "keySelector != null"); Expression > typedKeySelector = (Expression >)keySelector; if (firstLevelOrder) { IQueryable typedSource = (IQueryable )source; if (ascending) { return Queryable.OrderBy (typedSource, typedKeySelector); } else { return Queryable.OrderByDescending (typedSource, typedKeySelector); } } else { IOrderedQueryable typedSource = (IOrderedQueryable )source; if (ascending) { return Queryable.ThenBy (typedSource, typedKeySelector); } else { return Queryable.ThenByDescending (typedSource, typedKeySelector); } } } /// Generic method to invoke a Skip method on an IQueryable source. ///Element type of the source. /// Source query. /// Element count to skip. ///A new query that skips private static IQueryable InvokeSkipelements of . (IQueryable source, int count) { Debug.Assert(source != null, "source != null"); IQueryable typedSource = (IQueryable )source; return Queryable.Skip (typedSource, count); } /// Generic method to invoke a Take method on an IQueryable source. ///Element type of the source. /// Source query. /// Element count to take. ///A new query that takes private static IQueryable InvokeTakeelements of . (IQueryable source, int count) { Debug.Assert(source != null, "source != null"); IQueryable typedSource = (IQueryable )source; return Queryable.Take (typedSource, count); } /// Composes an OrderBy operation on the specified query. /// Query to compose over. /// Property to order by. /// Whether the ordering should be ascending or descending. /// Whether this is the first-level order; false if this is an inner-level order-by. ///A query that orders the specified query. private static IQueryable OrderBy(IQueryable query, PropertyInfo property, bool ascending, bool firstLevelOrder) { Debug.Assert(query != null, "query != null"); Debug.Assert(property != null, "property != null"); ParameterExpression parameter = Expression.Parameter(query.ElementType, "element"); MemberExpression body = Expression.Property(parameter, property); LambdaExpression selector = Expression.Lambda(body, parameter); MethodInfo method = typeof(RequestQueryProcessor).GetMethod("InvokeOrderBy", BindingFlags.Static | BindingFlags.NonPublic); method = method.MakeGenericMethod(query.ElementType, body.Type); return (IQueryable)method.Invoke(null, new object[] { query, selector, ascending, firstLevelOrder }); } ///Reads an $expand clause. /// Value to read. ///A list of paths, each of which is a list of identifiers. private static List> ReadExpand(string expand) { Debug.Assert(!String.IsNullOrEmpty(expand), "!String.IsNullOrEmpty(expand)"); List
> result = new List
>(); List
currentPath = null; ExpressionLexer lexer = new ExpressionLexer(expand); while (lexer.CurrentToken.Id != TokenId.End) { string identifier = lexer.ReadDottedIdentifier(); if (currentPath == null) { currentPath = new List (); result.Add(currentPath); } currentPath.Add(identifier); // Check whether we're at the end, whether we're drilling in, // or whether we're finishing with this path. TokenId tokenId = lexer.CurrentToken.Id; if (tokenId != TokenId.End) { if (tokenId == TokenId.Comma) { currentPath = null; } else { lexer.ValidateToken(TokenId.Slash); } lexer.NextToken(); } } return result; } /// Composes a Skip operation on the specified query. /// Query to compose over. /// Number of elements to skip. ///A query that skips the specified number of elements. private static IQueryable Skip(IQueryable query, int count) { Debug.Assert(query != null, "query != null"); MethodInfo method = typeof(RequestQueryProcessor).GetMethod("InvokeSkip", BindingFlags.Static | BindingFlags.NonPublic); method = method.MakeGenericMethod(query.ElementType); return (IQueryable)method.Invoke(null, new object[] { query, count }); } ///Composes a Take operation on the specified query. /// Query to compose over. /// Number of elements to take. ///A query that takes up to a specific number of elements. private static IQueryable Take(IQueryable query, int count) { Debug.Assert(query != null, "query != null"); MethodInfo method = typeof(RequestQueryProcessor).GetMethod("InvokeTake", BindingFlags.Static | BindingFlags.NonPublic); method = method.MakeGenericMethod(query.ElementType); return (IQueryable)method.Invoke(null, new object[] { query, count }); } ///Applies a default order, if possible. private void ApplyDefaultOrder() { // Check that default ordering should be applied. if (this.description.TargetKind == RequestTargetKind.Resource && (this.description.TargetSource == RequestTargetSource.EntitySet || this.description.TargetSource == RequestTargetSource.Property)) { ResourceType resourceType = this.service.Provider.GetResourceType(this.description.TargetElementType); Debug.Assert(resourceType != null, "resourceType != null"); bool firstLevelOrder = true; foreach (ResourceProperty keyProperty in resourceType.KeyProperties) { this.query = OrderBy(this.query, keyProperty.PropertyInfo, true /* ascending */, firstLevelOrder); firstLevelOrder = false; } } this.orderApplied = true; } ///Checks and resolved all textual expand paths and removes unnecessary paths. private void CheckExpandPaths() { Debug.Assert(this.expandPathsAsText != null, "this.expandPaths != null"); if (this.expandPathsAsText.Count > 0 && this.query == null) { throw DataServiceException.CreateBadRequestError(Strings.RequestQueryProcessor_QueryExpandOptionNotApplicable); } this.expandPaths = new List(this.expandPathsAsText.Count); for (int i = this.expandPathsAsText.Count - 1; i >= 0; i--) { Type type = this.query.ElementType; ResourceType resourceType = this.service.Provider.GetResourceType(type); List path = this.expandPathsAsText[i]; ExpandSegmentCollection segments = new ExpandSegmentCollection(path.Count); bool ignorePath = false; for (int j = 0; j < path.Count; j++) { string pathSegment = path[j]; #if ASTORIA_OPEN_OBJECT if (resourceType.IsOpenType) { ignorePath = false; segments.Add(new ExpandSegment(pathSegment, null)); continue; } #endif ResourceProperty property = resourceType.TryResolvePropertyName(pathSegment); if (property == null) { throw DataServiceException.CreateSyntaxError(Strings.RequestUriProcessor_PropertyNotFound(resourceType.FullName, pathSegment)); } Expression filter = null; if (property.ResourceContainer != null) { bool singleResult = property.Kind == ResourcePropertyKind.ResourceReference; this.service.Configuration.CheckResourceRightsForRead(property.ResourceContainer, singleResult); filter = DataServiceConfiguration.ComposeQueryInterceptors(this.service, property.ResourceContainer); } int expected = this.service.Configuration.MaxResultsPerCollection; segments.Add(new ExpandSegment(property.Name, filter, expected, property.ResourceContainer)); resourceType = property.ResourceType; ignorePath = ignorePath || property.TypeKind != ResourceTypeKind.EntityType; } // Even though the path might be correct, we might need to // ignore because it's a primitive. if (ignorePath) { this.expandPathsAsText.RemoveAt(i); } else { this.expandPaths.Add(segments); } } } /// Checks that all resolved expand paths are valid. private void CheckExpandUpdatedPaths() { Debug.Assert(this.expandPaths != null, "this.expandPaths != null"); for (int i = this.expandPaths.Count - 1; i >= 0; i--) { Type type = this.query.ElementType; ResourceType resourceType = this.service.Provider.GetResourceType(type); ExpandSegmentCollection path = this.expandPaths[i]; for (int j = 0; j < path.Count; j++) { string pathSegment = path[j].Name; #if ASTORIA_OPEN_OBJECT if (resourceType.IsOpenType) { break; } #endif ResourceProperty property = resourceType.TryResolvePropertyName(pathSegment); if (property == null) { throw DataServiceException.CreateSyntaxError(Strings.RequestUriProcessor_PropertyNotFound(resourceType.FullName, pathSegment)); } resourceType = property.ResourceType; } // No need to strip primitive types for object context; the framework will throw, but the user code // should know not to add them. } } ///Checks that the query option is applicable to this request. private void CheckFilterQueryApplicable() { if (!this.filterQueryApplicable) { throw DataServiceException.CreateBadRequestError(Strings.RequestQueryProcessor_QueryFilterOptionNotApplicable); } } ///Checks that set query options are applicable to this request. private void CheckSetQueryApplicable() { if (!this.setQueryApplicable) { throw DataServiceException.CreateBadRequestError(Strings.RequestQueryProcessor_QuerySetOptionsNotApplicable); } } ///Processes the $expand argument of the request. private void ProcessExpand() { string expand = this.service.Host.GetQueryStringItem(XmlConstants.HttpQueryStringExpand); if (!String.IsNullOrEmpty(expand)) { this.expandPathsAsText = ReadExpand(expand); } else { this.expandPathsAsText = new List>(); } this.CheckExpandPaths(); this.service.InternalApplyingExpansions(this.query, this.expandPaths); this.CheckExpandUpdatedPaths(); if (this.expandPaths.Count > 0) { this.queryResults = ((IExpandProvider)this.service.Provider).ApplyExpansions(this.query, this.expandPaths); } else { this.queryResults = this.query; } Debug.Assert(this.queryResults != null, "this.queryResults != null"); } ///
Processes the $filter argument of the request. private void ProcessFilter() { string filter = this.service.Host.GetQueryStringItem(XmlConstants.HttpQueryStringFilter); if (!String.IsNullOrEmpty(filter)) { this.CheckFilterQueryApplicable(); this.query = RequestQueryParser.Where(this.service, this.query, filter); } } ///Processes the $orderby argument of the request. private void ProcessOrderBy() { string orderBy = this.service.Host.GetQueryStringItem(XmlConstants.HttpQueryStringOrderBy); if (!String.IsNullOrEmpty(orderBy)) { this.CheckSetQueryApplicable(); IQueryable orderedQuery = RequestQueryParser.OrderBy(this.service, this.query, orderBy); if (orderedQuery != this.query) { this.query = orderedQuery; this.orderApplied = true; } } } ////// Processes query arguments and returns a request description for /// the resulting query. /// ///A modified private RequestDescription ProcessQuery() { // NOTE: The order set by ProcessOrderBy may be reset by other operations other than skip/top, // so filtering needs to occur first. this.ProcessFilter(); this.ProcessOrderBy(); this.ProcessSkip(); this.ProcessTop(); // NOTE: expand goes last, as it may be reset by other operations. this.ProcessExpand(); Debug.Assert(this.queryResults != null, "this.queryResults != null -- otherwise ProcessExpand didn't set it"); return new RequestDescription(this.description, this.queryResults, this.expandPaths); } ///that includes query information. Processes the $skip argument of the request. private void ProcessSkip() { int count; if (this.ProcessSkipOrTopArgument(XmlConstants.HttpQueryStringSkip, out count)) { this.CheckSetQueryApplicable(); this.query = Skip(this.query, count); } } ////// Checks whether the specified argument should be processed and what /// its value is. /// /// Name of the query item, $top or $skip. /// The value for the query item. ///true if the argument should be processed; false otherwise. private bool ProcessSkipOrTopArgument(string queryItem, out int count) { Debug.Assert(queryItem != null, "queryItem != null"); string itemText = this.service.Host.GetQueryStringItem(queryItem); if (String.IsNullOrEmpty(itemText)) { count = 0; return false; } if (!this.orderApplied) { this.ApplyDefaultOrder(); } if (!Int32.TryParse(itemText, NumberStyles.Integer, CultureInfo.InvariantCulture, out count)) { throw DataServiceException.CreateSyntaxError( Strings.RequestQueryProcessor_IncorrectArgumentFormat(queryItem, itemText)); } return true; } ///Processes the $top argument of the request. private void ProcessTop() { int count; if (this.ProcessSkipOrTopArgument(XmlConstants.HttpQueryStringTop, out count)) { this.CheckSetQueryApplicable(); this.query = Take(this.query, count); } } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007.
Link Menu
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- WebPartDisplayModeCollection.cs
- fixedPageContentExtractor.cs
- MessageBodyMemberAttribute.cs
- SchemaMapping.cs
- DashStyles.cs
- TemplateApplicationHelper.cs
- Binding.cs
- DesignerLoader.cs
- SiteMap.cs
- ReadWriteObjectLock.cs
- MergeFailedEvent.cs
- CodeTypeReferenceExpression.cs
- coordinator.cs
- KoreanLunisolarCalendar.cs
- OleDbPermission.cs
- RegexInterpreter.cs
- FtpCachePolicyElement.cs
- LocalClientSecuritySettings.cs
- AutoResizedEvent.cs
- BackoffTimeoutHelper.cs
- SerializerDescriptor.cs
- XsdDataContractImporter.cs
- IntSumAggregationOperator.cs
- cache.cs
- IndexedGlyphRun.cs
- SurrogateEncoder.cs
- EntityDataSourceConfigureObjectContext.cs
- EntityDataSourceUtil.cs
- Privilege.cs
- DbConnectionOptions.cs
- HttpCookie.cs
- HttpHandler.cs
- CachingHintValidation.cs
- AssemblyUtil.cs
- DbConnectionOptions.cs
- UidManager.cs
- DataGridAutoFormatDialog.cs
- SectionInformation.cs
- HtmlEmptyTagControlBuilder.cs
- SelectionPatternIdentifiers.cs
- DataBindingList.cs
- PreservationFileWriter.cs
- BamlCollectionHolder.cs
- SmtpMail.cs
- SelectionService.cs
- InternalBufferOverflowException.cs
- PagedDataSource.cs
- Package.cs
- HyperlinkAutomationPeer.cs
- PointAnimation.cs
- Application.cs
- EditorBrowsableAttribute.cs
- DataGridViewUtilities.cs
- CodePageEncoding.cs
- DataGridCellsPresenter.cs
- FileSystemWatcher.cs
- SQLStringStorage.cs
- KernelTypeValidation.cs
- StringCollectionEditor.cs
- QuotedPrintableStream.cs
- DSASignatureDeformatter.cs
- HtmlImage.cs
- bidPrivateBase.cs
- ContainerControl.cs
- XPathCompileException.cs
- ImageAnimator.cs
- CompoundFileIOPermission.cs
- ValidationSummary.cs
- Action.cs
- EntityDataSourceColumn.cs
- InvalidContentTypeException.cs
- AppendHelper.cs
- BamlCollectionHolder.cs
- DataGridAddNewRow.cs
- AccessControlList.cs
- SizeAnimation.cs
- Table.cs
- OptimisticConcurrencyException.cs
- MsmqIntegrationAppDomainProtocolHandler.cs
- UriSection.cs
- MenuItemStyleCollection.cs
- SHA384.cs
- HtmlHistory.cs
- ContentIterators.cs
- DataColumnChangeEvent.cs
- CmsInterop.cs
- SyndicationDeserializer.cs
- LinearGradientBrush.cs
- httpserverutility.cs
- BitmapEffectInputConnector.cs
- MissingMemberException.cs
- CanonicalFormWriter.cs
- XhtmlTextWriter.cs
- dtdvalidator.cs
- FileReader.cs
- TableCellAutomationPeer.cs
- ProfilePropertySettingsCollection.cs
- TextProviderWrapper.cs
- HttpModuleCollection.cs
- FixedMaxHeap.cs