Пример #1
0
 /// <summary>Copy constructor.</summary>
 /// <param name="other">Another <see cref="SegmentInfo"/> to get a shallow copy of.</param>
 internal SegmentInfo(SegmentInfo other)
 {
     Debug.Assert(other != null, "other != null");
     this.Identifier = other.Identifier;
     this.Key = other.Key;
     this.Operation = other.Operation;
     this.ProjectedProperty = other.ProjectedProperty;
     this.RequestExpression = other.RequestExpression;
     this.RequestEnumerable = other.RequestEnumerable;
     this.SingleResult = other.SingleResult;
     this.TargetResourceSet = other.TargetResourceSet;
     this.TargetKind = other.TargetKind;
     this.TargetSource = other.TargetSource;
     this.TargetResourceType = other.TargetResourceType;
 }
Пример #2
0
        /// <summary>
        /// Gets the query root for the segment.
        /// </summary>
        /// <param name="segment">Segment to compose the query.</param>
        /// <param name="service">The data service instance.</param>
        /// <param name="isLastSegment">true if <paramref name="segment"/> is the last segment; false otherwise.</param>
        /// <param name="checkRights">true if we need to check rights for this segment; false otherwise.</param>
        private static void ComposeExpressionForEntitySet(SegmentInfo segment, IDataService service, bool isLastSegment, bool checkRights)
        {
            Debug.Assert(
                segment != null && segment.TargetResourceSet != null && segment.TargetSource == RequestTargetSource.EntitySet,
                "segment != null && segment.TargetContainer != null && segment.TargetSource == RequestTargetSource.EntitySet");
            Debug.Assert(service != null, "service != null");

            bool hasKeyValues = segment.HasKeyValues;
            if (ShouldRequestQuery(service, isLastSegment, false, hasKeyValues))
            {
                segment.RequestExpression = service.Provider.GetQueryRootForResourceSet(segment.TargetResourceSet);
                Debug.Assert(segment.RequestExpression != null, "segment.RequestExpression != null");
            }

            if (hasKeyValues)
            {
                ApplyKeyToExpression(segment);
            }

            if (checkRights)
            {
                DataServiceConfiguration.CheckResourceRightsForRead(segment.TargetResourceSet, segment.SingleResult);
            }

            if (segment.RequestExpression != null)
            {
                // We only need to invoke the query interceptor if we called get query root.
                segment.RequestExpression = DataServiceConfiguration.ComposeResourceContainer(service, segment.TargetResourceSet, segment.RequestExpression);
            }
        }
