Пример #1
0
        /// <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);
        }
Пример #2
0
 /// <summary>
 /// Created a SingleResourceCastNode with the given source node and the given type to cast to.
 /// </summary>
 /// <param name="source"> Source <see cref="SingleValueNode"/> that is being cast.</param>
 /// <param name="structuredType">Type to cast to.</param>
 /// <exception cref="System.ArgumentNullException">Throws if the input entityType is null.</exception>
 public SingleResourceCastNode(SingleResourceNode source, IEdmStructuredType structuredType)
 {
     ExceptionUtils.CheckArgumentNotNull(structuredType, "structuredType");
     this.source                  = source;
     this.navigationSource        = source != null ? source.NavigationSource : null;
     this.structuredTypeReference = structuredType.GetTypeReference();
 }
Пример #3
0
        /// <summary>
        /// Ensures that the parent node is of structured type, throwing if it is not.
        /// </summary>
        /// <param name="parent">Parent node to a navigation property.</param>
        /// <returns>The given parent node as a SingleResourceNode.</returns>
        internal static SingleResourceNode EnsureParentIsResourceForNavProp(SingleValueNode parent)
        {
            ExceptionUtils.CheckArgumentNotNull(parent, "parent");

            SingleResourceNode parentResource = parent as SingleResourceNode;

            if (parentResource == null)
            {
                // TODO: update error message #644
                throw new ODataException(ODataErrorStrings.MetadataBinder_NavigationPropertyNotFollowingSingleEntityType);
            }

            return(parentResource);
        }
Пример #4
0
        /// <summary>
        /// Generates a bound query node representing an <see cref="IEdmProperty"/> given an already semantically bound parent node.
        /// </summary>
        /// <param name="parentNode">The semantically bound source node of this end path token</param>
        /// <param name="property">The <see cref="IEdmProperty"/> that will be bound to this node. Must not be primitive collection</param>
        /// <param name="state">The state of binding.</param>
        /// <returns>QueryNode bound to this property.</returns>
        internal static QueryNode GeneratePropertyAccessQueryNode(SingleResourceNode parentNode, IEdmProperty property, BindingState state)
        {
            ExceptionUtils.CheckArgumentNotNull(parentNode, "parentNode");
            ExceptionUtils.CheckArgumentNotNull(property, "property");

            // TODO: Remove this check.
            // We should verify that the top level of an expression is a bool rather than arbitrarily restrict property types.
            // We can get here if there is a query like $filter=MyCollectionProperty eq 'foo' or something.
            // If it was $filter=MyCollectionProperty/any(...) then we would have gone down the 'NonRootSegment' code path instead of this one
            if (property.Type.IsNonEntityCollectionType())
            {
                // if this happens to be a top level node (i.e. $filter=MyCollection), then it will fail further up the chain, so
                // don't need to worry about checking for that here.
                if (property.Type.IsStructuredCollectionType())
                {
                    return(new CollectionComplexNode(parentNode, property));
                }
                else
                {
                    return(new CollectionPropertyAccessNode(parentNode, property));
                }
            }

            if (property.PropertyKind == EdmPropertyKind.Navigation)
            {
                // These are error cases in practice, but we let ourselves throw later for better context-sensitive error messages
                IEdmNavigationProperty edmNavigationProperty = (IEdmNavigationProperty)property;
                if (edmNavigationProperty.TargetMultiplicity() == EdmMultiplicity.Many)
                {
                    return(new CollectionNavigationNode(parentNode, edmNavigationProperty, state.ParsedSegments));
                }

                return(new SingleNavigationNode(parentNode, edmNavigationProperty, state.ParsedSegments));
            }

            if (property.Type.IsComplex())
            {
                return(new SingleComplexNode(parentNode, property));
            }

            return(new SingleValuePropertyAccessNode(parentNode, property));
        }
