/// <summary> /// Builds an appropriate navigation query node (collection or single) for the given property and parent node. /// </summary> /// <param name="property">Navigation property.</param> /// <param name="parent">Parent Node.</param> /// <param name="namedValues">Named values (key values) that were included in the node we are binding, if any.</param> /// <param name="state">State of binding.</param> /// <param name="keyBinder">Object to perform binding on any key values that are present.</param> /// <param name="navigationSource">The navigation source of the navigation node.</param> /// <returns>A new CollectionNavigationNode or SingleNavigationNode to capture the navigation property access.</returns> internal static QueryNode GetNavigationNode(IEdmNavigationProperty property, SingleResourceNode parent, IEnumerable <NamedValue> namedValues, BindingState state, KeyBinder keyBinder, out IEdmNavigationSource navigationSource) { ExceptionUtils.CheckArgumentNotNull(property, "property"); ExceptionUtils.CheckArgumentNotNull(parent, "parent"); ExceptionUtils.CheckArgumentNotNull(state, "state"); ExceptionUtils.CheckArgumentNotNull(keyBinder, "keyBinder"); // Handle collection navigation property if (property.TargetMultiplicity() == EdmMultiplicity.Many) { CollectionNavigationNode collectionNavigationNode = new CollectionNavigationNode(parent, property, state.ParsedSegments); navigationSource = collectionNavigationNode.NavigationSource; // Doing key lookup on the collection navigation property if (namedValues != null) { return(keyBinder.BindKeyValues(collectionNavigationNode, namedValues, state.Model)); } // Otherwise it's just a normal collection of entities return(collectionNavigationNode); } Debug.Assert(namedValues == null || !namedValues.Any(), "namedValues should not exist if it isn't a collection"); // Otherwise it's a single navigation property SingleNavigationNode singleNavigationNode = new SingleNavigationNode(parent, property, state.ParsedSegments); navigationSource = singleNavigationNode.NavigationSource; return(singleNavigationNode); }
protected string Bind(QueryNode node) { CollectionNode collectionNode = node as CollectionNode; SingleValueNode singleValueNode = node as SingleValueNode; if (collectionNode != null) { switch (node.Kind) { case QueryNodeKind.CollectionNavigationNode: CollectionNavigationNode navigationNode = node as CollectionNavigationNode; return(BindNavigationPropertyNode(navigationNode.Source, navigationNode.NavigationProperty)); case QueryNodeKind.CollectionPropertyAccess: return(BindCollectionPropertyAccessNode(node as CollectionPropertyAccessNode)); } } else if (singleValueNode != null) { switch (node.Kind) { case QueryNodeKind.BinaryOperator: return(BindBinaryOperatorNode(node as BinaryOperatorNode)); case QueryNodeKind.Constant: return(BindConstantNode(node as ConstantNode)); case QueryNodeKind.Convert: return(BindConvertNode(node as ConvertNode)); //case QueryNodeKind.EntityRangeVariableReference: // return BindRangeVariable((node as EntityRangeVariableReferenceNode).RangeVariable); //case QueryNodeKind.NonentityRangeVariableReference: // return BindRangeVariable((node as NonentityRangeVariableReferenceNode).RangeVariable); case QueryNodeKind.SingleValuePropertyAccess: return(BindPropertyAccessQueryNode(node as SingleValuePropertyAccessNode)); case QueryNodeKind.UnaryOperator: return(BindUnaryOperatorNode(node as UnaryOperatorNode)); case QueryNodeKind.SingleValueFunctionCall: return(BindSingleValueFunctionCallNode(node as SingleValueFunctionCallNode)); case QueryNodeKind.SingleNavigationNode: SingleNavigationNode navigationNode = node as SingleNavigationNode; return(BindNavigationPropertyNode(navigationNode.Source, navigationNode.NavigationProperty)); case QueryNodeKind.Any: return(BindAnyNode(node as AnyNode)); case QueryNodeKind.All: return(BindAllNode(node as AllNode)); } } throw new NotSupportedException(String.Format("Nodes of type {0} are not supported", node.Kind)); }
/// <summary> /// Constructs a <see cref="SingleValuePropertyAccessNode"/>. /// </summary> /// <param name="source">The value containing this property.</param> /// <param name="property">The EDM property which is to be accessed.</param> /// <exception cref="System.ArgumentNullException">Throws if input source or property is null.</exception> /// <exception cref="ArgumentException">Throws if input property is not structural, or is a collection.</exception> public AggregatedCollectionPropertyNode(CollectionNavigationNode source, IEdmProperty property) { ExceptionUtils.CheckArgumentNotNull(source, "source"); ExceptionUtils.CheckArgumentNotNull(property, "property"); if (property.PropertyKind != EdmPropertyKind.Structural) { throw new ArgumentException(ODataErrorStrings.Nodes_PropertyAccessShouldBeNonEntityProperty(property.Name)); } if (property.Type.IsCollection()) { throw new ArgumentException(ODataErrorStrings.Nodes_PropertyAccessTypeShouldNotBeCollection(property.Name)); } this.source = source; this.property = property; this.typeReference = property.Type.AsComplex(); this.navigationSource = source.NavigationSource; }
/// <summary> /// Visit a CollectionNavigationNode /// </summary> /// <param name="nodeIn">the node to visit</param> /// <returns>Defined by the implementer</returns> public virtual T Visit(CollectionNavigationNode nodeIn) { throw new NotImplementedException(); }
/// <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) { // 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); } if (endPathToken.Identifier == ExpressionConstants.QueryOptionCount) { return(new CountVirtualPropertyNode()); } 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.AggregatedPropertyNames != null && this.state.AggregatedPropertyNames.Any(p => p == collectionParent.NavigationProperty.Name)) { return(new AggregatedCollectionPropertyNode(collectionParent, property)); } } if (functionCallBinder.TryBindEndPathAsFunctionCall(endPathToken, parent, state, out boundFunction)) { return(boundFunction); } throw new ODataException(ODataErrorStrings.MetadataBinder_PropertyAccessSourceNotSingleValue(endPathToken.Identifier)); }
/// <summary> /// Binds a <see cref="InnerPathToken"/>. /// This includes more than just navigations - it includes complex property access and primitive collections. /// </summary> /// <param name="segmentToken">The segment token to bind.</param> /// <returns>The bound node.</returns> internal QueryNode BindInnerPathSegment(InnerPathToken segmentToken) { FunctionCallBinder functionCallBinder = new FunctionCallBinder(this.bindMethod, state); // First we get the parent node QueryNode parent = this.DetermineParentNode(segmentToken, state); Debug.Assert(parent != null, "parent should never be null"); SingleValueNode singleValueParent = parent as SingleValueNode; if (singleValueParent == null) { QueryNode boundFunction; if (functionCallBinder.TryBindInnerPathAsFunctionCall(segmentToken, parent, out boundFunction)) { return(boundFunction); } CollectionNavigationNode collectionParent = parent as CollectionNavigationNode; if (collectionParent != null) { IEdmEntityTypeReference parentType = collectionParent.EntityItemType; IEdmProperty collectionProperty = this.Resolver.ResolveProperty(parentType.StructuredDefinition(), segmentToken.Identifier); if (collectionProperty != null && collectionProperty.PropertyKind == EdmPropertyKind.Structural) { return(new AggregatedCollectionPropertyNode(collectionParent, collectionProperty)); } } throw new ODataException(ODataErrorStrings.MetadataBinder_PropertyAccessSourceNotSingleValue(segmentToken.Identifier)); } // Using the parent and name of this token, we try to get the IEdmProperty it represents IEdmProperty property = BindProperty(singleValueParent.TypeReference, segmentToken.Identifier, this.Resolver); if (property == null) { QueryNode boundFunction; if (functionCallBinder.TryBindInnerPathAsFunctionCall(segmentToken, parent, out boundFunction)) { return(boundFunction); } if (singleValueParent.TypeReference != null && !singleValueParent.TypeReference.Definition.IsOpen()) { throw new ODataException( ODataErrorStrings.MetadataBinder_PropertyNotDeclared( parent.GetEdmTypeReference().FullName(), segmentToken.Identifier)); } return(new SingleValueOpenPropertyAccessNode(singleValueParent, segmentToken.Identifier)); } IEdmStructuralProperty structuralProperty = property as IEdmStructuralProperty; if (property.Type.IsComplex()) { // Generate a segment to parsed segments for the parsed token state.ParsedSegments.Add(new PropertySegment(structuralProperty)); return(new SingleComplexNode(singleValueParent as SingleResourceNode, property)); } else if (property.Type.IsPrimitive()) { return(new SingleValuePropertyAccessNode(singleValueParent, property)); } // Note - this means nonentity collection (primitive or complex) if (property.Type.IsNonEntityCollectionType()) { if (property.Type.IsStructuredCollectionType()) { // Generate a segment to parsed segments for the parsed token state.ParsedSegments.Add(new PropertySegment(structuralProperty)); return(new CollectionComplexNode(singleValueParent as SingleResourceNode, property)); } return(new CollectionPropertyAccessNode(singleValueParent, property)); } IEdmNavigationProperty navigationProperty = property as IEdmNavigationProperty; if (navigationProperty == null) { throw new ODataException(ODataErrorStrings.MetadataBinder_IllegalSegmentType(property.Name)); } SingleResourceNode parentResource = EnsureParentIsResourceForNavProp(singleValueParent); IEdmNavigationSource navigationSource; QueryNode node = GetNavigationNode(navigationProperty, parentResource, segmentToken.NamedValues, state, new KeyBinder(this.bindMethod), out navigationSource); // Generate a segment to parsed segments for the parsed token state.ParsedSegments.Add(new NavigationPropertySegment(navigationProperty, navigationSource)); return(node); }