Пример #3
0
        /// <summary>
        /// Create a new request description from the given request description and new entity as the result.
        /// </summary>
        /// <param name="description">Existing request description.</param>
        /// <param name="entity">entity that needs to be the result of the new request.</param>
        /// <param name="targetResourceType">The new target resource type for the new request description.</param>
        /// <returns>a new instance of request description containing information about the given entity.</returns>
        internal static RequestDescription CreateSingleResultRequestDescription(RequestDescription description, object entity, ResourceType targetResourceType)
        {
            // Create a new request description for the results that will be returned.
            SegmentInfo segmentInfo = new SegmentInfo
            {
                RequestExpression = Expression.Constant(entity),
                RequestEnumerable = new[] { entity },
                TargetKind = description.TargetKind,
                TargetSource = description.TargetSource,
                SingleResult = true,
                ProjectedProperty = description.Property,
                TargetResourceType = targetResourceType,
                TargetResourceSet = description.LastSegmentInfo.TargetResourceSet,
                Identifier = description.LastSegmentInfo.Identifier
            };
#if DEBUG
            segmentInfo.AssertValid();
#endif
            IList<SegmentInfo> segmentInfos = description.SegmentInfos;
            segmentInfos[segmentInfos.Count - 1] = segmentInfo;

            RequestDescription resultDescription = new RequestDescription(segmentInfos, description.ResultUri);
            resultDescription.CopyFrom(description);

            return resultDescription;
        }
        /// <summary>
        /// Creates a property segment
        /// </summary>
        /// <param name="previous">previous segment info.</param>
        /// <param name="property">property to create the segment for.</param>
        /// <returns>new segment for the given property.</returns>
        private SegmentInfo CreatePropertySegment(SegmentInfo previous, ResourceProperty property)
        {
            // Handle a strongly-typed property.
            SegmentInfo segment = new SegmentInfo() { Identifier = property.Name, ProjectedProperty = property };
            segment.TargetResourceType = property.ResourceType;
            ResourcePropertyKind propertyKind = property.Kind;
            segment.SingleResult = (propertyKind != ResourcePropertyKind.ResourceSetReference);
            segment.TargetSource = RequestTargetSource.Property;

            if (previous.TargetKind == RequestTargetKind.Link && property.TypeKind != ResourceTypeKind.EntityType)
            {
                throw DataServiceException.CreateBadRequestError(Strings.RequestUriProcessor_LinkSegmentMustBeFollowedByEntitySegment(segment.Identifier, XmlConstants.UriLinkSegment));
            }

            switch (propertyKind)
            {
                case ResourcePropertyKind.ComplexType:
                    segment.TargetKind = RequestTargetKind.ComplexObject;
                    break;
                case ResourcePropertyKind.Collection:
                    segment.TargetKind = RequestTargetKind.Collection;
                    break;
                case ResourcePropertyKind.ResourceReference:
                case ResourcePropertyKind.ResourceSetReference:
                    segment.TargetKind = RequestTargetKind.Resource;
                    segment.TargetResourceSet = this.providerWrapper.GetResourceSet(previous.TargetResourceSet, previous.TargetResourceType, property);
                    if (segment.TargetResourceSet == null)
                    {
                        throw DataServiceException.CreateResourceNotFound(property.Name);
                    }

                    break;
                default:
                    Debug.Assert(property.IsOfKind(ResourcePropertyKind.Primitive), "must be primitive type property");
                    segment.TargetKind = RequestTargetKind.Primitive;
                    break;
            }

            return segment;
        }
        /// <summary>
        /// Creates the next segment.
        /// </summary>
        /// <param name="previous">The previous segment.</param>
        /// <param name="segment">The the next segment.</param>
        /// <returns>The newly created next segment.</returns>
        private SegmentInfo CreateNextSegment(SegmentInfo previous, ODataPathSegment segment)
        {
            if (segment is ValueSegment)
            {
                return CreateValueSegment(previous);
            }

            SegmentInfo segmentInfo;
            if (segment is CountSegment)
            {
                segmentInfo = CreateCountSegment(previous);

                // DEVNOTE: Prior to refactor, $count was handled alongside properties. See more detailed comment in HandleCountSegment
                Debug.Assert(segmentInfo.TargetSource == RequestTargetSource.Property, "segment.TargetSource == RequestTargetSource.Property");
                return segmentInfo;
            }

            // if its not one of the recognized special segments, then it must be a property, type-segment, or key value.
            Debug.Assert(
                previous.TargetKind == RequestTargetKind.ComplexObject
                || previous.TargetKind == RequestTargetKind.Resource
                || previous.TargetKind == RequestTargetKind.OpenProperty
                || previous.TargetKind == RequestTargetKind.Link,
                "previous.TargetKind(" + previous.TargetKind + ") can have properties");

            CheckSegmentIsComposable(previous);

            // if the segment corresponds to a declared property, handle it
            // otherwise, fall back to type-segments, actions, and dynamic/open properties
            ResourceProperty projectedProperty;
            if (TryGetPropertyFromSegment(segment, out projectedProperty))
            {
                CheckSingleResult(previous.SingleResult, previous.Identifier);

                if (projectedProperty.IsOfKind(ResourcePropertyKind.Stream))
                {
                    segmentInfo = CreateNamedStreamSegment(previous, projectedProperty);
                    Debug.Assert(segmentInfo.TargetSource == RequestTargetSource.Property, "segment.TargetSource == RequestTargetSource.Property");
                    return segmentInfo;
                }

                segmentInfo = this.CreatePropertySegment(previous, projectedProperty);
                Debug.Assert(segmentInfo.TargetSource == RequestTargetSource.Property, "segment.TargetSource == RequestTargetSource.Property");
                return segmentInfo;
            }

            // If the property resolution failed, and the previous segment was targeting an entity, then we should
            // try and resolve the identifier as type name.
            SegmentInfo typeNameSegment;
            if (this.TryCreateTypeNameSegment(previous, segment, out typeNameSegment))
            {
                Debug.Assert(typeNameSegment.TargetSource == previous.TargetSource, "segment.TargetSource == previous.TargetSource");
                return typeNameSegment;
            }

            OperationWrapper serviceAction = null;
            var actionSegment = segment as OperationSegment;
            if (actionSegment != null)
            {
                Debug.Assert(actionSegment.Operations.Count() == 1, "Operation segment should only ever have exactly one operation. Was a change made to how MetadataProviderEdmModel finds actions/service operations");
                serviceAction = ((MetadataProviderEdmOperation)actionSegment.Operations.Single()).ServiceOperation;
                Debug.Assert(serviceAction != null, "serviceAction != null");
                Debug.Assert(serviceAction.Kind == OperationKind.Action, "serviceAction.Kind == OperationKind.Action");
            }

            if (serviceAction != null)
            {
                Debug.Assert(serviceAction.Kind == OperationKind.Action, "serviceAction.Kind == OperationKind.Action");

                // Service Actions can bind to a set with any ResourceSetRights except ResourceSetRights.None.
                segmentInfo = this.CreateSegmentForServiceAction(previous, serviceAction);
                Debug.Assert(segmentInfo.TargetSource == RequestTargetSource.ServiceOperation, "segment.TargetSource == RequestTargetSource.ServiceOperation");
                return segmentInfo;
            }

            CheckSingleResult(previous.SingleResult, previous.Identifier);
            segmentInfo = CreateOpenPropertySegment(previous, ((OpenPropertySegment)segment).PropertyName);
            Debug.Assert(segmentInfo.TargetSource == RequestTargetSource.Property, "segment.TargetSource == RequestTargetSource.Property");
            return segmentInfo;
        }
        /// <summary>Creates the first <see cref="SegmentInfo"/> for a request.</summary>
        /// <param name="segment">The text of the segment.</param>
        /// <returns>A description of the information on the segment.</returns>
        private SegmentInfo CreateFirstSegment(ODataPathSegment segment)
        {
            Debug.Assert(segment != null, "identifier != null");

            // Look for well-known system entry points.
            if (segment is MetadataSegment)
            {
                return new SegmentInfo { Identifier = XmlConstants.UriMetadataSegment, TargetKind = RequestTargetKind.Metadata };
            }

            if (segment is BatchSegment)
            {
                return new SegmentInfo { Identifier = XmlConstants.UriBatchSegment, TargetKind = RequestTargetKind.Batch };
            }

            if (segment is CountSegment)
            {
                // $count on root: throw
                throw DataServiceException.CreateResourceNotFound(Strings.RequestUriProcessor_CountOnRoot);
            }

            // Look for a service operation.
            OperationImportSegment serviceOperation = segment as OperationImportSegment;
            if (serviceOperation != null)
            {
                Debug.Assert(serviceOperation.OperationImports.Count() == 1, "Operation import segment should only ever have exactly one operation. Was a change made to how MetadataProviderEdmModel finds actions/service operations");
                var operationImport = serviceOperation.OperationImports.Single();
                var operation = ((MetadataProviderEdmOperationImport)operationImport).ServiceOperation;
                Debug.Assert(operation != null, "operation != null");

                if (operation.Kind == OperationKind.ServiceOperation)
                {
                    return CreateSegmentForServiceOperation(operation);
                }

                Debug.Assert(operation.Kind == OperationKind.Action, "serviceAction.Kind == OperationKind.Action");
                return this.CreateSegmentForServiceAction(null /*previousSegment*/, operation);
            }

            var batchReferenceSegment = segment as BatchReferenceSegment;
            if (batchReferenceSegment != null)
            {
                SegmentInfo referencedSegmentInfo = this.crossReferenceCallback(batchReferenceSegment.ContentId);
                Debug.Assert(referencedSegmentInfo != null, "Could not find SegmentInfo for content-id: " + batchReferenceSegment.ContentId);
                referencedSegmentInfo.Identifier = batchReferenceSegment.ContentId;
                return referencedSegmentInfo;
            }

            // Look for an entity set.
            EntitySetSegment entitySetSegment = segment as EntitySetSegment;
            if (entitySetSegment != null)
            {
                var container = ((IResourceSetBasedEdmEntitySet)entitySetSegment.EntitySet).ResourceSet;
                Debug.Assert(container != null, "container != null");
                SegmentInfo segmentInfo = new SegmentInfo
                {
                    Identifier = container.Name,
                    TargetResourceSet = container,
                    TargetResourceType = container.ResourceType,
                    TargetSource = RequestTargetSource.EntitySet,
                    TargetKind = RequestTargetKind.Resource,
                    SingleResult = false
                };

                return segmentInfo;
            }

            WebUtil.CheckResourceExists(false, segment.ToString());
            return null;
        }
        /// <summary>
        /// Creates a new segment for an open property.
        /// </summary>
        /// <param name="previous">previous segment info.</param>
        /// <param name="identifier">name of the segment.</param>
        /// <returns>new open property segment.</returns>
        private static SegmentInfo CreateOpenPropertySegment(SegmentInfo previous, string identifier)
        {
            SegmentInfo segment = new SegmentInfo { Identifier = identifier };

            // Handle an open type property. If the current leaf isn't an 
            // object (which implies it's already an open type), then
            // it should be marked as an open type.
            if (previous.TargetResourceType != null)
            {
                WebUtil.CheckResourceExists(previous.TargetResourceType.IsOpenType, segment.Identifier);
            }

            // Open navigation properties are not supported on OpenTypes.  We should throw on the following cases:
            // 1. the segment before $ref is always a navigation property pointing to a resource
            // 2. if the segment hasQuery, it is pointing to a resource
            // 3. if this is a POST operation, the target has to be either a set of links or an entity set
            if (previous.TargetKind == RequestTargetKind.Link)
            {
                throw DataServiceException.CreateBadRequestError(Strings.OpenNavigationPropertiesNotSupportedOnOpenTypes(segment.Identifier));
            }

            segment.TargetSource = RequestTargetSource.Property;
            segment.TargetResourceType = null;
            segment.TargetKind = RequestTargetKind.OpenProperty;
            segment.SingleResult = true;
            return segment;
        }
        /// <summary>
        /// Handle $ref segment
        /// </summary>
        /// <param name="previous">previous segment info.</param>
        /// <returns>The links segment info</returns>
        private static SegmentInfo CreateEntityReferenceLinkSegment(SegmentInfo previous)
        {
            Debug.Assert(previous.TargetKind == RequestTargetKind.Resource, "Can we ever get here?");
            WebUtil.CheckSyntaxValid(previous.TargetKind == RequestTargetKind.Resource);
            CheckSingleResult(previous.SingleResult, previous.Identifier);

            return new SegmentInfo(previous) { Identifier = XmlConstants.UriLinkSegment, TargetKind = RequestTargetKind.Link, };
        }
