private GroupByTransformationNode BindGroupByToken(GroupByToken token) { List <GroupByPropertyNode> properties = new List <GroupByPropertyNode>(); foreach (EndPathToken propertyToken in token.Properties) { QueryNode bindResult = this.bindMethod(propertyToken); SingleValuePropertyAccessNode property = bindResult as SingleValuePropertyAccessNode; SingleComplexNode complexProperty = bindResult as SingleComplexNode; if (property != null) { RegisterProperty(properties, ReversePropertyPath(property)); } else if (complexProperty != null) { RegisterProperty(properties, ReversePropertyPath(complexProperty)); } else { SingleValueOpenPropertyAccessNode openProperty = bindResult as SingleValueOpenPropertyAccessNode; if (openProperty != null) { IEdmTypeReference type = GetTypeReferenceByPropertyName(openProperty.Name); properties.Add(new GroupByPropertyNode(openProperty.Name, openProperty, type)); } else { throw new ODataException( ODataErrorStrings.ApplyBinder_GroupByPropertyNotPropertyAccessValue(propertyToken.Identifier)); } } } var newProperties = new HashSet <EndPathToken>(((GroupByToken)token).Properties); TransformationNode aggregate = null; if (token.Child != null) { if (token.Child.Kind == QueryTokenKind.Aggregate) { aggregate = BindAggregateToken((AggregateToken)token.Child); aggregateExpressionsCache = ((AggregateTransformationNode)aggregate).AggregateExpressions; newProperties.UnionWith(aggregateExpressionsCache.Select(statement => new EndPathToken(statement.Alias, null))); } else { throw new ODataException(ODataErrorStrings.ApplyBinder_UnsupportedGroupByChild(token.Child.Kind)); } } state.AggregatedPropertyNames = newProperties; // TODO: Determine source return(new GroupByTransformationNode(properties, aggregate, null)); }
private GroupByTransformationNode BindGroupByToken(GroupByToken token) { var properties = new List <GroupByPropertyNode>(); foreach (var propertyToken in token.Properties) { var bindResult = this.bindMethod(propertyToken); var property = bindResult as SingleValuePropertyAccessNode; var complexProperty = bindResult as SingleComplexNode; if (property != null) { RegisterProperty(properties, ReversePropertyPath(property)); } else if (complexProperty != null) { RegisterProperty(properties, ReversePropertyPath(complexProperty)); } else { var openProperty = bindResult as SingleValueOpenPropertyAccessNode; if (openProperty != null) { var type = GetTypeReferenceByPropertyName(openProperty.Name); properties.Add(new GroupByPropertyNode(openProperty.Name, openProperty, type)); } else { throw new ODataException( ODataErrorStrings.ApplyBinder_GroupByPropertyNotPropertyAccessValue(propertyToken.Identifier)); } } } TransformationNode aggregate = null; if (token.Child != null) { if (token.Child.Kind == QueryTokenKind.Aggregate) { aggregate = BindAggregateToken((AggregateToken)token.Child); aggregateExpressionsCache = ((AggregateTransformationNode)aggregate).Expressions; state.AggregatedPropertyNames = aggregateExpressionsCache.Select(statement => statement.Alias).ToList(); } else { throw new ODataException(ODataErrorStrings.ApplyBinder_UnsupportedGroupByChild(token.Child.Kind)); } } // TODO: Determine source return(new GroupByTransformationNode(properties, aggregate, null)); }
/// <summary> /// Build a segment from a token. /// </summary> /// <param name="tokenIn">the token to bind</param> /// <param name="model">The model.</param> /// <param name="edmType">the type of the current scope based on type segments.</param> /// <param name="resolver">Resolver for uri parser.</param> /// <param name="state">The binding state.</param> /// <returns>The segment created from the token.</returns> public static ODataPathSegment ConvertNonTypeTokenToSegment(PathSegmentToken tokenIn, IEdmModel model, IEdmStructuredType edmType, ODataUriResolver resolver, BindingState state = null) { ExceptionUtils.CheckArgumentNotNull(resolver, "resolver"); ODataPathSegment nextSegment; if (UriParserHelper.IsAnnotation(tokenIn.Identifier)) { if (TryBindAsDeclaredTerm(tokenIn, model, resolver, out nextSegment)) { return(nextSegment); } string qualifiedTermName = tokenIn.Identifier.Remove(0, 1); int separator = qualifiedTermName.LastIndexOf(".", StringComparison.Ordinal); string namespaceName = qualifiedTermName.Substring(0, separator); string termName = qualifiedTermName.Substring(separator == 0 ? 0 : separator + 1); // Don't allow selecting odata control information if (String.Compare(namespaceName, ODataConstants.ODataPrefix, StringComparison.OrdinalIgnoreCase) == 0) { throw new ODataException(ODataErrorStrings.UriSelectParser_TermIsNotValid(tokenIn.Identifier)); } return(new AnnotationSegment(new EdmTerm(namespaceName, termName, EdmCoreModel.Instance.GetUntyped()))); } EndPathToken endPathToken = new EndPathToken(tokenIn.Identifier, null); if ((state?.IsCollapsed ?? false) && !(state?.AggregatedPropertyNames?.Contains(endPathToken) ?? false)) { throw new ODataException(ODataErrorStrings.ApplyBinder_GroupByPropertyNotPropertyAccessValue(tokenIn.Identifier)); } if (TryBindAsDeclaredProperty(tokenIn, edmType, resolver, out nextSegment)) { return(nextSegment); } // Operations must be container-qualified, and because the token type indicates it was not a .-separated identifier, we should not try to look up operations. if (tokenIn.IsNamespaceOrContainerQualified()) { if (TryBindAsOperation(tokenIn, model, edmType, out nextSegment)) { return(nextSegment); } // If an action or function is requested in a selectItem using a qualifiedActionName or a qualifiedFunctionName // and that operation cannot be bound to the entities requested, the service MUST ignore the selectItem. if (!edmType.IsOpen) { return(null); } } if (edmType.IsOpen || (state?.AggregatedPropertyNames?.Contains(endPathToken) ?? false)) { return(new DynamicPathSegment(tokenIn.Identifier)); } throw new ODataException(ODataErrorStrings.MetadataBinder_PropertyNotDeclared(edmType.FullTypeName(), tokenIn.Identifier)); }
/// <summary> /// Binds an end path token into a PropertyAccessToken, OpenPropertyToken, or FunctionCallToken. /// </summary> /// <param name="endPathToken">The property access token to bind.</param> /// <returns>A Query node representing this endpath token, bound to metadata.</returns> internal QueryNode BindEndPath(EndPathToken endPathToken) { ExceptionUtils.CheckArgumentNotNull(endPathToken, "EndPathToken"); ExceptionUtils.CheckArgumentStringNotNullOrEmpty(endPathToken.Identifier, "EndPathToken.Identifier"); // Set the parent (get the parent type, so you can check whether the Identifier inside EndPathToken really is legit offshoot of the parent type) QueryNode parent = this.DetermineParentNode(endPathToken); QueryNode boundFunction; SingleValueNode singleValueParent = parent as SingleValueNode; if (singleValueParent != null) { if (endPathToken.Identifier == ExpressionConstants.QueryOptionCount) { return(new CountVirtualPropertyNode()); } if (state.IsCollapsed && !IsAggregatedProperty(endPathToken)) { throw new ODataException(ODataErrorStrings.ApplyBinder_GroupByPropertyNotPropertyAccessValue(endPathToken.Identifier)); } // Now that we have the parent type, can find its corresponding EDM type IEdmStructuredTypeReference structuredParentType = singleValueParent.TypeReference == null ? null : singleValueParent.TypeReference.AsStructuredOrNull(); IEdmProperty property = structuredParentType == null ? null : this.Resolver.ResolveProperty(structuredParentType.StructuredDefinition(), endPathToken.Identifier); if (property != null) { return(GeneratePropertyAccessQueryNode(singleValueParent as SingleResourceNode, property, state)); } if (functionCallBinder.TryBindEndPathAsFunctionCall(endPathToken, singleValueParent, state, out boundFunction)) { return(boundFunction); } return(GeneratePropertyAccessQueryForOpenType(endPathToken, singleValueParent)); } // Collection with any or all expression is already supported and handled separately. // Add support of collection with $count segment. CollectionNode colNode = parent as CollectionNode; if (colNode != null && endPathToken.Identifier.Equals(UriQueryConstants.CountSegment)) { // create a collection count node for collection node property. return(new CountNode(colNode)); } CollectionNavigationNode collectionParent = parent as CollectionNavigationNode; if (collectionParent != null) { IEdmEntityTypeReference parentType = collectionParent.EntityItemType; IEdmProperty property = this.Resolver.ResolveProperty(parentType.StructuredDefinition(), endPathToken.Identifier); if (property.PropertyKind == EdmPropertyKind.Structural && !property.Type.IsCollection() && this.state.InEntitySetAggregation) { return(new AggregatedCollectionPropertyNode(collectionParent, property)); } } if (functionCallBinder.TryBindEndPathAsFunctionCall(endPathToken, parent, state, out boundFunction)) { return(boundFunction); } throw new ODataException(ODataErrorStrings.MetadataBinder_PropertyAccessSourceNotSingleValue(endPathToken.Identifier)); }