Пример #5
0
        /// <summary>
        /// Build a SingleValueFunctionCallNode for a function that isn't bound to a BuiltInFunction
        /// </summary>
        /// <param name="functionCallTokenName">Name for the function</param>
        /// <param name="args">list of already bound query nodes for this function</param>
        /// <returns>A single value function call node bound to this function.</returns>
        private SingleValueNode CreateUnboundFunctionNode(string functionCallTokenName, List <QueryNode> args)
        {
            // need to figure out the return type and check the correct number of arguments based on the function name
            IEdmTypeReference returnType = null;

            switch (functionCallTokenName)
            {
            case ExpressionConstants.UnboundFunctionIsOf:
            {
                returnType = ValidateAndBuildIsOfArgs(state, ref args);
                break;
            }

            case ExpressionConstants.UnboundFunctionCast:
            {
                returnType = ValidateAndBuildCastArgs(state, ref args);
                if (returnType.IsStructured())
                {
                    SingleResourceNode entityNode = args.ElementAt(0) as SingleResourceNode;

                    return(new SingleResourceFunctionCallNode(functionCallTokenName, args,
                                                              returnType.AsStructured(), entityNode != null ? entityNode.NavigationSource : null));
                }

                break;
            }

            default:
            {
                break;
            }
            }

            // we have everything else we need, so return the new SingleValueFunctionCallNode.
            return(new SingleValueFunctionCallNode(functionCallTokenName, args, returnType));
        }