Пример #9
0
        /// <summary>
        /// Creates the next segment.
        /// </summary>
        /// <param name="previous">The previous segment.</param>
        /// <param name="segment">The the next segment.</param>
        /// <returns>The newly created next segment.</returns>
        private SegmentInfo CreateNextSegment(SegmentInfo previous, ODataPathSegment segment)
        {
            if (segment is ValueSegment)
            {
                return(CreateValueSegment(previous));
            }

            SegmentInfo segmentInfo;

            if (segment is CountSegment)
            {
                segmentInfo = CreateCountSegment(previous);

                // DEVNOTE: Prior to refactor, $count was handled alongside properties. See more detailed comment in HandleCountSegment
                Debug.Assert(segmentInfo.TargetSource == RequestTargetSource.Property, "segment.TargetSource == RequestTargetSource.Property");
                return(segmentInfo);
            }

            // if its not one of the recognized special segments, then it must be a property, type-segment, or key value.
            Debug.Assert(
                previous.TargetKind == RequestTargetKind.ComplexObject ||
                previous.TargetKind == RequestTargetKind.Resource ||
                previous.TargetKind == RequestTargetKind.OpenProperty ||
                previous.TargetKind == RequestTargetKind.Link,
                "previous.TargetKind(" + previous.TargetKind + ") can have properties");

            CheckSegmentIsComposable(previous);

            // if the segment corresponds to a declared property, handle it
            // otherwise, fall back to type-segments, actions, and dynamic/open properties
            ResourceProperty projectedProperty;

            if (TryGetPropertyFromSegment(segment, out projectedProperty))
            {
                CheckSingleResult(previous.SingleResult, previous.Identifier);

                if (projectedProperty.IsOfKind(ResourcePropertyKind.Stream))
                {
                    segmentInfo = CreateNamedStreamSegment(previous, projectedProperty);
                    Debug.Assert(segmentInfo.TargetSource == RequestTargetSource.Property, "segment.TargetSource == RequestTargetSource.Property");
                    return(segmentInfo);
                }

                segmentInfo = this.CreatePropertySegment(previous, projectedProperty);
                Debug.Assert(segmentInfo.TargetSource == RequestTargetSource.Property, "segment.TargetSource == RequestTargetSource.Property");
                return(segmentInfo);
            }

            // If the property resolution failed, and the previous segment was targeting an entity, then we should
            // try and resolve the identifier as type name.
            SegmentInfo typeNameSegment;

            if (this.TryCreateTypeNameSegment(previous, segment, out typeNameSegment))
            {
                Debug.Assert(typeNameSegment.TargetSource == previous.TargetSource, "segment.TargetSource == previous.TargetSource");
                return(typeNameSegment);
            }

            OperationWrapper serviceAction = null;
            var actionSegment = segment as OperationSegment;

            if (actionSegment != null)
            {
                Debug.Assert(actionSegment.Operations.Count() == 1, "Operation segment should only ever have exactly one operation. Was a change made to how MetadataProviderEdmModel finds actions/service operations");
                serviceAction = ((MetadataProviderEdmOperation)actionSegment.Operations.Single()).ServiceOperation;
                Debug.Assert(serviceAction != null, "serviceAction != null");
                Debug.Assert(serviceAction.Kind == OperationKind.Action, "serviceAction.Kind == OperationKind.Action");
            }

            if (serviceAction != null)
            {
                Debug.Assert(serviceAction.Kind == OperationKind.Action, "serviceAction.Kind == OperationKind.Action");

                // Service Actions can bind to a set with any ResourceSetRights except ResourceSetRights.None.
                segmentInfo = this.CreateSegmentForServiceAction(previous, serviceAction);
                Debug.Assert(segmentInfo.TargetSource == RequestTargetSource.ServiceOperation, "segment.TargetSource == RequestTargetSource.ServiceOperation");
                return(segmentInfo);
            }

            CheckSingleResult(previous.SingleResult, previous.Identifier);
            segmentInfo = CreateOpenPropertySegment(previous, ((OpenPropertySegment)segment).PropertyName);
            Debug.Assert(segmentInfo.TargetSource == RequestTargetSource.Property, "segment.TargetSource == RequestTargetSource.Property");
            return(segmentInfo);
        }
Пример #10
0
        /// <summary>Creates the first <see cref="SegmentInfo"/> for a request.</summary>
        /// <param name="segment">The text of the segment.</param>
        /// <returns>A description of the information on the segment.</returns>
        private SegmentInfo CreateFirstSegment(ODataPathSegment segment)
        {
            Debug.Assert(segment != null, "identifier != null");

            // Look for well-known system entry points.
            if (segment is MetadataSegment)
            {
                return(new SegmentInfo {
                    Identifier = XmlConstants.UriMetadataSegment, TargetKind = RequestTargetKind.Metadata
                });
            }

            if (segment is BatchSegment)
            {
                return(new SegmentInfo {
                    Identifier = XmlConstants.UriBatchSegment, TargetKind = RequestTargetKind.Batch
                });
            }

            if (segment is CountSegment)
            {
                // $count on root: throw
                throw DataServiceException.CreateResourceNotFound(Strings.RequestUriProcessor_CountOnRoot);
            }

            // Look for a service operation.
            OperationImportSegment serviceOperation = segment as OperationImportSegment;

            if (serviceOperation != null)
            {
                Debug.Assert(serviceOperation.OperationImports.Count() == 1, "Operation import segment should only ever have exactly one operation. Was a change made to how MetadataProviderEdmModel finds actions/service operations");
                var operationImport = serviceOperation.OperationImports.Single();
                var operation       = ((MetadataProviderEdmOperationImport)operationImport).ServiceOperation;
                Debug.Assert(operation != null, "operation != null");

                if (operation.Kind == OperationKind.ServiceOperation)
                {
                    return(CreateSegmentForServiceOperation(operation));
                }

                Debug.Assert(operation.Kind == OperationKind.Action, "serviceAction.Kind == OperationKind.Action");
                return(this.CreateSegmentForServiceAction(null /*previousSegment*/, operation));
            }

            var batchReferenceSegment = segment as BatchReferenceSegment;

            if (batchReferenceSegment != null)
            {
                SegmentInfo referencedSegmentInfo = this.crossReferenceCallback(batchReferenceSegment.ContentId);
                Debug.Assert(referencedSegmentInfo != null, "Could not find SegmentInfo for content-id: " + batchReferenceSegment.ContentId);
                referencedSegmentInfo.Identifier = batchReferenceSegment.ContentId;
                return(referencedSegmentInfo);
            }

            // Look for an entity set.
            EntitySetSegment entitySetSegment = segment as EntitySetSegment;

            if (entitySetSegment != null)
            {
                var container = ((IResourceSetBasedEdmEntitySet)entitySetSegment.EntitySet).ResourceSet;
                Debug.Assert(container != null, "container != null");
                SegmentInfo segmentInfo = new SegmentInfo
                {
                    Identifier         = container.Name,
                    TargetResourceSet  = container,
                    TargetResourceType = container.ResourceType,
                    TargetSource       = RequestTargetSource.EntitySet,
                    TargetKind         = RequestTargetKind.Resource,
                    SingleResult       = false
                };

                return(segmentInfo);
            }

            WebUtil.CheckResourceExists(false, segment.ToString());
            return(null);
        }
