/// <summary> /// Processes the filter of the query (if any). /// </summary> /// <param name="query">The query tree constructed so far.</param> /// <param name="filter">The filter to bind.</param> /// <returns>If no filter is specified, returns the <paramref name="query"/> unchanged. If a filter is specified it returns the combined query including the filter.</returns> private QueryNode ProcessFilter(QueryNode query, QueryToken filter) { ExceptionUtils.CheckArgumentNotNull(query, "query"); if (filter != null) { CollectionQueryNode entityCollection = query.AsEntityCollectionNode(); if (entityCollection == null) { throw new ODataException(Strings.MetadataBinder_FilterNotApplicable); } this.parameter = new ParameterQueryNode() { ParameterType = entityCollection.ItemType }; QueryNode expressionNode = this.Bind(filter); SingleValueQueryNode expressionResultNode = expressionNode as SingleValueQueryNode; if (expressionResultNode == null || (expressionResultNode.TypeReference != null && !expressionResultNode.TypeReference.IsODataPrimitiveTypeKind())) { throw new ODataException(Strings.MetadataBinder_FilterExpressionNotSingleValue); } // The type may be null here if the query statically represents the null literal // TODO: once we support open types/properties a 'null' type will mean 'we don't know the type'. Review. IEdmTypeReference expressionResultType = expressionResultNode.TypeReference; if (expressionResultType != null) { IEdmPrimitiveTypeReference primitiveExpressionResultType = expressionResultType.AsPrimitiveOrNull(); if (primitiveExpressionResultType == null || primitiveExpressionResultType.PrimitiveKind() != EdmPrimitiveTypeKind.Boolean) { throw new ODataException(Strings.MetadataBinder_FilterExpressionNotSingleValue); } } query = new FilterQueryNode() { Collection = entityCollection, Parameter = this.parameter, Expression = expressionResultNode }; this.parameter = null; } return query; }
/// <summary> /// Processes the specified order-by token. /// </summary> /// <param name="query">The query tree constructed so far.</param> /// <param name="orderByToken">The order-by token to bind.</param> /// <returns>Returns the combined query including the ordering.</returns> private QueryNode ProcessSingleOrderBy(QueryNode query, OrderByQueryToken orderByToken) { Debug.Assert(query != null, "query != null"); ExceptionUtils.CheckArgumentNotNull(orderByToken, "orderByToken"); CollectionQueryNode entityCollection = query.AsEntityCollectionNode(); if (entityCollection == null) { throw new ODataException(Strings.MetadataBinder_OrderByNotApplicable); } this.parameter = new ParameterQueryNode() { ParameterType = entityCollection.ItemType }; QueryNode expressionNode = this.Bind(orderByToken.Expression); // TODO: shall we really restrict order-by expressions to primitive types? SingleValueQueryNode expressionResultNode = expressionNode as SingleValueQueryNode; if (expressionResultNode == null || (expressionResultNode.TypeReference != null && !expressionResultNode.TypeReference.IsODataPrimitiveTypeKind())) { throw new ODataException(Strings.MetadataBinder_OrderByExpressionNotSingleValue); } query = new OrderByQueryNode() { Collection = entityCollection, Direction = orderByToken.Direction, Parameter = this.parameter, Expression = expressionResultNode }; this.parameter = null; return query; }
/// <summary> /// Binds a <see cref="NavigationPropertyToken"/>. /// </summary> /// <param name="segmentToken">The segment token to bind.</param> /// <returns>The bound node.</returns> private SingleValueQueryNode BindNavigationProperty(NavigationPropertyToken segmentToken) { QueryNode source = null; IEdmNavigationProperty property; if (segmentToken.Parent != null) { source = this.Bind(segmentToken.Parent); IEdmType entityType = null; if (IsDerivedComplexType(segmentToken, GetType(source))) { IEdmProperty returnProperty = BindProperty(GetType(source).ToTypeReference(), segmentToken.Name); return new PropertyAccessQueryNode() { Source = (SingleValueQueryNode)source, Property = returnProperty }; } else { entityType = GetType(source); } ParameterQueryNode parentNode = new ParameterQueryNode { ParameterType = entityType.ToTypeReference() }; property = (IEdmNavigationProperty)BindProperty(parentNode.TypeReference, segmentToken.Name); } else { if (IsDerivedComplexType(segmentToken, this.parameter.TypeReference.Definition)) { IEdmProperty returnProperty = BindProperty(new EdmEntityTypeReference((IEdmEntityType)this.parameter.TypeReference.Definition, true), segmentToken.Name); return new PropertyAccessQueryNode { Source = this.parameter, Property = returnProperty }; } property = (IEdmNavigationProperty)BindProperty(this.parameter.TypeReference, segmentToken.Name); } // Ensure that only collections head Any queries and nothing else if (property.OwnMultiplicity() == EdmMultiplicity.Many && segmentToken.AnyAllParent == false) { throw new ODataException(Strings.MetadataBinder_PropertyAccessSourceNotSingleValue(segmentToken.Name)); } else if (property.OwnMultiplicity() != EdmMultiplicity.Many && segmentToken.AnyAllParent == true) { throw new ODataException(Strings.MetadataBinder_InvalidAnyAllHead); } return new NavigationPropertyNode() { Source = source, NavigationProperty = property }; }
/// <summary> /// Binds a property access token. /// </summary> /// <param name="propertyAccessToken">The property access token to bind.</param> /// <returns>The bound property access token.</returns> protected virtual QueryNode BindPropertyAccess(PropertyAccessQueryToken propertyAccessToken) { ExceptionUtils.CheckArgumentNotNull(propertyAccessToken, "propertyAccessToken"); ExceptionUtils.CheckArgumentStringNotNullOrEmpty(propertyAccessToken.Name, "propertyAccessToken.Name"); SingleValueQueryNode parentNode; SingleValueQueryNode navigationPath = null; // Set the parentNode (get the parent type, so you can check whether the Name inside propertyAccessToken really is legit offshoot of the parent type) QueryNode parent = null; if (propertyAccessToken.Parent == null) { if (this.parameter == null) { throw new ODataException(Strings.MetadataBinder_PropertyAccessWithoutParentParameter); } parentNode = this.parameter; parent = this.parameter; } else { parent = this.Bind(propertyAccessToken.Parent) as SingleValueQueryNode; if (parent == null) { throw new ODataException(Strings.MetadataBinder_PropertyAccessSourceNotSingleValue(propertyAccessToken.Name)); } NavigationPropertyNode parentNav = parent as NavigationPropertyNode; CastNode parentCast = parent as CastNode; PropertyAccessQueryNode parentProperty = parent as PropertyAccessQueryNode; if (parentProperty != null) { navigationPath = parentProperty; var propertyPath = (IEdmStructuralProperty)parentProperty.Property; parentNode = new ParameterQueryNode() { ParameterType = propertyPath.Type }; } else if (parentCast != null) { IEdmType entityType = parentCast.EdmType; parentNode = new ParameterQueryNode() { ParameterType = entityType.ToTypeReference() }; } else if (parentNav != null) { navigationPath = parentNav; IEdmEntityType entityType = parentNav.NavigationProperty.ToEntityType(); parentNode = new ParameterQueryNode() { ParameterType = new EdmEntityTypeReference(entityType, true) }; } else { parentNode = this.Bind(propertyAccessToken.Parent) as SingleValueQueryNode; } } // Now that we have the parent type, can find its corresponding EDM type IEdmStructuredTypeReference structuredParentType = parentNode.TypeReference == null ? null : parentNode.TypeReference.AsStructuredOrNull(); IEdmProperty property = structuredParentType == null ? null : structuredParentType.FindProperty(propertyAccessToken.Name); if (property != null) { // TODO: Check that we have entity set once we add the entity set propagation into the bound tree // See RequestQueryParser.ExpressionParser.ParseMemberAccess if (property.Type.IsNonEntityODataCollectionTypeKind()) { throw new ODataException(Strings.MetadataBinder_MultiValuePropertyNotSupportedInExpression(property.Name)); } if (property.PropertyKind == EdmPropertyKind.Navigation) { // TODO: Implement navigations throw new NotImplementedException(); } if (navigationPath != null) { parentNode = navigationPath; } return new PropertyAccessQueryNode() { Source = (SingleValueQueryNode)parent, Property = property }; } else { if (parentNode.TypeReference != null && !parentNode.TypeReference.Definition.IsOpenType()) { throw new ODataException(Strings.MetadataBinder_PropertyNotDeclared(parentNode.TypeReference.ODataFullName(), propertyAccessToken.Name)); } // TODO: Implement open property support throw new NotImplementedException(); } }
private Expression BindParameterQueryNode(ParameterQueryNode parameterNode) { return(_lambdaParameters[parameterNode.Name]); }
/// <summary> /// Translates a parameter node. /// </summary> /// <param name="parameterNode">The parameter node to translate.</param> /// <returns>Expression which evaluates to the result of the parameter.</returns> protected virtual Expression TranslateParameter(ParameterQueryNode parameterNode) { ExceptionUtils.CheckArgumentNotNull(parameterNode, "parameterNode"); if (this.parameterNodeDefinitions.Count == 0) { throw new ODataException(Strings.QueryExpressionTranslator_ParameterNotDefinedInScope); } KeyValuePair<ParameterQueryNode, Expression> currentParameter = this.parameterNodeDefinitions.Peek(); if (!object.ReferenceEquals(currentParameter.Key, parameterNode)) { throw new ODataException(Strings.QueryExpressionTranslator_ParameterNotDefinedInScope); } return currentParameter.Value; }
/// <summary> /// Processes the filter of the query (if any). /// </summary> /// <param name="query">The query tree constructed so far.</param> /// <param name="filter">The filter to bind.</param> /// <returns>If no filter is specified, returns the <paramref name="query"/> unchanged. If a filter is specified it returns the combined query including the filter.</returns> private QueryNode ProcessFilter(QueryNode query, QueryToken filter) { ExceptionUtils.CheckArgumentNotNull(query, "query"); if (filter != null) { CollectionQueryNode entityCollection = query.AsEntityCollectionNode(); if (entityCollection == null) { throw new ODataException(Strings.MetadataBinder_FilterNotApplicable); } this.parameter = new ParameterQueryNode() { ParameterResourceType = entityCollection.ItemType }; QueryNode expressionNode = this.Bind(filter); SingleValueQueryNode expressionResultNode = expressionNode as SingleValueQueryNode; if (expressionResultNode == null || (expressionResultNode.ResourceType != null && expressionResultNode.ResourceType.ResourceTypeKind != ResourceTypeKind.Primitive)) { throw new ODataException(Strings.MetadataBinder_FilterExpressionNotSingleValue); } // The resource type may be null here if the query statically represents the null literal // TODO: once we support open types/properties a 'null' resource type will mean 'we don't know the type'. Review. if (expressionResultNode.ResourceType != null && !this.ResourceTypesEqual(expressionResultNode.ResourceType, PrimitiveTypeUtils.BoolResourceType) && !this.ResourceTypesEqual(expressionResultNode.ResourceType, PrimitiveTypeUtils.NullableBoolResourceType)) { throw new ODataException(Strings.MetadataBinder_FilterExpressionNotSingleValue); } query = new FilterQueryNode() { Collection = entityCollection, Parameter = this.parameter, Expression = expressionResultNode }; this.parameter = null; } return query; }