Пример #6
0
        /// <summary>
        /// Try to bind an identifier to a FunctionCallNode
        /// </summary>
        /// <param name="identifier">the identifier to bind</param>
        /// <param name="arguments">the semantically bound list of arguments.</param>
        /// <param name="parent">a semantically bound parent node.</param>
        /// <param name="state">the current state of the binding algorithm</param>
        /// <param name="boundFunction">a single value function call node representing this function call, if we found one.</param>
        /// <returns>true if we found a function for this token.</returns>
        private bool TryBindIdentifier(string identifier, IEnumerable <FunctionParameterToken> arguments, QueryNode parent, BindingState state, out QueryNode boundFunction)
        {
            boundFunction = null;

            IEdmType        bindingType       = null;
            SingleValueNode singleValueParent = parent as SingleValueNode;

            if (singleValueParent != null)
            {
                if (singleValueParent.TypeReference != null)
                {
                    bindingType = singleValueParent.TypeReference.Definition;
                }
            }
            else
            {
                CollectionNode collectionValueParent = parent as CollectionNode;
                if (collectionValueParent != null)
                {
                    bindingType = collectionValueParent.CollectionType.Definition;
                }
            }

            if (!UriEdmHelpers.IsBindingTypeValid(bindingType))
            {
                return(false);
            }

            // All functions should be fully qualified, if they aren't they they aren't functions.
            // When using extension, there may be function call with unqualified name. So loose the restriction here.
            if (identifier.IndexOf(".", StringComparison.Ordinal) == -1 && this.Resolver.GetType() == typeof(ODataUriResolver))
            {
                return(false);
            }

            IEdmOperation operation;
            List <FunctionParameterToken> syntacticArguments = arguments == null ? new List <FunctionParameterToken>() : arguments.ToList();

            if (!FunctionOverloadResolver.ResolveOperationFromList(identifier, syntacticArguments.Select(ar => ar.ParameterName).ToList(), bindingType, state.Model, out operation, this.Resolver))
            {
                // TODO: FunctionOverloadResolver.ResolveOperationFromList() looks up the function by parameter names, but it shouldn't ignore parameter types. (test case ParseFilter_AliasInFunction_PropertyAsValue_TypeMismatch should fail)
                return(false);
            }

            if (singleValueParent != null && singleValueParent.TypeReference == null)
            {
                // if the parent exists, but has no type information, then we're in open type land, and we
                // shouldn't go any farther.
                throw new ODataException(ODataErrorStrings.FunctionCallBinder_CallingFunctionOnOpenProperty(identifier));
            }

            if (operation.IsAction())
            {
                return(false);
            }

            IEdmFunction function = (IEdmFunction)operation;

            // TODO:  $filter $orderby parameter expression which contains complex or collection should NOT be supported in this way
            //     but should be parsed into token tree, and binded to node tree: parsedParameters.Select(p => this.bindMethod(p));
            ICollection <FunctionParameterToken> parsedParameters = HandleComplexOrCollectionParameterValueIfExists(state.Configuration.Model, function, syntacticArguments, state.Configuration.Resolver.EnableCaseInsensitive);

            IEnumerable <QueryNode> boundArguments = parsedParameters.Select(p => this.bindMethod(p));

            boundArguments = boundArguments.ToList(); // force enumerable to run : will immediately evaluate all this.bindMethod(p).
            IEdmTypeReference  returnType       = function.ReturnType;
            IEdmEntitySetBase  returnSet        = null;
            SingleResourceNode singleEntityNode = parent as SingleResourceNode;

            if (singleEntityNode != null)
            {
                returnSet = function.GetTargetEntitySet(singleEntityNode.NavigationSource, state.Model);
            }

            string functionName = function.FullName();

            if (returnType.IsEntity())
            {
                boundFunction = new SingleResourceFunctionCallNode(functionName, new[] { function }, boundArguments, (IEdmEntityTypeReference)returnType.Definition.ToTypeReference(), returnSet, parent);
            }
            else if (returnType.IsStructuredCollection())
            {
                IEdmCollectionTypeReference collectionTypeReference = (IEdmCollectionTypeReference)returnType;
                boundFunction = new CollectionResourceFunctionCallNode(functionName, new[] { function }, boundArguments, collectionTypeReference, returnSet, parent);
            }
            else if (returnType.IsCollection())
            {
                IEdmCollectionTypeReference collectionTypeReference = (IEdmCollectionTypeReference)returnType;
                boundFunction = new CollectionFunctionCallNode(functionName, new[] { function }, boundArguments, collectionTypeReference, parent);
            }
            else
            {
                boundFunction = new SingleValueFunctionCallNode(functionName, new[] { function }, boundArguments,
                                                                returnType, parent);
            }

            return(true);
        }
        /// <summary>
        /// Binds a DottedIdentifierToken and it's parent node (if needed).
        /// </summary>
        /// <param name="dottedIdentifierToken">Token to bind to metadata.</param>
        /// <returns>A bound node representing the cast.</returns>
        internal QueryNode BindDottedIdentifier(DottedIdentifierToken dottedIdentifierToken)
        {
            ExceptionUtils.CheckArgumentNotNull(dottedIdentifierToken, "castToken");
            ExceptionUtils.CheckArgumentNotNull(state, "state");

            QueryNode parent     = null;
            IEdmType  parentType = null;

            if (state.ImplicitRangeVariable != null)
            {
                if (dottedIdentifierToken.NextToken == null)
                {
                    parent     = NodeFactory.CreateRangeVariableReferenceNode(state.ImplicitRangeVariable);
                    parentType = state.ImplicitRangeVariable.TypeReference.Definition;
                }
                else
                {
                    parent     = this.bindMethod(dottedIdentifierToken.NextToken);
                    parentType = parent.GetEdmType();
                }
            }

            SingleResourceNode parentAsSingleResource = parent as SingleResourceNode;
            IEdmSchemaType     childType           = UriEdmHelpers.FindTypeFromModel(state.Model, dottedIdentifierToken.Identifier, this.Resolver);
            IEdmStructuredType childStructuredType = childType as IEdmStructuredType;

            if (childStructuredType == null)
            {
                SingleValueNode    singleValueNode    = parent as SingleValueNode;
                FunctionCallBinder functionCallBinder = new FunctionCallBinder(bindMethod, state);
                QueryNode          functionCallNode;
                if (functionCallBinder.TryBindDottedIdentifierAsFunctionCall(dottedIdentifierToken, singleValueNode, out functionCallNode))
                {
                    return(functionCallNode);
                }
                else if ((!string.IsNullOrEmpty(dottedIdentifierToken.Identifier)) &&
                         (dottedIdentifierToken.Identifier[dottedIdentifierToken.Identifier.Length - 1] == '\''))
                {
                    // check if it is enum or not
                    QueryNode enumNode;
                    if (EnumBinder.TryBindDottedIdentifierAsEnum(dottedIdentifierToken, parentAsSingleResource, state, this.Resolver, out enumNode))
                    {
                        return(enumNode);
                    }
                    else
                    {
                        throw new ODataException(ODataErrorStrings.Binder_IsNotValidEnumConstant(dottedIdentifierToken.Identifier));
                    }
                }
                else
                {
                    IEdmTypeReference edmTypeReference = UriEdmHelpers.FindTypeFromModel(state.Model, dottedIdentifierToken.Identifier, this.Resolver).ToTypeReference();
                    if (edmTypeReference is IEdmPrimitiveTypeReference || edmTypeReference is IEdmEnumTypeReference)
                    {
                        IEdmPrimitiveType childPrimitiveType = childType as IEdmPrimitiveType;
                        if (childPrimitiveType != null && dottedIdentifierToken.NextToken != null)
                        {
                            return(new SingleValueCastNode(singleValueNode, childPrimitiveType));
                        }
                        else
                        {
                            return(new ConstantNode(dottedIdentifierToken.Identifier, dottedIdentifierToken.Identifier));
                        }
                    }
                    else
                    {
                        throw new ODataException(ODataErrorStrings.CastBinder_ChildTypeIsNotEntity(dottedIdentifierToken.Identifier));
                    }
                }
            }

            // Check whether childType is a derived type of the type of its parent node
            UriEdmHelpers.CheckRelatedTo(parentType, childType);

            this.state.ParsedSegments.Add(new TypeSegment(childType, parentType, null));

            CollectionResourceNode parentAsCollection = parent as CollectionResourceNode;

            if (parentAsCollection != null)
            {
                return(new CollectionResourceCastNode(parentAsCollection, childStructuredType));
            }

            return(new SingleResourceCastNode(parentAsSingleResource, childStructuredType));
        }