Пример #11
0
        /// <summary>
        /// Validates the binding parameter and reads the payload parameters for the given action.
        /// </summary>
        /// <param name="dataService">Data service instance.</param>
        /// <param name="actionSegment">The segment for the action whose parameters is being read.</param>
        /// <param name="previousSegment">The segment before the action.</param>
        /// <returns>A new Expression[] with parameter values.</returns>
        private static Expression[] ValidateBindingParameterAndReadPayloadParametersForAction(IDataService dataService, SegmentInfo actionSegment, SegmentInfo previousSegment)
        {
            Debug.Assert(dataService != null, "dataService != null");
            Debug.Assert(actionSegment != null, "actionSegment != null");

            OperationWrapper operation = actionSegment.Operation;
            Debug.Assert(operation != null, "operation != null");
            Debug.Assert(operation.Kind == OperationKind.Action, "operation.Kind == OperationKind.Action");

            int idx = 0;
            int parametersCount = operation.Parameters.Count;
            Expression[] operationParameters = new Expression[parametersCount];
            if (previousSegment != null)
            {
                if (operation.BindingParameter == null)
                {
                    throw DataServiceException.CreateBadRequestError(Strings.RequestUriProcessor_UnbindableOperationsMustBeCalledAtRootLevel(operation.Name));
                }

                ResourceType previousSegmentType = previousSegment.TargetResourceType;

                // We throw in HandleOpenPropertySegment() when we see an open property on a POST operation. previousSegmentType should not be null here.
                Debug.Assert(previousSegmentType != null, "previousSegmentType != null");

                ResourceType bindingParameterType = operation.BindingParameter.ParameterType;
                Debug.Assert(bindingParameterType != null, "bindingParameterType != null");
                Debug.Assert(
                    bindingParameterType.ResourceTypeKind == ResourceTypeKind.EntityType || bindingParameterType.ResourceTypeKind == ResourceTypeKind.EntityCollection,
                    "The ServiceAction constructor requires the binding parameter type to be entity or entity collection.");

                // We need to make sure the item type of the binding parameter is assignable from the item type of the previous segment and
                // we need to make sure the cardinality of the binding parameter maches that of the previous segment.
                bool bindingParameterIsCollection;
                ResourceType bindingParameterItemType = RequestUriProcessor.GetItemTypeFromResourceType(bindingParameterType, out bindingParameterIsCollection);
                Debug.Assert(
                    !bindingParameterIsCollection || bindingParameterType.ResourceTypeKind == ResourceTypeKind.EntityCollection || bindingParameterType.ResourceTypeKind == ResourceTypeKind.Collection,
                    "!bindingParameterIsCollection || bindingParameterType.ResourceTypeKind == ResourceTypeKind.EntityCollection || bindingParameterType.ResourceTypeKind == ResourceTypeKind.Collection");

                bool previousSegmentIsCollection;
                ResourceType previousSegmentItemType = RequestUriProcessor.GetItemTypeFromResourceType(previousSegmentType, out previousSegmentIsCollection);
                Debug.Assert(
                    !previousSegmentIsCollection || previousSegmentType.ResourceTypeKind == ResourceTypeKind.EntityCollection || previousSegmentType.ResourceTypeKind == ResourceTypeKind.Collection,
                    "!previousSegmentIsCollection || previousSegmentType.ResourceTypeKind == ResourceTypeKind.EntityCollection || previousSegmentType.ResourceTypeKind == ResourceTypeKind.Collection");

                // If previousSegment is Collection, previousSegment.SingleResult is false but previousSegmentIsCollection is true because previousSegmentType is of Collection type.
                // If previousSegment is a ResourceSetReference, its TargetResourceType is the item type of the set rather than the entity collection type so previousSegmentIsCollection
                // is false. previousSegment.SingleResult is true and gives us the cardinality in that case. 
                previousSegmentIsCollection = previousSegmentIsCollection || !previousSegment.SingleResult;

                if (bindingParameterIsCollection != previousSegmentIsCollection || !bindingParameterItemType.IsAssignableFrom(previousSegmentItemType))
                {
                    throw DataServiceException.CreateBadRequestError(Strings.RequestUriProcessor_BindingParameterNotAssignableFromPreviousSegment(operation.Name, previousSegment.Identifier));
                }

                operationParameters[idx++] = previousSegment.RequestExpression;
            }
            else
            {
                // No previous segment - throw if the operation has a binding parameter
                if (operation.BindingParameter != null)
                {
                    throw DataServiceException.CreateBadRequestError(Strings.RequestUriProcessor_MissingBindingParameter(operation.Name));
                }
            }

            // Try to read parameters from the payload if:
            // 1) We are expecting parameters in the payload, i.e. we have more than 1 non-binding parameters.
            // 2) We don't expect parameters in the payload but content type is not null. In this case if the payload is empty, ODataParameterReader
            //    will return no more parameters. Otherwise ODataParameterReader will throw when there is any unexpected parameter on the payload.
            if (idx < parametersCount || !string.IsNullOrEmpty(dataService.OperationContext.RequestMessage.ContentType))
            {
                Dictionary<string, object> parameters = Deserializer.ReadPayloadParameters(actionSegment, dataService);
                for (; idx < parametersCount; idx++)
                {
                    string parameterName = operation.Parameters[idx].Name;
                    object parameterValue;
                    if (!parameters.TryGetValue(parameterName, out parameterValue))
                    {
                        Debug.Fail("ODataParameterReader should throw when there is any missing parameter.");
                    }

                    operationParameters[idx] = Expression.Constant(parameterValue, typeof(object));
                }
            }

            return operationParameters;
        }
Пример #12
0
        /// <summary>
        /// Composes the query expression for properties
        /// </summary>
        /// <param name="segment">Segment to compose the expression for.</param>
        /// <param name="previous">The previous segment.</param>
        /// <param name="service">The data service instance.</param>
        /// <param name="lastSegment">true if <paramref name="segment"/> is also the last segment; false otherwise.</param>
        /// <param name="checkRights">true if we need to check rights for this segment; false otherwise.</param>
        private static void ComposeExpressionForProperty(SegmentInfo segment, SegmentInfo previous, IDataService service, bool lastSegment, bool checkRights)
        {
            Debug.Assert(segment != null && segment.ProjectedProperty != null, "segment != null && segment.ProjectedProperty != null");

            if (segment.ProjectedProperty.CanReflectOnInstanceTypeProperty)
            {
                segment.RequestExpression = segment.ProjectedProperty.Kind == ResourcePropertyKind.ResourceSetReference ?
                    SelectMultiple(previous.RequestExpression, segment.ProjectedProperty) :
                    SelectElement(previous.RequestExpression, segment.ProjectedProperty);
            }
            else
            {
                segment.RequestExpression = segment.ProjectedProperty.Kind == ResourcePropertyKind.ResourceSetReference ?
                    SelectLateBoundPropertyMultiple(
                        previous.RequestExpression,
                        segment.ProjectedProperty) :
                    SelectLateBoundProperty(
                        previous.RequestExpression,
                        segment.ProjectedProperty);
            }

            if (segment.HasKeyValues)
            {
                ApplyKeyToExpression(segment);
            }

            // Do security checks and authorization query composition.
            if (segment.TargetResourceSet != null)
            {
                if (checkRights)
                {
                    DataServiceConfiguration.CheckResourceRightsForRead(segment.TargetResourceSet, segment.SingleResult);
                }

                if (ShouldRequestQuery(service, lastSegment, previous.TargetKind == RequestTargetKind.Link, segment.HasKeyValues))
                {
                    segment.RequestExpression = DataServiceConfiguration.ComposeResourceContainer(service, segment.TargetResourceSet, segment.RequestExpression);
                }
            }
        }
Пример #13
0
        /// <summary>
        /// Compose the query expression for a type name segment.
        /// </summary>
        /// <param name="segment">The type name segment.</param>
        /// <param name="previous">The previous segment.</param>
        private static void ComposeExpressionForTypeNameSegment(SegmentInfo segment, SegmentInfo previous)
        {
            Debug.Assert(segment != null && segment.IsTypeIdentifierSegment, "segment != null && segment.IsTypeIdentifierSegment");
            Debug.Assert(previous != null, "previous != null");

            segment.RequestExpression = SelectDerivedResourceType(previous.RequestExpression, segment.TargetResourceType);
            if (segment.HasKeyValues)
            {
                ApplyKeyToExpression(segment);
            }
        }
Пример #14
0
        /// <summary>
        /// Invokes the service action for the segment.
        /// </summary>
        /// <param name="segment">Segment info for the service action.</param>
        /// <param name="previousSegment">Previous segment.</param>
        /// <param name="service">Data service instance.</param>
        private static void ComposeExpressionForServiceAction(SegmentInfo segment, SegmentInfo previousSegment, IDataService service)
        {
            Debug.Assert(
                segment != null && segment.Operation != null && segment.Operation.Kind == OperationKind.Action,
                "segment != null && segment.Operation != null && segment.Operation.Kind == OperationKind.Action");

            Expression[] parameterTokens = ValidateBindingParameterAndReadPayloadParametersForAction(service, segment, previousSegment);
            segment.RequestExpression = service.ActionProvider.CreateInvokable(segment.Operation, parameterTokens);
            Debug.Assert(segment.RequestExpression != null, "segment.RequestExpression != null");
        }
Пример #15
0
        /// <summary>
        /// Invokes the service operation for the segment.
        /// </summary>
        /// <param name="segment">The segment</param>
        /// <param name="service">The service instance</param>
        /// <param name="checkRights">true if we need to check rights for the operation; false otherwise.</param>
        /// <param name="lastSegment">the last segment of the request.</param>
        private static void ComposeExpressionForServiceOperation(SegmentInfo segment, IDataService service, bool checkRights, SegmentInfo lastSegment)
        {
            Debug.Assert(
                segment != null && segment.Operation != null && segment.Operation.Kind == OperationKind.ServiceOperation && segment.TargetSource == RequestTargetSource.ServiceOperation,
                "The segment must be a service operation segment.");
            Debug.Assert(service != null, "service != null");
            bool lastSegmentIsServiceAction = false;

            if (checkRights)
            {
                segment.CheckSegmentRights();
            }

            if (segment != lastSegment)
            {
                lastSegmentIsServiceAction = lastSegment.IsServiceActionSegment;
                if (segment.Operation.Method == XmlConstants.HttpMethodPost && lastSegmentIsServiceAction)
                {
                    // An action cannot be composed from a WebInvoke service operation.
                    throw DataServiceException.CreateBadRequestError(Strings.RequestUriProcessor_ActionComposedWithWebInvokeServiceOperationNotAllowed);
                }
            }

            if (!lastSegmentIsServiceAction && service.OperationContext.RequestMethod != segment.Operation.Method)
            {
                throw DataServiceException.CreateMethodNotAllowed(Strings.RequestUriProcessor_MethodNotAllowed, segment.Operation.Method);
            }

            object[] operationParameters = ReadOperationParameters(service.OperationContext.RequestMessage, segment.Operation);
            ConstantExpression methodResult = null;
            switch (segment.Operation.ResultKind)
            {
                case ServiceOperationResultKind.QueryWithMultipleResults:
                case ServiceOperationResultKind.QueryWithSingleResult:
                    methodResult = service.Provider.InvokeServiceOperation(segment.Operation, operationParameters);
                    WebUtil.CheckResourceExists(methodResult.Value != null, segment.Identifier);
                    break;

                case ServiceOperationResultKind.DirectValue:
                case ServiceOperationResultKind.Enumeration:
                    methodResult = service.Provider.InvokeServiceOperation(segment.Operation, operationParameters);
                    Debug.Assert(methodResult != null, "methodResult != null");
                    WebUtil.CheckResourceExists(segment.SingleResult || methodResult.Value != null, segment.Identifier);   // Enumerations shouldn't be null.
                    break;

                default:
                    Debug.Assert(segment.Operation.ResultKind == ServiceOperationResultKind.Void, "segment.Operation.ResultKind == ServiceOperationResultKind.Nothing");
                    service.Provider.InvokeServiceOperation(segment.Operation, operationParameters);
                    break;
            }

            segment.RequestExpression = methodResult;
            if (segment.RequestExpression != null)
            {
                if (segment.HasKeyValues)
                {
                    // key predicates should be applied after operation is invoked
                    ApplyKeyToExpression(segment);
                }
            }
        }
 /// <summary>
 /// Determines if the previous segment can be composed upon.
 /// </summary>
 /// <param name="previous">previous segment info.</param>
 /// <returns>
 ///   <c>true</c> if the segment can be composed upon; otherwise, <c>false</c>.
 /// </returns>
 private static bool IsSegmentComposable(SegmentInfo previous)
 {
     OperationWrapper op = previous.Operation;
     return op == null || 
            (op.ResultKind != ServiceOperationResultKind.Enumeration 
             && op.ResultKind != ServiceOperationResultKind.DirectValue 
             && (op.ResultKind != ServiceOperationResultKind.QueryWithSingleResult
                 || op.ResultType.ResourceTypeKind == ResourceTypeKind.EntityType));
 }
        /// <summary>
        /// Handle $count segment
        /// </summary>
        /// <param name="previous">previous segment info.</param>
        /// <returns>The count segment info</returns>
        private static SegmentInfo CreateCountSegment(SegmentInfo previous)
        {
            CheckSegmentIsComposable(previous);

            if ((previous.TargetKind != RequestTargetKind.Resource || previous.SingleResult) && previous.TargetKind != RequestTargetKind.Collection)
            {
                throw DataServiceException.CreateResourceNotFound(Strings.RequestUriProcessor_CountNotSupported(previous.Identifier));
            }

            var segment = new SegmentInfo();
            segment.Identifier = XmlConstants.UriCountSegment;

            // DEVNOTE: prior to refactoring, $count was handled alongside properties
            // TODO: introduce a new target-source value or otherwise refactor this so that
            // $count is not treated like a property. Using the previous segment's kind
            // becomes problematic for service operations because the current segment
            // will not know the specific operation.
            segment.TargetSource = RequestTargetSource.Property;

            segment.SingleResult = true;
            segment.TargetKind = RequestTargetKind.PrimitiveValue;
            segment.TargetResourceType = previous.TargetResourceType;
            segment.TargetResourceSet = previous.TargetResourceSet;

            return segment;
        }