Пример #8
0
        /// <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);
                }

                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 ExceptionUtil.CreatePropertyNotFoundException(segmentToken.Identifier, parent.GetEdmTypeReference().FullName());
                }

                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);
        }
Пример #9
0
 /// <summary>
 /// Constructs a <see cref="SingleComplexNode"/>.
 /// </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 SingleComplexNode(SingleResourceNode source, IEdmProperty property)
     : this(ExceptionUtils.CheckArgumentNotNull(source, "source").NavigationSource, property)
 {
     this.source = source;
 }
Пример #10
0
 /// <summary>
 /// Constructs a CollectionNavigationNode.
 /// </summary>
 /// <param name="source">The previous node in the path.</param>
 /// <param name="navigationProperty">The navigation property this node represents.</param>
 /// <param name="parsedSegments">The path segments parsed in path and query option.</param>
 internal CollectionNavigationNode(SingleResourceNode source, IEdmNavigationProperty navigationProperty, List <ODataPathSegment> parsedSegments)
     : this(ExceptionUtils.CheckArgumentNotNull(source, "source").NavigationSource, navigationProperty, parsedSegments)
 {
     this.source = source;
 }
Пример #11
0
 /// <summary>
 /// Creates a CollectionNavigationNode.
 /// </summary>
 /// <param name="source">The parent of this collection navigation node.</param>
 /// <param name="navigationProperty">The navigation property that defines the collection node.</param>
 /// <param name="bindingPath">The binding path of navigation property</param>
 /// <returns>The collection node.</returns>
 /// <exception cref="System.ArgumentNullException">Throws if the input source or navigation property is null.</exception>
 /// <exception cref="ArgumentException">Throws if the input navigation doesn't target a collection.</exception>
 public CollectionNavigationNode(SingleResourceNode source, IEdmNavigationProperty navigationProperty, IEdmPathExpression bindingPath)
     : this(ExceptionUtils.CheckArgumentNotNull(source, "source").NavigationSource, navigationProperty, bindingPath)
 {
     this.source = source;
 }