Пример #18
0
        /// <summary>
        /// Tries to create a type name segment if the given identifier refers to a known type.
        /// </summary>
        /// <param name="previous">previous segment info.</param>
        /// <param name="segment">The segment being interpreted.</param>
        /// <param name="typeNameSegment">The type name segment, if one was created.</param>
        /// <returns>Whether or not a type segment was created for the identifier.</returns>
        private bool TryCreateTypeNameSegment(SegmentInfo previous, ODataPathSegment segment, out SegmentInfo typeNameSegment)
        {
            var typeSegment = segment as TypeSegment;

            if (typeSegment == null || previous.TargetResourceSet == null)
            {
                typeNameSegment = null;
                return(false);
            }

            ResourceType targetResourceType = MetadataProviderUtils.GetResourceType(typeSegment);

            // if the new type segment prevents any results from possibly being returned, then short-circuit and throw a 404.
            ResourceType previousResourceType = previous.TargetResourceType;

            Debug.Assert(previousResourceType != null, "previous.TargetResourceType != null");
            if (!targetResourceType.IsAssignableFrom(previousResourceType) && !previousResourceType.IsAssignableFrom(targetResourceType))
            {
                throw DataServiceException.CreateBadRequestError(Strings.RequestUriProcessor_InvalidTypeIdentifier_UnrelatedType(targetResourceType.FullName, previousResourceType.FullName));
            }

            // Since we allow derived navigation properties or named streams in V1/V2, the server will generate edit links and navigation links with type segment in it.
            // Hence we need to be able to process type segment in the request even when the server MPV is set to V1/V2. But we do not want to expose new functionality
            // like filtering collections based on type, etc on V1/V2 servers. Hence only checking for MPV to be v3 or greater if the previous segment is a collection
            if (!previous.SingleResult)
            {
                VersionUtil.CheckMaxProtocolVersion(VersionUtil.Version4Dot0, this.maxProtocolVersion);
            }

            typeNameSegment = new SegmentInfo
            {
                Identifier         = targetResourceType.FullName,
                Operation          = previous.Operation,
                TargetKind         = previous.TargetKind,
                TargetSource       = previous.TargetSource,
                TargetResourceType = targetResourceType,
                SingleResult       = previous.SingleResult,
                TargetResourceSet  = previous.TargetResourceSet,
                ProjectedProperty  = previous.ProjectedProperty,
                Key = previous.Key,
                RequestExpression       = previous.RequestExpression,
                RequestEnumerable       = previous.RequestEnumerable,
                IsTypeIdentifierSegment = true
            };

            return(true);
        }
        /// <summary>
        /// Create a $value segment
        /// </summary>
        /// <param name="previous">previous segment info.</param>
        /// <returns>new segement info for $value.</returns>
        private static SegmentInfo CreateValueSegment(SegmentInfo previous)
        {
            SegmentInfo segment;
            if (previous.TargetKind == RequestTargetKind.Primitive)
            {
                segment = new SegmentInfo(previous);
            }
            else
            {
                CheckSegmentIsComposable(previous);

                segment = new SegmentInfo
                {
                    TargetSource = RequestTargetSource.Property,
                    TargetResourceType = previous.TargetResourceType
                };
            }

            segment.Identifier = XmlConstants.UriValueSegment;
            CheckSingleResult(previous.SingleResult, previous.Identifier);

            segment.SingleResult = true;

            if (previous.TargetKind == RequestTargetKind.Primitive)
            {
                segment.TargetKind = RequestTargetKind.PrimitiveValue;
            }
            else if (previous.TargetKind == RequestTargetKind.OpenProperty)
            {
                segment.TargetKind = RequestTargetKind.OpenPropertyValue;
            }
            else
            {
                // If the previous segment is an entity, we expect it to be an MLE. We cannot validate our assumption
                // until later when we get the actual instance of the entity because the type hierarchy can contain
                // a mix of MLE and non-MLE types.
                segment.TargetKind = RequestTargetKind.MediaResource;
            }

            return segment;
        }
Пример #20
0
        /// <summary>Creates an <see cref="SegmentInfo"/> list for the given <paramref name="path"/>.</summary>
        /// <param name="path">Segments to process.</param>
        /// <returns>Segment information describing the given <paramref name="path"/>.</returns>
        internal IList <SegmentInfo> ConvertPath(ODataPath path)
        {
            Debug.Assert(path != null, "path != null");

            SegmentInfo        previous     = null;
            List <SegmentInfo> segmentInfos = new List <SegmentInfo>();
            bool crossReferencingUri        = false;

            foreach (ODataPathSegment segment in path)
            {
                crossReferencingUri |= segment is BatchReferenceSegment;

                SegmentInfo segmentInfo;
                if (previous == null)
                {
                    segmentInfo = this.CreateFirstSegment(segment);
                }
                else
                {
                    ThrowIfMustBeLeafSegment(previous);

                    var keySegment = segment as KeySegment;
                    if (keySegment != null)
                    {
                        // All service operations other than those returning IQueryable MUST NOT have a key expression.
                        if (!IsSegmentComposable(previous))
                        {
                            throw DataServiceException.CreateBadRequestError(Strings.RequestUriProcessor_SegmentDoesNotSupportKeyPredicates(previous.Identifier));
                        }

                        CheckSegmentIsComposable(previous);

                        if (crossReferencingUri)
                        {
                            throw DataServiceException.CreateBadRequestError(Strings.BadRequest_ResourceCanBeCrossReferencedOnlyForBindOperation);
                        }

                        previous.SingleResult = true;
                        previous.Key          = keySegment;
                        continue;
                    }

                    if (segment is NavigationPropertyLinkSegment)
                    {
                        segmentInfo = CreateEntityReferenceLinkSegment(previous);
        #if DEBUG
                        segmentInfo.AssertValid();
        #endif
                        segmentInfos.Add(segmentInfo);

                        previous = segmentInfo;
                    }

                    segmentInfo = this.CreateNextSegment(previous, segment);
                }

                Debug.Assert(segmentInfo != null, "segment != null");
#if DEBUG
                segmentInfo.AssertValid();
#endif
                segmentInfos.Add(segmentInfo);

                // we need to copy the segment over (even if it was an escape marker) as decisions will be made about it the next time through the loop.
                previous = segmentInfo;
            }

            return(segmentInfos);
        }
        /// <summary>
        /// Creates a named stream segment
        /// </summary>
        /// <param name="previous">previous segment info.</param>
        /// <param name="streamProperty">stream property to create the segment for.</param>
        /// <returns>new named stream segment.</returns>
        private static SegmentInfo CreateNamedStreamSegment(SegmentInfo previous, ResourceProperty streamProperty)
        {
            Debug.Assert(streamProperty.IsOfKind(ResourcePropertyKind.Stream), "streamProperty.IsOfKind(ResourcePropertyKind.Stream)");

            // Handle Named Stream.
            SegmentInfo segment = new SegmentInfo() { Identifier = streamProperty.Name };
            segment.TargetKind = RequestTargetKind.MediaResource;
            segment.SingleResult = true;
            segment.TargetResourceType = previous.TargetResourceType;
            segment.TargetSource = RequestTargetSource.Property;
            Debug.Assert(segment.Identifier != XmlConstants.UriValueSegment, "'$value' cannot be the name of a named stream.");

            return segment;
        }
        public void RawValueWithMimeTypeShouldUseFormat()
        {
            this.host.AbsoluteServiceUri = new Uri("http://myservice.org/");
            this.host.AbsoluteRequestUri = new Uri("http://myservice.org/FakeSet");

            SegmentInfo segment = new SegmentInfo
            {
                TargetKind = RequestTargetKind.PrimitiveValue,
                TargetSource = RequestTargetSource.Property,
                ProjectedProperty = new ResourceProperty("Fake", ResourcePropertyKind.Primitive, ResourceType.GetPrimitiveResourceType(typeof(string))) { MimeType = "fake/things" }
            };

            var requestDescription = new RequestDescription(new[] { segment }, new Uri("http://temp.org"));

            var testSubject = this.ForNormalRequest(requestDescription);
            testSubject.WriterSettings.UseFormat.Should().BeTrue();
            testSubject.WriterSettings.Format.Should().BeSameAs(ODataFormat.RawValue);
        }
        /// <summary>
        /// Creates a segment for the given service action.
        /// </summary>
        /// <param name="previousSegment">The previous segment before the operation to be invoked.</param>
        /// <param name="serviceAction">The service action to create the segment for.</param>
        /// <returns>A fully populated PathSegment representing the service action</returns>
        private SegmentInfo CreateSegmentForServiceAction(SegmentInfo previousSegment, OperationWrapper serviceAction)
        {
            Debug.Assert(serviceAction != null && serviceAction.Kind == OperationKind.Action, "serviceAction != null && serviceAction.Kind == OperationKind.Action");

            SegmentInfo segment = new SegmentInfo() { Identifier = serviceAction.Name, Operation = serviceAction };

            WebUtil.DebugEnumIsDefined(serviceAction.ResultKind);
            Debug.Assert(segment.IsServiceActionSegment, "IsServiceActionSegment(segment)");

            if (previousSegment != null && previousSegment.TargetKind == RequestTargetKind.Link)
            {
                throw DataServiceException.CreateBadRequestError(Strings.RequestUriProcessor_LinkSegmentMustBeFollowedByEntitySegment(serviceAction.Name, XmlConstants.UriLinkSegment));
            }

            segment.TargetSource = RequestTargetSource.ServiceOperation;

            if (serviceAction.ResultKind != ServiceOperationResultKind.Void)
            {
                segment.TargetResourceSet = serviceAction.GetResultSet(this.providerWrapper, previousSegment == null ? null : previousSegment.TargetResourceSet);
                segment.TargetResourceType = serviceAction.ReturnType;
                segment.TargetKind = TargetKindFromType(segment.TargetResourceType);
                if (segment.TargetKind == RequestTargetKind.Resource && segment.TargetResourceSet == null)
                {
                    // Service actions are either visible (ServiceActionRights.Invoke) or not (ServiceActionRight.None). The fact that
                    // DataServiceActionProviderWrapper.TryResolveServiceAction() returns a non-null value means the action is visible.
                    // If the result of the action is of entity type, we need to make sure the target set is visible or else the self
                    // and edit links of the entities in the response payload would not be usable.
                    Debug.Assert(serviceAction.IsVisible, "serviceAction.IsVisible");
                    throw DataServiceException.CreateForbidden();
                }

                segment.SingleResult = serviceAction.ResultKind == ServiceOperationResultKind.DirectValue;
                Debug.Assert(serviceAction.ResultKind != ServiceOperationResultKind.QueryWithSingleResult, "QueryWithSingleResult is not applicable for Actions.");
            }
            else
            {
                segment.TargetResourceSet = null;
                segment.TargetResourceType = null;
                segment.TargetKind = RequestTargetKind.VoidOperation;
            }

            return segment;
        }
        /// <summary>
        /// Creates a segment for a service operation
        /// </summary>
        /// <param name="serviceOperation">The service operation for the segment.</param>
        /// <returns>A fully populated PathSegment representing the service operation</returns>
        private static SegmentInfo CreateSegmentForServiceOperation(OperationWrapper serviceOperation)
        {
            Debug.Assert(serviceOperation != null, "serviceOperation != null");
            WebUtil.DebugEnumIsDefined(serviceOperation.ResultKind);

            SegmentInfo segment = new SegmentInfo
            {
                Identifier = serviceOperation.Name,
                Operation = serviceOperation, 
                TargetSource = RequestTargetSource.ServiceOperation,
                TargetResourceSet = serviceOperation.ResourceSet
            };

            if (serviceOperation.ResultKind != ServiceOperationResultKind.Void)
            {
                segment.TargetResourceType = serviceOperation.ResultType;
                segment.TargetKind = TargetKindFromType(segment.TargetResourceType);

                // this service operation returns results
                // we should check service operation rights
                // SingleResult operations are service operations defined with [SingleResult] attribute, returns a DirectValue
                // or a service operation returning multiple results, but contains key predicates in the query portion.
                // For these operations, you MUST have ReadSingle defined
                // For multiple-result-operations(IQueryable/IEnumerable), you MUST have ReadMultiple defined.
                segment.SingleResult = (serviceOperation.ResultKind == ServiceOperationResultKind.QueryWithSingleResult || serviceOperation.ResultKind == ServiceOperationResultKind.DirectValue);
            }
            else
            {
                segment.TargetResourceType = null;
                segment.TargetKind = RequestTargetKind.VoidOperation;
            }

            return segment;
        }
        /// <summary>
        /// Tries to create a type name segment if the given identifier refers to a known type.
        /// </summary>
        /// <param name="previous">previous segment info.</param>
        /// <param name="segment">The segment being interpreted.</param>
        /// <param name="typeNameSegment">The type name segment, if one was created.</param>
        /// <returns>Whether or not a type segment was created for the identifier.</returns>
        private bool TryCreateTypeNameSegment(SegmentInfo previous, ODataPathSegment segment, out SegmentInfo typeNameSegment)
        {
            var typeSegment = segment as TypeSegment;
            if (typeSegment == null || previous.TargetResourceSet == null)
            {
                typeNameSegment = null;
                return false;
            }

            ResourceType targetResourceType = MetadataProviderUtils.GetResourceType(typeSegment);
            
            // if the new type segment prevents any results from possibly being returned, then short-circuit and throw a 404.
            ResourceType previousResourceType = previous.TargetResourceType;
            Debug.Assert(previousResourceType != null, "previous.TargetResourceType != null");
            if (!targetResourceType.IsAssignableFrom(previousResourceType) && !previousResourceType.IsAssignableFrom(targetResourceType))
            {
                throw DataServiceException.CreateBadRequestError(Strings.RequestUriProcessor_InvalidTypeIdentifier_UnrelatedType(targetResourceType.FullName, previousResourceType.FullName));
            }

            // Since we allow derived navigation properties or named streams in V1/V2, the server will generate edit links and navigation links with type segment in it.
            // Hence we need to be able to process type segment in the request even when the server MPV is set to V1/V2. But we do not want to expose new functionality
            // like filtering collections based on type, etc on V1/V2 servers. Hence only checking for MPV to be v3 or greater if the previous segment is a collection
            if (!previous.SingleResult)
            {
                VersionUtil.CheckMaxProtocolVersion(VersionUtil.Version4Dot0, this.maxProtocolVersion);
            }

            typeNameSegment = new SegmentInfo
            {
                Identifier = targetResourceType.FullName,
                Operation = previous.Operation,
                TargetKind = previous.TargetKind,
                TargetSource = previous.TargetSource,
                TargetResourceType = targetResourceType,
                SingleResult = previous.SingleResult,
                TargetResourceSet = previous.TargetResourceSet,
                ProjectedProperty = previous.ProjectedProperty,
                Key = previous.Key,
                RequestExpression = previous.RequestExpression,
                RequestEnumerable = previous.RequestEnumerable,
                IsTypeIdentifierSegment = true
            };

            return true;
        }
        /// <summary>
        /// Throws if the given segment must be a leaf, as a later segment is being created.
        /// </summary>
        /// <param name="previous">The previous segment which may need to be a leaf.</param>
        private static void ThrowIfMustBeLeafSegment(SegmentInfo previous)
        {
            if (previous.IsServiceActionSegment)
            {
                throw DataServiceException.CreateBadRequestError(Strings.RequestUriProcessor_MustBeLeafSegment(previous.Identifier));
            }

            if (previous.TargetKind == RequestTargetKind.Batch || previous.TargetKind == RequestTargetKind.Metadata || previous.TargetKind == RequestTargetKind.PrimitiveValue || previous.TargetKind == RequestTargetKind.VoidOperation || previous.TargetKind == RequestTargetKind.OpenPropertyValue || previous.TargetKind == RequestTargetKind.MediaResource || previous.TargetKind == RequestTargetKind.Collection)
            {
                // Nothing can come after a $metadata, $value or $batch segment.
                // Nothing can come after a service operation with void return type.
                // Nothing can come after a collection property.
                throw DataServiceException.ResourceNotFoundError(Strings.RequestUriProcessor_MustBeLeafSegment(previous.Identifier));
            }
        }
Пример #27
0
        private static void RunPayloadKindTest(RequestTargetKind requestTargetKind, RequestTargetSource requestTargetSource, ResourceType targetResourceType, bool singleResult, bool isLinkUri, ODataPayloadKind expectedKind)
        {
            var segment = new SegmentInfo
            {
                TargetKind = requestTargetKind,
                TargetSource = requestTargetSource,
                TargetResourceType = targetResourceType,
                SingleResult = singleResult,
                Identifier = "Fake",
            };

            var operation = new ServiceOperation("Fake", ServiceOperationResultKind.Void, null, null, "GET", null);
            operation.SetReadOnly();
            segment.Operation = new OperationWrapper(operation);
            segment.ProjectedProperty = new ResourceProperty("Fake", ResourcePropertyKind.Primitive, ResourceType.GetPrimitiveResourceType(typeof(int)));

            SegmentInfo[] segmentInfos;
            if (isLinkUri)
            {
                segmentInfos = new[]
                {
                    new SegmentInfo(),
                    new SegmentInfo
                    {
                       TargetKind = RequestTargetKind.Link,  
                    },
                    segment
                };
            }
            else
            {
                segmentInfos = new[]
                {
                    new SegmentInfo
                    {
                        Identifier = "Fake",
                    },
                    new SegmentInfo(),
                    segment
                };
            }

            var requestDescription = new RequestDescription(segmentInfos, new Uri("http://temp.org/"));
            requestDescription.ResponsePayloadKind.Should().Be(expectedKind);
        }
 /// <summary>
 /// Checks that the previous segment can be composed upon.
 /// </summary>
 /// <param name="previous">previous segment info.</param>
 private static void CheckSegmentIsComposable(SegmentInfo previous)
 {
     if (!IsSegmentComposable(previous))
     {
         // Enumerable and DirectValue results cannot be composed at all, and we don't allow we can't access properties in a single queryable complex either
         throw DataServiceException.ResourceNotFoundError(
             Strings.RequestUriProcessor_IEnumerableServiceOperationsCannotBeFurtherComposed(previous.Identifier));
     }
 }
Пример #29
0
        /// <summary>
        /// Initializes a new RequestDescription for a query specified by the
        /// request Uri.
        /// </summary>
        /// <param name="targetKind">The kind of target for the request.</param>
        /// <param name="targetSource">The source for this target.</param>
        /// <param name="resultUri">URI to the results requested (with no query component).</param>
        internal RequestDescription(RequestTargetKind targetKind, RequestTargetSource targetSource, Uri resultUri)
        {
            WebUtil.DebugEnumIsDefined(targetKind);
            Debug.Assert(resultUri != null, "resultUri != null");
            Debug.Assert(resultUri.IsAbsoluteUri, "resultUri.IsAbsoluteUri(" + resultUri + ")");

            SegmentInfo segment = new SegmentInfo
            {
                TargetKind = targetKind,
                TargetSource = targetSource,
                SingleResult = true
            };
            this.segmentInfos = new[] { segment };
            this.resultUri = resultUri;

#if DEBUG
            this.maxFeatureVersion = VersionUtil.Version4Dot0;
#endif
            this.ResponseVersion = VersionUtil.DataServiceDefaultResponseVersion;
            this.ActualResponseVersion = this.ResponseVersion;

            this.Preference = ClientPreference.None;
        }
Пример #30
0
        /// <summary>
        /// Apply the key predicates extracted from the segment's query portion
        /// </summary>
        /// <param name="segment">The segment on which the query is extracted</param>
        private static void ApplyKeyToExpression(SegmentInfo segment)
        {
            Debug.Assert(segment != null, "segment!= null");
            Debug.Assert(segment.SingleResult, "Segment should have single result set");
            Debug.Assert(segment.HasKeyValues, "Key is not empty");

            segment.RequestExpression = SelectResourceByKey(segment.RequestExpression, segment.TargetResourceType, segment.Key);
        }