/// <summary>
        /// Creates a context URI parser and parses the context URI read from the payload.
        /// </summary>
        /// <param name="model">The model to use when resolving the target of the URI.</param>
        /// <param name="contextUriFromPayload">The string value of the odata.metadata annotation read from the payload.</param>
        /// <param name="payloadKind">The payload kind we expect the context URI to conform to.</param>
        /// <param name="version">The OData version to use for determining the set of built-in functions available.</param>
        /// <param name="readerBehavior">Reader behavior if the caller is a reader, null if no reader behavior is available.</param>
        /// <param name="needParseFragment">Whether the fragment after $metadata should be parsed, if set to false, only MetadataDocumentUri is parsed.</param>
        /// <returns>The result from parsing the context URI.</returns>
        internal static ODataJsonLightContextUriParseResult Parse(
            IEdmModel model,
            string contextUriFromPayload,
            ODataPayloadKind payloadKind,
            ODataVersion version,
            ODataReaderBehavior readerBehavior,
            bool needParseFragment)
        {
            if (contextUriFromPayload == null)
            {
                throw new ODataException(ODataErrorStrings.ODataJsonLightContextUriParser_NullMetadataDocumentUri);
            }

            // Create an absolute URI from the payload string
            Uri contextUri = new Uri(contextUriFromPayload, UriKind.Absolute);
            ODataJsonLightContextUriParser parser = new ODataJsonLightContextUriParser(model, contextUri);

            parser.TokenizeContextUri();
            if (needParseFragment)
            {
                parser.ParseContextUri(payloadKind, readerBehavior, version);
            }

            return(parser.parseResult);
        }
Example #2
0
        /// <summary>
        /// Creates a context URI parser and parses the context URI read from the payload.
        /// </summary>
        /// <param name="model">The model to use when resolving the target of the URI.</param>
        /// <param name="contextUriFromPayload">The string value of the odata.metadata annotation read from the payload.</param>
        /// <param name="payloadKind">The payload kind we expect the context URI to conform to.</param>
        /// <param name="readerBehavior">Reader behavior if the caller is a reader, null if no reader behavior is available.</param>
        /// <param name="needParseFragment">Whether the fragment after $metadata should be parsed, if set to false, only MetadataDocumentUri is parsed.</param>
        /// <returns>The result from parsing the context URI.</returns>
        internal static ODataJsonLightContextUriParseResult Parse(
            IEdmModel model,
            string contextUriFromPayload,
            ODataPayloadKind payloadKind,
            ODataReaderBehavior readerBehavior,
            bool needParseFragment)
        {
            if (contextUriFromPayload == null)
            {
                throw new ODataException(ODataErrorStrings.ODataJsonLightContextUriParser_NullMetadataDocumentUri);
            }

            // Create an absolute URI from the payload string
            // TODO: Support relative context uri and resolving other relative uris
            Uri contextUri;

            if (!Uri.TryCreate(contextUriFromPayload, UriKind.Absolute, out contextUri))
            {
                throw new ODataException(ODataErrorStrings.ODataJsonLightContextUriParser_TopLevelContextUrlShouldBeAbsolute(contextUriFromPayload));
            }

            ODataJsonLightContextUriParser parser = new ODataJsonLightContextUriParser(model, contextUri);

            parser.TokenizeContextUri();
            if (needParseFragment)
            {
                parser.ParseContextUri(payloadKind, readerBehavior);
            }

            return(parser.parseResult);
        }
        /// <summary>
        /// Creates a context URI parser and parses the context URI read from the payload.
        /// </summary>
        /// <param name="model">The model to use when resolving the target of the URI.</param>
        /// <param name="contextUriFromPayload">The string value of the odata.metadata annotation read from the payload.</param>
        /// <param name="payloadKind">The payload kind we expect the context URI to conform to.</param>
        /// <param name="readerBehavior">Reader behavior if the caller is a reader, null if no reader behavior is available.</param>
        /// <param name="needParseFragment">Whether the fragment after $metadata should be parsed, if set to false, only MetadataDocumentUri is parsed.</param>
        /// <returns>The result from parsing the context URI.</returns>
        internal static ODataJsonLightContextUriParseResult Parse(
            IEdmModel model,
            string contextUriFromPayload,
            ODataPayloadKind payloadKind,
            ODataReaderBehavior readerBehavior,
            bool needParseFragment)
        {
            if (contextUriFromPayload == null)
            {
                throw new ODataException(ODataErrorStrings.ODataJsonLightContextUriParser_NullMetadataDocumentUri);
            }

            // Create an absolute URI from the payload string
            // TODO: Support relative context uri and resolving other relative uris
            Uri contextUri;
            if (!Uri.TryCreate(contextUriFromPayload, UriKind.Absolute, out contextUri))
            {
                throw new ODataException(ODataErrorStrings.ODataJsonLightContextUriParser_TopLevelContextUrlShouldBeAbsolute(contextUriFromPayload));
            }

            ODataJsonLightContextUriParser parser = new ODataJsonLightContextUriParser(model, contextUri);

            parser.TokenizeContextUri();
            if (needParseFragment)
            {
                parser.ParseContextUri(payloadKind, readerBehavior);
            }

            return parser.parseResult;
        }
Example #4
0
        /// <summary>
        /// Applies the model and validates the context URI against it.
        /// </summary>
        /// <param name="expectedPayloadKind">The payload kind we expect the context URI to conform to.</param>
        /// <param name="readerBehavior">Reader behavior if the caller is a reader, null if no reader behavior is available.</param>
        private void ParseContextUri(ODataPayloadKind expectedPayloadKind, ODataReaderBehavior readerBehavior)
        {
            ODataPayloadKind detectedPayloadKind = this.ParseContextUriFragment(this.parseResult.Fragment, readerBehavior);

            // unsupported payload kind indicates that this is during payload kind detection, so we should not fail.
            bool detectedPayloadKindMatchesExpectation = detectedPayloadKind == expectedPayloadKind || expectedPayloadKind == ODataPayloadKind.Unsupported;

            if (detectedPayloadKind == ODataPayloadKind.Collection)
            {
                // If the detected payload kind is 'collection' it can always also be treated as a property.
                this.parseResult.DetectedPayloadKinds = new[] { ODataPayloadKind.Collection, ODataPayloadKind.Property };
                if (expectedPayloadKind == ODataPayloadKind.Property)
                {
                    detectedPayloadKindMatchesExpectation = true;
                }
            }
            else if (detectedPayloadKind == ODataPayloadKind.Entry)
            {
                this.parseResult.DetectedPayloadKinds = new[] { ODataPayloadKind.Entry, ODataPayloadKind.Delta };
                if (expectedPayloadKind == ODataPayloadKind.Delta)
                {
                    this.parseResult.DeltaKind            = ODataDeltaKind.Entry;
                    detectedPayloadKindMatchesExpectation = true;
                }
            }
            else
            {
                this.parseResult.DetectedPayloadKinds = new[] { detectedPayloadKind };
            }

            // If the expected and detected payload kinds don't match and we are not running payload kind detection
            // right now (payloadKind == ODataPayloadKind.Unsupported) and we did not detect a collection kind for
            // an expected property kind (which is allowed), fail.
            if (!detectedPayloadKindMatchesExpectation)
            {
                throw new ODataException(ODataErrorStrings.ODataJsonLightContextUriParser_ContextUriDoesNotMatchExpectedPayloadKind(UriUtils.UriToString(this.parseResult.ContextUri), expectedPayloadKind.ToString()));
            }

            // NOTE: we interpret an empty select query option to mean that nothing should be projected
            //       (whereas a missing select query option means everything should be projected).
            string selectQueryOption = this.parseResult.SelectQueryOption;

            if (selectQueryOption != null)
            {
                if (detectedPayloadKind != ODataPayloadKind.Feed && detectedPayloadKind != ODataPayloadKind.Entry && detectedPayloadKind != ODataPayloadKind.Delta)
                {
                    throw new ODataException(ODataErrorStrings.ODataJsonLightContextUriParser_InvalidPayloadKindWithSelectQueryOption(expectedPayloadKind.ToString()));
                }
            }
        }
Example #5
0
        private void CompareReaderBehavior(
            ODataMessageReaderSettings settings,
            ODataBehaviorKind formatBehaviorKind,
            ODataBehaviorKind apiBehaviorKind,
            bool allowDuplicatePropertyNames,
            Func <IEdmType, string, IEdmType> typeResolver)
        {
            ODataReaderBehavior readerBehavior = settings.ReaderBehavior;

            Assert.AreEqual(formatBehaviorKind, readerBehavior.FormatBehaviorKind, "Reader format behavior kinds don't match.");
            Assert.AreEqual(apiBehaviorKind, readerBehavior.ApiBehaviorKind, "Reader API behavior kinds don't match.");
            Assert.AreEqual(allowDuplicatePropertyNames, readerBehavior.AllowDuplicatePropertyNames, "AllowDuplicatePropertyNames values don't match.");
            Assert.AreSame(typeResolver, readerBehavior.TypeResolver, "TypeResolver values don't match.");
        }
        /// <summary>
        /// Resolves a type.
        /// </summary>
        /// <param name="typeName">The type name.</param>
        /// <param name="readerBehavior">Reader behavior if the caller is a reader, null if no reader behavior is available.</param>
        /// <param name="version">The version of the payload being read.</param>
        /// <returns>The resolved Edm type.</returns>
        private IEdmType ResolveType(string typeName, ODataReaderBehavior readerBehavior, ODataVersion version)
        {
            string typeNameToResolve = EdmLibraryExtensions.GetCollectionItemTypeName(typeName) ?? typeName;

            EdmTypeKind typeKind;
            IEdmType    resolvedType = MetadataUtils.ResolveTypeNameForRead(this.model, /*expectedType*/ null, typeNameToResolve, readerBehavior, version, out typeKind);

            if (resolvedType == null || resolvedType.TypeKind != EdmTypeKind.Primitive && resolvedType.TypeKind != EdmTypeKind.Complex)
            {
                throw new ODataException(ODataErrorStrings.ODataJsonLightMetadataUriParser_InvalidEntitySetNameOrTypeName(UriUtilsCommon.UriToString(this.parseResult.MetadataUri), typeName));
            }

            resolvedType = typeNameToResolve == typeName ? resolvedType : EdmLibraryExtensions.GetCollectionType(resolvedType.ToTypeReference(true /*nullable*/));
            return(resolvedType);
        }
Example #7
0
        /// <summary>
        /// Resolves the name of a primitive, complex, entity or collection type to the respective type. Uses the semantics used be readers.
        /// Thus it can be a bit looser.
        /// </summary>
        /// <param name="model">The model to use.</param>
        /// <param name="expectedType">The expected type for the type name being resolved, or null if none is available.</param>
        /// <param name="typeName">The name of the type to resolve.</param>
        /// <param name="readerBehavior">Reader behavior if the caller is a reader, null if no reader behavior is available.</param>
        /// <param name="typeKind">The type kind of the type, if it could be determined. This will be None if we couldn't tell. It might be filled
        /// even if the method returns null, for example for Collection types with item types which are not recognized.</param>
        /// <returns>The <see cref="IEdmType"/> representing the type specified by the <paramref name="typeName"/>;
        /// or null if no such type could be found.</returns>
        internal static IEdmType ResolveTypeNameForRead(
            IEdmModel model,
            IEdmType expectedType,
            string typeName,
            ODataReaderBehavior readerBehavior,
            out EdmTypeKind typeKind)
        {
            Func <IEdmType, string, IEdmType> customTypeResolver = readerBehavior == null ? null : readerBehavior.TypeResolver;

            Debug.Assert(
                customTypeResolver == null || readerBehavior.ApiBehaviorKind == ODataBehaviorKind.WcfDataServicesClient,
                "Custom type resolver can only be specified in WCF DS Client behavior.");

            return(ResolveTypeName(model, expectedType, typeName, customTypeResolver, out typeKind));
        }
        /// <summary>
        /// Creates a metadata URI parser and parses the metadata URI read from the payload.
        /// </summary>
        /// <param name="model">The model to use when resolving the target of the URI.</param>
        /// <param name="metadataUriFromPayload">The string value of the odata.metadata annotation read from the payload.</param>
        /// <param name="payloadKind">The payload kind we expect the metadata URI to conform to.</param>
        /// <param name="version">The OData version to use for determining the set of built-in functions available.</param>
        /// <param name="readerBehavior">Reader behavior if the caller is a reader, null if no reader behavior is available.</param>
        /// <returns>The result from parsing the metadata URI.</returns>
        internal static ODataJsonLightMetadataUriParseResult Parse(
            IEdmModel model,
            string metadataUriFromPayload,
            ODataPayloadKind payloadKind,
            ODataVersion version,
            ODataReaderBehavior readerBehavior)
        {
            DebugUtils.CheckNoExternalCallers();

            if (metadataUriFromPayload == null)
            {
                throw new ODataException(ODataErrorStrings.ODataJsonLightMetadataUriParser_NullMetadataDocumentUri);
            }

            // Create an absolute URI from the payload string
            Uri metadataUri = new Uri(metadataUriFromPayload, UriKind.Absolute);
            ODataJsonLightMetadataUriParser parser = new ODataJsonLightMetadataUriParser(model, metadataUri);

            parser.TokenizeMetadataUri();

            parser.ParseMetadataUri(payloadKind, readerBehavior, version);
            return(parser.parseResult);
        }
Example #9
0
        /// <summary>
        /// Resolves a type.
        /// </summary>
        /// <param name="typeName">The type name.</param>
        /// <param name="readerBehavior">Reader behavior if the caller is a reader, null if no reader behavior is available.</param>
        /// <returns>The resolved Edm type.</returns>
        private ODataPayloadKind ResolveType(string typeName, ODataReaderBehavior readerBehavior)
        {
            string typeNameToResolve = EdmLibraryExtensions.GetCollectionItemTypeName(typeName) ?? typeName;
            bool   isCollection      = typeNameToResolve != typeName;

            EdmTypeKind typeKind;
            IEdmType    resolvedType = MetadataUtils.ResolveTypeNameForRead(this.model, /*expectedType*/ null, typeNameToResolve, readerBehavior, out typeKind);

            if (resolvedType == null || resolvedType.TypeKind != EdmTypeKind.Primitive && resolvedType.TypeKind != EdmTypeKind.Enum && resolvedType.TypeKind != EdmTypeKind.Complex && resolvedType.TypeKind != EdmTypeKind.Entity && resolvedType.TypeKind != EdmTypeKind.TypeDefinition)
            {
                throw new ODataException(ODataErrorStrings.ODataJsonLightContextUriParser_InvalidEntitySetNameOrTypeName(UriUtils.UriToString(this.parseResult.ContextUri), typeName));
            }

            if (resolvedType.TypeKind == EdmTypeKind.Entity)
            {
                this.parseResult.EdmType = resolvedType;
                return(isCollection ? ODataPayloadKind.Feed : ODataPayloadKind.Entry);
            }

            resolvedType             = isCollection ? EdmLibraryExtensions.GetCollectionType(resolvedType.ToTypeReference(true /*nullable*/)) : resolvedType;
            this.parseResult.EdmType = resolvedType;
            return(isCollection ? ODataPayloadKind.Collection : ODataPayloadKind.Property);
        }
 /// <summary>Creates a new entity set element type resolver with all the information needed when resolving for reading scenarios.</summary>
 /// <param name="model">The model to use or null if no model is available.</param>
 /// <param name="readerBehavior">Reader behavior if the caller is a reader, null if no reader behavior is available.</param>
 public EdmTypeReaderResolver(IEdmModel model, ODataReaderBehavior readerBehavior)
 {
     this.model = model;
     this.readerBehavior = readerBehavior;
 }
Example #11
0
        internal static IEdmType ResolveTypeNameForRead(IEdmModel model, IEdmType expectedType, string typeName, ODataReaderBehavior readerBehavior, ODataVersion version, out EdmTypeKind typeKind)
        {
            Func <IEdmType, string, IEdmType> customTypeResolver = (readerBehavior == null) ? null : readerBehavior.TypeResolver;

            return(ResolveTypeName(model, expectedType, typeName, customTypeResolver, version, out typeKind));
        }
Example #12
0
        private ODataPayloadKind ParseContextUriFragment(string fragment, ODataReaderBehavior readerBehavior)
        {
            bool           hasItemSelector = false;
            ODataDeltaKind kind            = ODataDeltaKind.None;

            // Deal with /$entity
            if (fragment.EndsWith(ODataConstants.ContextUriFragmentItemSelector, StringComparison.Ordinal))
            {
                hasItemSelector = true;
                fragment        = fragment.Substring(0, fragment.Length - ODataConstants.ContextUriFragmentItemSelector.Length);
            }
            else if (fragment.EndsWith(ODataConstants.ContextUriDeltaFeed, StringComparison.Ordinal))
            {
                kind     = ODataDeltaKind.Feed;
                fragment = fragment.Substring(0, fragment.Length - ODataConstants.ContextUriDeltaFeed.Length);
            }
            else if (fragment.EndsWith(ODataConstants.ContextUriDeletedEntry, StringComparison.Ordinal))
            {
                kind     = ODataDeltaKind.DeletedEntry;
                fragment = fragment.Substring(0, fragment.Length - ODataConstants.ContextUriDeletedEntry.Length);
            }
            else if (fragment.EndsWith(ODataConstants.ContextUriDeltaLink, StringComparison.Ordinal))
            {
                kind     = ODataDeltaKind.Link;
                fragment = fragment.Substring(0, fragment.Length - ODataConstants.ContextUriDeltaLink.Length);
            }
            else if (fragment.EndsWith(ODataConstants.ContextUriDeletedLink, StringComparison.Ordinal))
            {
                kind     = ODataDeltaKind.DeletedLink;
                fragment = fragment.Substring(0, fragment.Length - ODataConstants.ContextUriDeletedLink.Length);
            }

            this.parseResult.DeltaKind = kind;

            // Deal with query option
            if (fragment.EndsWith(")", StringComparison.Ordinal))
            {
                int index = fragment.Length - 2;
                for (int rcount = 1; rcount > 0 && index > 0; --index)
                {
                    switch (fragment[index])
                    {
                    case '(':
                        rcount--;
                        break;

                    case ')':
                        rcount++;
                        break;
                    }
                }

                if (index == 0)
                {
                    throw new ODataException(ODataErrorStrings.ODataJsonLightContextUriParser_InvalidContextUrl(UriUtils.UriToString(this.parseResult.ContextUri)));
                }

                string previous = fragment.Substring(0, index + 1);

                // Don't treat Collection(Edm.Type) as SelectExpand segment
                if (!previous.Equals("Collection"))
                {
                    string selectExpandStr = fragment.Substring(index + 2);
                    selectExpandStr = selectExpandStr.Substring(0, selectExpandStr.Length - 1);

                    // Do not treat Key as SelectExpand segment
                    if (KeyPattern.IsMatch(selectExpandStr))
                    {
                        throw new ODataException(ODataErrorStrings.ODataJsonLightContextUriParser_LastSegmentIsKeySegment(UriUtils.UriToString(this.parseResult.ContextUri)));
                    }

                    this.parseResult.SelectQueryOption = ExtractSelectQueryOption(selectExpandStr);
                    fragment = previous;
                }
            }

            ODataPayloadKind detectedPayloadKind = ODataPayloadKind.Unsupported;
            EdmTypeResolver  edmTypeResolver     = new EdmTypeReaderResolver(this.model, readerBehavior);

            if (!fragment.Contains(ODataConstants.UriSegmentSeparator) && !hasItemSelector && kind == ODataDeltaKind.None)
            {
                // Service document: no fragment
                if (fragment.Length == 0)
                {
                    detectedPayloadKind = ODataPayloadKind.ServiceDocument;
                }
                else if (fragment.Equals(ODataConstants.ContextUriFragmentNull, StringComparison.OrdinalIgnoreCase))
                {
                    detectedPayloadKind             = ODataPayloadKind.Property;
                    this.parseResult.IsNullProperty = true;
                }
                else if (fragment.Equals(ODataConstants.EntityReferenceCollectionSegmentName + "(" + ODataConstants.EntityReferenceSegmentName + ")"))
                {
                    detectedPayloadKind = ODataPayloadKind.EntityReferenceLinks;
                }
                else if (fragment.Equals(ODataConstants.EntityReferenceSegmentName))
                {
                    detectedPayloadKind = ODataPayloadKind.EntityReferenceLink;
                }
                else
                {
                    var foundNavigationSource = this.model.FindDeclaredNavigationSource(fragment);

                    if (foundNavigationSource != null)
                    {
                        // Feed: {schema.entity-container.entity-set} or Singleton: {schema.entity-container.singleton}
                        this.parseResult.NavigationSource = foundNavigationSource;
                        this.parseResult.EdmType          = edmTypeResolver.GetElementType(foundNavigationSource);
                        detectedPayloadKind = foundNavigationSource is IEdmSingleton ? ODataPayloadKind.Entry : ODataPayloadKind.Feed;
                    }
                    else
                    {
                        // Property: {schema.type} or Collection({schema.type}) where schema.type is primitive or complex.
                        detectedPayloadKind = this.ResolveType(fragment, readerBehavior);
                        Debug.Assert(
                            this.parseResult.EdmType.TypeKind == EdmTypeKind.Primitive || this.parseResult.EdmType.TypeKind == EdmTypeKind.Enum || this.parseResult.EdmType.TypeKind == EdmTypeKind.TypeDefinition || this.parseResult.EdmType.TypeKind == EdmTypeKind.Complex || this.parseResult.EdmType.TypeKind == EdmTypeKind.Collection || this.parseResult.EdmType.TypeKind == EdmTypeKind.Entity,
                            "The first context URI segment must be a set or a non-entity type.");
                    }
                }
            }
            else
            {
                Debug.Assert(this.parseResult.MetadataDocumentUri.IsAbsoluteUri, "this.parseResult.MetadataDocumentUri.IsAbsoluteUri");

                string metadataDocumentStr = UriUtils.UriToString(this.parseResult.MetadataDocumentUri);

                if (!metadataDocumentStr.EndsWith(ODataConstants.UriMetadataSegment, StringComparison.Ordinal))
                {
                    throw new ODataException(ODataErrorStrings.ODataJsonLightContextUriParser_InvalidContextUrl(UriUtils.UriToString(this.parseResult.ContextUri)));
                }

                Uri serviceRoot = new Uri(metadataDocumentStr.Substring(0, metadataDocumentStr.Length - ODataConstants.UriMetadataSegment.Length));

                ODataUriParser odataUriParser = new ODataUriParser(this.model, serviceRoot, new Uri(serviceRoot, fragment));

                ODataPath path;
                try
                {
                    path = odataUriParser.ParsePath();
                }
                catch (ODataException)
                {
                    throw new ODataException(ODataErrorStrings.ODataJsonLightContextUriParser_InvalidContextUrl(UriUtils.UriToString(this.parseResult.ContextUri)));
                }

                if (path.Count == 0)
                {
                    throw new ODataException(ODataErrorStrings.ODataJsonLightContextUriParser_InvalidContextUrl(UriUtils.UriToString(this.parseResult.ContextUri)));
                }

                this.parseResult.Path = path;

                parseResult.NavigationSource = path.NavigationSource();
                parseResult.EdmType          = path.LastSegment.EdmType;

                ODataPathSegment lastSegment = path.TrimEndingTypeSegment().LastSegment;
                if (lastSegment is EntitySetSegment || lastSegment is NavigationPropertySegment)
                {
                    if (kind != ODataDeltaKind.None)
                    {
                        detectedPayloadKind = ODataPayloadKind.Delta;
                    }
                    else
                    {
                        detectedPayloadKind = hasItemSelector ? ODataPayloadKind.Entry : ODataPayloadKind.Feed;
                    }

                    if (this.parseResult.EdmType is IEdmCollectionType)
                    {
                        var collectionTypeReference = this.parseResult.EdmType.ToTypeReference().AsCollection();
                        if (collectionTypeReference != null)
                        {
                            this.parseResult.EdmType = collectionTypeReference.ElementType().Definition;
                        }
                    }
                }
                else if (lastSegment is SingletonSegment)
                {
                    detectedPayloadKind = ODataPayloadKind.Entry;
                }
                else if (path.IsIndividualProperty())
                {
                    detectedPayloadKind = ODataPayloadKind.Property;

                    IEdmCollectionType collectionType = parseResult.EdmType as IEdmCollectionType;
                    if (collectionType != null)
                    {
                        detectedPayloadKind = ODataPayloadKind.Collection;
                    }
                }
                else
                {
                    throw new ODataException(ODataErrorStrings.ODataJsonLightContextUriParser_InvalidContextUrl(UriUtils.UriToString(this.parseResult.ContextUri)));
                }
            }

            return(detectedPayloadKind);
        }
        /// <summary>
        /// Resolves a type.
        /// </summary>
        /// <param name="typeName">The type name.</param>
        /// <param name="readerBehavior">Reader behavior if the caller is a reader, null if no reader behavior is available.</param>
        /// <returns>The resolved Edm type.</returns>
        private ODataPayloadKind ResolveType(string typeName, ODataReaderBehavior readerBehavior)
        {
            string typeNameToResolve = EdmLibraryExtensions.GetCollectionItemTypeName(typeName) ?? typeName;
            bool isCollection = typeNameToResolve != typeName;

            EdmTypeKind typeKind;
            IEdmType resolvedType = MetadataUtils.ResolveTypeNameForRead(this.model, /*expectedType*/ null, typeNameToResolve, readerBehavior, out typeKind);
            if (resolvedType == null || resolvedType.TypeKind != EdmTypeKind.Primitive && resolvedType.TypeKind != EdmTypeKind.Enum && resolvedType.TypeKind != EdmTypeKind.Complex && resolvedType.TypeKind != EdmTypeKind.Entity && resolvedType.TypeKind != EdmTypeKind.TypeDefinition)
            {
                throw new ODataException(ODataErrorStrings.ODataJsonLightContextUriParser_InvalidEntitySetNameOrTypeName(UriUtils.UriToString(this.parseResult.ContextUri), typeName));
            }

            if (resolvedType.TypeKind == EdmTypeKind.Entity)
            {
                this.parseResult.EdmType = resolvedType;
                return isCollection ? ODataPayloadKind.Feed : ODataPayloadKind.Entry;
            }

            resolvedType = isCollection ? EdmLibraryExtensions.GetCollectionType(resolvedType.ToTypeReference(true /*nullable*/)) : resolvedType;
            this.parseResult.EdmType = resolvedType;
            return isCollection ? ODataPayloadKind.Collection : ODataPayloadKind.Property;
        }
Example #14
0
        public void CalculateBindableOperationsForEntityCollectionTypeWithTypeResolver()
        {
            var bindingType        = new EdmCollectionType(this.model.FindType("TestModel.Movie").ToTypeReference(nullable: false));
            var bindableOperations = MetadataUtils.CalculateBindableOperationsForType(bindingType, this.model, new EdmTypeReaderResolver(this.model, ODataReaderBehavior.CreateWcfDataServicesClientBehavior(this.NameToTypeResolver)));

            Assert.Equal(2, bindableOperations.Length);
            foreach (var operation in bindableOperations)
            {
                Assert.Equal("RateMultiple", operation.Name);
            }
        }
 /// <summary>Creates a new entity set element type resolver with all the information needed when resolving for reading scenarios.</summary>
 /// <param name="model">The model to use or null if no model is available.</param>
 /// <param name="readerBehavior">Reader behavior if the caller is a reader, null if no reader behavior is available.</param>
 /// <param name="version">The version of the payload being read.</param>
 public EdmTypeReaderResolver(IEdmModel model, ODataReaderBehavior readerBehavior, ODataVersion version)
 {
     this.model          = model;
     this.readerBehavior = readerBehavior;
     this.version        = version;
 }
        /// <summary>
        /// Converts the given datetime value into the allowed target types.
        /// </summary>
        /// <param name="datetimeValue">DateTime value as read by the JsonReader.</param>
        /// <param name="targetType">Target type to which the datetime value needs to be converted.</param>
        /// <param name="primitiveTypeReference">Type reference to which the value needs to be converted.</param>
        /// <param name="readerBehavior">ODataReaderBehavior instance.</param>
        /// <returns>Object which is in sync with the target type.</returns>
        private static object ConvertDateTimeValue(DateTime datetimeValue, Type targetType, IEdmPrimitiveTypeReference primitiveTypeReference, ODataReaderBehavior readerBehavior)
        {
            if (targetType == typeof(DateTimeOffset))
            {
                // If the kind of DateTime is local or UTC, that means we know the timezone information. Hence we should allow
                // conversion to DateTimeOffset - there is no dataloss or ambiguity in that conversion.
                if (datetimeValue.Kind == DateTimeKind.Local || datetimeValue.Kind == DateTimeKind.Utc)
                {
                    return(new DateTimeOffset(datetimeValue));
                }
            }

            if (targetType != typeof(DateTime) && (readerBehavior == null || readerBehavior.FormatBehaviorKind != ODataBehaviorKind.WcfDataServicesServer))
            {
                throw new ODataException(ODataErrorStrings.ODataJsonReaderUtils_CannotConvertDateTime(primitiveTypeReference.ODataFullName()));
            }

            return(datetimeValue);
        }
        /// <summary>
        /// Resolves an entity set with an optional type cast and updates the parse result.
        /// </summary>
        /// <param name="entitySet">The entity set to resolve the type cast against.</param>
        /// <param name="typeCast">The optional type cast.</param>
        /// <param name="readerBehavior">Reader behavior if the caller is a reader, null if no reader behavior is available.</param>
        /// <param name="version">The version of the payload being read.</param>
        /// <param name="entitySetElementType">The type of the given entity set.</param>
        /// <returns>The resolved entity type.</returns>
        private IEdmEntityType ResolveTypeCast(IEdmEntitySet entitySet, string typeCast, ODataReaderBehavior readerBehavior, ODataVersion version, IEdmEntityType entitySetElementType)
        {
            Debug.Assert(entitySet != null, "entitySet != null");

            IEdmEntityType entityType = entitySetElementType;

            // Parse the type cast if it exists
            if (!string.IsNullOrEmpty(typeCast))
            {
                EdmTypeKind typeKind;
                entityType = MetadataUtils.ResolveTypeNameForRead(this.model, /*expectedType*/ null, typeCast, readerBehavior, version, out typeKind) as IEdmEntityType;
                if (entityType == null)
                {
                    throw new ODataException(ODataErrorStrings.ODataJsonLightMetadataUriParser_InvalidEntityTypeInTypeCast(UriUtilsCommon.UriToString(this.parseResult.MetadataUri), typeCast));
                }

                // Validate that the entity type is assignable to the base type of the set
                if (!entitySetElementType.IsAssignableFrom(entityType))
                {
                    throw new ODataException(ODataErrorStrings.ODataJsonLightMetadataUriParser_IncompatibleEntityTypeInTypeCast(UriUtilsCommon.UriToString(this.parseResult.MetadataUri), typeCast, entitySetElementType.FullName(), entitySet.FullName()));
                }
            }

            return(entityType);
        }
        /// <summary>
        /// Parses the fragment of an entity reference link metadata URI.
        /// </summary>
        /// <param name="edmTypeResolver">Edm Type Resolver used to get the ElementType of the entity set.</param>
        /// <param name="entitySet">Entity Set used as a starting point to find the navigation property</param>
        /// <param name="typeName">The name of the type declaring the navigation property.</param>
        /// <param name="propertyName">The name of the navigation property.</param>
        /// <param name="readerBehavior">Reader behavior if the caller is a reader, null if no reader behavior is available.</param>
        /// <param name="version">The version of the payload being read.</param>
        /// <returns>The resolved navigation property.</returns>
        private IEdmNavigationProperty ResolveEntityReferenceLinkMetadataFragment(EdmTypeResolver edmTypeResolver, IEdmEntitySet entitySet, string typeName, string propertyName, ODataReaderBehavior readerBehavior, ODataVersion version)
        {
            //// {entitySet}/$links/{nav-property}
            //// {entitySet}/$links/{nav-property}/@Element
            //// {entitySet}/typeName/$links/{nav-property}
            //// {entitySet}/typeName/$links/{nav-property}/@Element
            IEdmEntityType edmEntityType = edmTypeResolver.GetElementType(entitySet);

            if (typeName != null)
            {
                edmEntityType = this.ResolveTypeCast(entitySet, typeName, readerBehavior, version, edmEntityType);
            }

            IEdmNavigationProperty navigationProperty = this.ResolveNavigationProperty(edmEntityType, propertyName);

            return(navigationProperty);
        }
        /// <summary>
        /// Returns the parse results of the metadata uri if it has a AssociationLink in the uri
        /// </summary>
        /// <param name="edmTypeResolver">Edm Type Resolver to determine entityset type element.</param>
        /// <param name="partCount">Number of split parts the metadata fragment is split into.</param>
        /// <param name="parts">The  actual metadata fragment parts.</param>
        /// <param name="readerBehavior">The reader behavior.</param>
        /// <param name="version">The odata version.</param>
        /// <returns>Returns with an EntityReferenceLink or Links depending on the Uri, sets the parse results with the navigation, and set</returns>
        private ODataPayloadKind ParseAssociationLinks(EdmTypeResolver edmTypeResolver, int partCount, string[] parts, ODataReaderBehavior readerBehavior, ODataVersion version)
        {
            return(this.ResolveEntitySet(
                       parts[0],
                       (IEdmEntitySet resolvedEntitySet) =>
            {
                ODataPayloadKind detectedPayloadKind = ODataPayloadKind.Unsupported;
                IEdmNavigationProperty navigationProperty;
                switch (partCount)
                {
                case 3:
                    if (string.CompareOrdinal(ODataConstants.AssociationLinkSegmentName, parts[1]) == 0)
                    {
                        // Entity reference links: {schema.entity-container.entity-set}/$links/{nav-property}
                        navigationProperty = this.ResolveEntityReferenceLinkMetadataFragment(edmTypeResolver, resolvedEntitySet, (string)null, parts[2], readerBehavior, version);
                        detectedPayloadKind = this.SetEntityLinkParseResults(navigationProperty, null);
                    }
                    else
                    {
                        throw new ODataException(ODataErrorStrings.ODataJsonLightMetadataUriParser_InvalidAssociationLink(UriUtilsCommon.UriToString(this.parseResult.MetadataUri)));
                    }

                    break;

                case 4:
                    if (string.CompareOrdinal(ODataConstants.AssociationLinkSegmentName, parts[1]) == 0)
                    {
                        // Entry with property: {schema.entity-container.entity-set}/$links/{col-nav-property}/@Element
                        // Entry with property: {schema.entity-container.entity-set}/$links/{ref-nav-property}/@Element (invalid, will throw)
                        navigationProperty = this.ResolveEntityReferenceLinkMetadataFragment(edmTypeResolver, resolvedEntitySet, null, parts[2], readerBehavior, version);
                        this.ValidateLinkMetadataUriFragmentItemSelector(parts[3]);
                        detectedPayloadKind = this.SetEntityLinkParseResults(navigationProperty, parts[3]);
                    }
                    else if (string.CompareOrdinal(ODataConstants.AssociationLinkSegmentName, parts[2]) == 0)
                    {
                        // Entry with property: {schema.entity-container.entity-set}/type/$links/{colproperty}
                        navigationProperty = this.ResolveEntityReferenceLinkMetadataFragment(edmTypeResolver, resolvedEntitySet, parts[1], parts[3], readerBehavior, version);
                        detectedPayloadKind = this.SetEntityLinkParseResults(navigationProperty, null);
                    }
                    else
                    {
                        throw new ODataException(ODataErrorStrings.ODataJsonLightMetadataUriParser_InvalidAssociationLink(UriUtilsCommon.UriToString(this.parseResult.MetadataUri)));
                    }

                    break;

                case 5:
                    if (string.CompareOrdinal(ODataConstants.AssociationLinkSegmentName, parts[2]) == 0)
                    {
                        // Entry with property: {schema.entity-container.entity-set}/type/$links/{navproperty}/@Element
                        navigationProperty = this.ResolveEntityReferenceLinkMetadataFragment(edmTypeResolver, resolvedEntitySet, parts[1], parts[3], readerBehavior, version);
                        this.ValidateLinkMetadataUriFragmentItemSelector(parts[2]);
                        detectedPayloadKind = this.SetEntityLinkParseResults(navigationProperty, parts[4]);
                    }
                    else
                    {
                        throw new ODataException(ODataErrorStrings.ODataJsonLightMetadataUriParser_InvalidAssociationLink(UriUtilsCommon.UriToString(this.parseResult.MetadataUri)));
                    }

                    break;

                default:
                    throw new ODataException(ODataErrorStrings.ODataJsonLightMetadataUriParser_InvalidAssociationLink(UriUtilsCommon.UriToString(this.parseResult.MetadataUri)));
                }

                return detectedPayloadKind;
            }));
        }
        private ODataPayloadKind ParseMetadataUriFragment(string fragment, ODataReaderBehavior readerBehavior, ODataVersion version)
        {
            // remove any query options like $select that may have been embedded in the fragment.
            int indexOfQueryOptionSeparator = fragment.IndexOf(JsonLightConstants.MetadataUriQueryOptionSeparator);

            if (indexOfQueryOptionSeparator > 0)
            {
                // Extract the $select query option value from the fragment if it exists
                string fragmentQueryOptions = fragment.Substring(indexOfQueryOptionSeparator);
                this.parseResult.SelectQueryOption = ExtractSelectQueryOption(fragmentQueryOptions);

                fragment = fragment.Substring(0, indexOfQueryOptionSeparator);
            }

            string[] parts     = fragment.Split(JsonLightConstants.MetadataUriFragmentPartSeparator);
            int      partCount = parts.Length;

            ODataPayloadKind detectedPayloadKind = ODataPayloadKind.Unsupported;
            EdmTypeResolver  edmTypeResolver     = new EdmTypeReaderResolver(this.model, readerBehavior, version);

            var foundAssociationLinksSegment = fragment.IndexOf(ODataConstants.AssociationLinkSegmentName, StringComparison.Ordinal);

            if (foundAssociationLinksSegment > -1)
            {
                detectedPayloadKind = this.ParseAssociationLinks(edmTypeResolver, partCount, parts, readerBehavior, version);
            }
            else
            {
                switch (partCount)
                {
                case 1:
                    // Service document: no fragment
                    if (fragment.Length == 0)
                    {
                        detectedPayloadKind = ODataPayloadKind.ServiceDocument;
                        break;
                    }

                    if (parts[0].Equals(JsonLightConstants.MetadataUriFragmentNull, StringComparison.OrdinalIgnoreCase))
                    {
                        detectedPayloadKind             = ODataPayloadKind.Property;
                        this.parseResult.IsNullProperty = true;
                        break;
                    }

                    IEdmEntitySet entitySet = this.model.ResolveEntitySet(parts[0]);
                    if (entitySet != null)
                    {
                        // Feed: {schema.entity-container.entity-set}
                        this.parseResult.EntitySet = entitySet;
                        this.parseResult.EdmType   = edmTypeResolver.GetElementType(entitySet);
                        detectedPayloadKind        = ODataPayloadKind.Feed;
                        break;
                    }

                    // Property: {schema.type} or Collection({schema.type}) where schema.type is primitive or complex.
                    this.parseResult.EdmType = this.ResolveType(parts[0], readerBehavior, version);
                    Debug.Assert(
                        this.parseResult.EdmType.TypeKind == EdmTypeKind.Primitive || this.parseResult.EdmType.TypeKind == EdmTypeKind.Complex || this.parseResult.EdmType.IsNonEntityCollectionType(),
                        "The first metadata URI segment must be a set or a non-entity type.");
                    detectedPayloadKind = this.parseResult.EdmType is IEdmCollectionType ? ODataPayloadKind.Collection : ODataPayloadKind.Property;

                    break;

                case 2:
                    // Entry: {schema.entity-container.entity-set}/@Element
                    // Feed with type cast: {schema.entity-container.entity-set}/{type-cast}
                    detectedPayloadKind = this.ResolveEntitySet(
                        parts[0],
                        (IEdmEntitySet resolvedEntitySet) =>
                    {
                        IEdmEntityType entitySetElementType = edmTypeResolver.GetElementType(resolvedEntitySet);

                        if (string.CompareOrdinal(JsonLightConstants.MetadataUriFragmentItemSelector, parts[1]) == 0)
                        {
                            this.parseResult.EdmType = entitySetElementType;
                            return(ODataPayloadKind.Entry);
                        }
                        else
                        {
                            this.parseResult.EdmType = this.ResolveTypeCast(resolvedEntitySet, parts[1], readerBehavior, version, entitySetElementType);
                            return(ODataPayloadKind.Feed);
                        }
                    });

                    break;

                case 3:
                    detectedPayloadKind = this.ResolveEntitySet(
                        parts[0],
                        (IEdmEntitySet resolvedEntitySet) =>
                    {
                        IEdmEntityType entitySetElementType = edmTypeResolver.GetElementType(resolvedEntitySet);

                        // Entry with type cast: {schema.entity-container.entity-set}/{type-cast}/@Element
                        this.parseResult.EdmType = this.ResolveTypeCast(resolvedEntitySet, parts[1], readerBehavior, version, entitySetElementType);
                        this.ValidateMetadataUriFragmentItemSelector(parts[2]);
                        return(ODataPayloadKind.Entry);
                    });

                    break;

                default:
                    throw new ODataException(ODataErrorStrings.ODataJsonLightMetadataUriParser_FragmentWithInvalidNumberOfParts(UriUtilsCommon.UriToString(this.parseResult.MetadataUri), partCount, 3));
                }
            }

            return(detectedPayloadKind);
        }
        /// <summary>
        /// Applies the model and validates the context URI against it.
        /// </summary>
        /// <param name="expectedPayloadKind">The payload kind we expect the context URI to conform to.</param>
        /// <param name="readerBehavior">Reader behavior if the caller is a reader, null if no reader behavior is available.</param>
        private void ParseContextUri(ODataPayloadKind expectedPayloadKind, ODataReaderBehavior readerBehavior)
        {
            ODataPayloadKind detectedPayloadKind = this.ParseContextUriFragment(this.parseResult.Fragment, readerBehavior);

            // unsupported payload kind indicates that this is during payload kind detection, so we should not fail.
            bool detectedPayloadKindMatchesExpectation = detectedPayloadKind == expectedPayloadKind || expectedPayloadKind == ODataPayloadKind.Unsupported;
            if (detectedPayloadKind == ODataPayloadKind.Collection)
            {
                // If the detected payload kind is 'collection' it can always also be treated as a property.
                this.parseResult.DetectedPayloadKinds = new[] { ODataPayloadKind.Collection, ODataPayloadKind.Property };
                if (expectedPayloadKind == ODataPayloadKind.Property)
                {
                    detectedPayloadKindMatchesExpectation = true;
                }
            }
            else if (detectedPayloadKind == ODataPayloadKind.Entry)
            {
                this.parseResult.DetectedPayloadKinds = new[] { ODataPayloadKind.Entry, ODataPayloadKind.Delta };
                if (expectedPayloadKind == ODataPayloadKind.Delta)
                {
                    this.parseResult.DeltaKind = ODataDeltaKind.Entry;
                    detectedPayloadKindMatchesExpectation = true;
                }
            }
            else
            {
                this.parseResult.DetectedPayloadKinds = new[] { detectedPayloadKind };
            }

            // If the expected and detected payload kinds don't match and we are not running payload kind detection
            // right now (payloadKind == ODataPayloadKind.Unsupported) and we did not detect a collection kind for
            // an expected property kind (which is allowed), fail.
            if (!detectedPayloadKindMatchesExpectation)
            {
                throw new ODataException(ODataErrorStrings.ODataJsonLightContextUriParser_ContextUriDoesNotMatchExpectedPayloadKind(UriUtils.UriToString(this.parseResult.ContextUri), expectedPayloadKind.ToString()));
            }

            // NOTE: we interpret an empty select query option to mean that nothing should be projected
            //       (whereas a missing select query option means everything should be projected).
            string selectQueryOption = this.parseResult.SelectQueryOption;
            if (selectQueryOption != null)
            {
                if (detectedPayloadKind != ODataPayloadKind.Feed && detectedPayloadKind != ODataPayloadKind.Entry && detectedPayloadKind != ODataPayloadKind.Delta)
                {
                    throw new ODataException(ODataErrorStrings.ODataJsonLightContextUriParser_InvalidPayloadKindWithSelectQueryOption(expectedPayloadKind.ToString()));
                }
            }
        }
Example #22
0
 /// <summary>Creates a new entity set element type resolver with all the information needed when resolving for reading scenarios.</summary>
 /// <param name="model">The model to use or null if no model is available.</param>
 /// <param name="readerBehavior">Reader behavior if the caller is a reader, null if no reader behavior is available.</param>
 public EdmTypeReaderResolver(IEdmModel model, ODataReaderBehavior readerBehavior)
 {
     this.model          = model;
     this.readerBehavior = readerBehavior;
 }
Example #23
0
        /// <summary>
        /// Resolves the name of a primitive, complex, entity or collection type to the respective type. Uses the semantics used be readers.
        /// Thus it can be a bit looser.
        /// </summary>
        /// <param name="model">The model to use or null if no model is available.</param>
        /// <param name="expectedType">The expected type for the type name being resolved, or null if none is available.</param>
        /// <param name="typeName">The name of the type to resolve.</param>
        /// <param name="readerBehavior">Reader behavior if the caller is a reader, null if no reader behavior is available.</param>
        /// <param name="version">The version of the payload being read.</param>
        /// <param name="typeKind">The type kind of the type, if it could be determined. This will be None if we couldn't tell. It might be filled
        /// even if the method returns null, for example for Collection types with item types which are not recognized.</param>
        /// <returns>The <see cref="IEdmType"/> representing the type specified by the <paramref name="typeName"/>;
        /// or null if no such type could be found.</returns>
        internal static IEdmType ResolveTypeNameForRead(
            IEdmModel model,
            IEdmType expectedType,
            string typeName,
            ODataReaderBehavior readerBehavior,
            ODataVersion version,
            out EdmTypeKind typeKind)
        {
            DebugUtils.CheckNoExternalCallers();
            Func<IEdmType, string, IEdmType> customTypeResolver = readerBehavior == null ? null : readerBehavior.TypeResolver;
            Debug.Assert(
                customTypeResolver == null || readerBehavior.ApiBehaviorKind == ODataBehaviorKind.WcfDataServicesClient,
                "Custom type resolver can only be specified in WCF DS Client behavior.");

            return ResolveTypeName(model, expectedType, typeName, customTypeResolver, version, out typeKind);
        }
 internal static object ConvertValue(object value, IEdmPrimitiveTypeReference primitiveTypeReference, ODataMessageReaderSettings messageReaderSettings, ODataVersion version, bool validateNullValue)
 {
     if (value == null)
     {
         ReaderValidationUtils.ValidateNullValue(EdmCoreModel.Instance, primitiveTypeReference, messageReaderSettings, validateNullValue, version);
         return(null);
     }
     try
     {
         Type primitiveClrType = EdmLibraryExtensions.GetPrimitiveClrType(primitiveTypeReference.PrimitiveDefinition(), false);
         ODataReaderBehavior readerBehavior = messageReaderSettings.ReaderBehavior;
         string stringValue = value as string;
         if (stringValue != null)
         {
             return(ConvertStringValue(stringValue, primitiveClrType, version));
         }
         if (value is int)
         {
             return(ConvertInt32Value((int)value, primitiveClrType, primitiveTypeReference, (readerBehavior != null) && readerBehavior.UseV1ProviderBehavior));
         }
         if (value is double)
         {
             double num = (double)value;
             if (primitiveClrType == typeof(float))
             {
                 return(Convert.ToSingle(num));
             }
             if (!IsV1PrimitiveType(primitiveClrType) || ((primitiveClrType != typeof(double)) && ((readerBehavior == null) || !readerBehavior.UseV1ProviderBehavior)))
             {
                 throw new ODataException(Microsoft.Data.OData.Strings.ODataJsonReaderUtils_CannotConvertDouble(primitiveTypeReference.ODataFullName()));
             }
             return(value);
         }
         if (value is bool)
         {
             if ((primitiveClrType != typeof(bool)) && ((readerBehavior == null) || (readerBehavior.FormatBehaviorKind != ODataBehaviorKind.WcfDataServicesServer)))
             {
                 throw new ODataException(Microsoft.Data.OData.Strings.ODataJsonReaderUtils_CannotConvertBoolean(primitiveTypeReference.ODataFullName()));
             }
             return(value);
         }
         if (value is DateTime)
         {
             if ((primitiveClrType != typeof(DateTime)) && ((readerBehavior == null) || (readerBehavior.FormatBehaviorKind != ODataBehaviorKind.WcfDataServicesServer)))
             {
                 throw new ODataException(Microsoft.Data.OData.Strings.ODataJsonReaderUtils_CannotConvertDateTime(primitiveTypeReference.ODataFullName()));
             }
             return(value);
         }
         if ((value is DateTimeOffset) && (primitiveClrType != typeof(DateTimeOffset)))
         {
             throw new ODataException(Microsoft.Data.OData.Strings.ODataJsonReaderUtils_CannotConvertDateTimeOffset(primitiveTypeReference.ODataFullName()));
         }
     }
     catch (Exception exception)
     {
         if (!ExceptionUtils.IsCatchableExceptionType(exception))
         {
             throw;
         }
         throw ReaderValidationUtils.GetPrimitiveTypeConversionException(primitiveTypeReference, exception);
     }
     return(value);
 }
Example #25
0
        public void CalculateBindableOperationsForDerivedEntityTypeWithTypeResolver()
        {
            var bindingType        = this.model.FindType("TestModel.TVMovie");
            var bindableOperations = MetadataUtils.CalculateBindableOperationsForType(bindingType, this.model, new EdmTypeReaderResolver(this.model, ODataReaderBehavior.CreateWcfDataServicesClientBehavior(this.NameToTypeResolver)));

            Assert.Equal(2, bindableOperations.Length);
            Assert.True(bindableOperations.Count(o => o.Name == "Rate") == 1);
            Assert.True(bindableOperations.Count(o => o.Name == "ChangeChannel") == 1);
        }
Example #26
0
        public void CalculateBindableOperationsForEntityTypeWithTypeResolver()
        {
            var bindingType        = this.model.FindType("TestModel.Movie");
            var bindableOperations = MetadataUtils.CalculateBindableOperationsForType(bindingType, this.model, new EdmTypeReaderResolver(this.model, ODataReaderBehavior.CreateWcfDataServicesClientBehavior(this.NameToTypeResolver)));

            Assert.AreEqual(1, bindableOperations.Length);
            foreach (var operation in bindableOperations)
            {
                Assert.AreEqual("Rate", operation.Name);
            }
        }
        /// <summary>
        /// Converts the given JSON value to the expected type as per OData conversion rules for JSON values.
        /// </summary>
        /// <param name="value">Value to the converted.</param>
        /// <param name="primitiveTypeReference">Type reference to which the value needs to be converted.</param>
        /// <param name="messageReaderSettings">The message reader settings used for reading.</param>
        /// <param name="version">The version of the OData protocol used for reading.</param>
        /// <param name="validateNullValue">true to validate null values; otherwise false.</param>
        /// <returns>Object which is in sync with the property type (modulo the V1 exception of converting numbers to non-compatible target types).</returns>
        internal static object ConvertValue(
            object value,
            IEdmPrimitiveTypeReference primitiveTypeReference,
            ODataMessageReaderSettings messageReaderSettings,
            ODataVersion version,
            bool validateNullValue)
        {
            DebugUtils.CheckNoExternalCallers();
            Debug.Assert(primitiveTypeReference != null, "primitiveTypeReference != null");

            //// NOTE: this method was copied from WCF DS (and changed to take a type reference instead of a CLR target type)

            if (value == null)
            {
                // Only primitive type references are validated. Core model is sufficient.
                ReaderValidationUtils.ValidateNullValue(EdmCoreModel.Instance, primitiveTypeReference, messageReaderSettings, validateNullValue, version);
                return(null);
            }

            try
            {
                Type targetType = EdmLibraryExtensions.GetPrimitiveClrType(primitiveTypeReference.PrimitiveDefinition(), false);
                ODataReaderBehavior readerBehavior = messageReaderSettings.ReaderBehavior;

                string stringValue = value as string;
                if (stringValue != null)
                {
                    return(ConvertStringValue(stringValue, targetType, version));
                }
                else if (value is Int32)
                {
                    return(ConvertInt32Value((int)value, targetType, primitiveTypeReference, readerBehavior == null ? false : readerBehavior.UseV1ProviderBehavior));
                }
                else if (value is Double)
                {
                    Double doubleValue = (Double)value;
                    if (targetType == typeof(Single))
                    {
                        return(Convert.ToSingle(doubleValue));
                    }

                    if (!IsV1PrimitiveType(targetType) || (targetType != typeof(Double) && (readerBehavior == null || !readerBehavior.UseV1ProviderBehavior)))
                    {
                        throw new ODataException(o.Strings.ODataJsonReaderUtils_CannotConvertDouble(primitiveTypeReference.ODataFullName()));
                    }
                }
                else if (value is bool)
                {
                    if (targetType != typeof(bool) && (readerBehavior == null || readerBehavior.FormatBehaviorKind != ODataBehaviorKind.WcfDataServicesServer))
                    {
                        throw new ODataException(o.Strings.ODataJsonReaderUtils_CannotConvertBoolean(primitiveTypeReference.ODataFullName()));
                    }
                }
                else if (value is DateTime)
                {
                    if (targetType != typeof(DateTime) && (readerBehavior == null || readerBehavior.FormatBehaviorKind != ODataBehaviorKind.WcfDataServicesServer))
                    {
                        throw new ODataException(o.Strings.ODataJsonReaderUtils_CannotConvertDateTime(primitiveTypeReference.ODataFullName()));
                    }
                }
                else if (value is DateTimeOffset)
                {
                    // Currently, we do not support any conversion for DateTimeOffset date type. Hence failing if the target
                    // type is not DateTimeOffset.
                    if (targetType != typeof(DateTimeOffset))
                    {
                        throw new ODataException(o.Strings.ODataJsonReaderUtils_CannotConvertDateTimeOffset(primitiveTypeReference.ODataFullName()));
                    }
                }
            }
            catch (Exception e)
            {
                if (!ExceptionUtils.IsCatchableExceptionType(e))
                {
                    throw;
                }

                throw ReaderValidationUtils.GetPrimitiveTypeConversionException(primitiveTypeReference, e);
            }

            // otherwise just return the value without doing any conversion
            return(value);
        }
        private ODataPayloadKind ParseContextUriFragment(string fragment, ODataReaderBehavior readerBehavior)
        {
            bool hasItemSelector = false;
            ODataDeltaKind kind = ODataDeltaKind.None;

            // Deal with /$entity
            if (fragment.EndsWith(ODataConstants.ContextUriFragmentItemSelector, StringComparison.Ordinal))
            {
                hasItemSelector = true;
                fragment = fragment.Substring(0, fragment.Length - ODataConstants.ContextUriFragmentItemSelector.Length);
            }
            else if (fragment.EndsWith(ODataConstants.ContextUriDeltaFeed, StringComparison.Ordinal))
            {
                kind = ODataDeltaKind.Feed;
                fragment = fragment.Substring(0, fragment.Length - ODataConstants.ContextUriDeltaFeed.Length);
            }
            else if (fragment.EndsWith(ODataConstants.ContextUriDeletedEntry, StringComparison.Ordinal))
            {
                kind = ODataDeltaKind.DeletedEntry;
                fragment = fragment.Substring(0, fragment.Length - ODataConstants.ContextUriDeletedEntry.Length);
            }
            else if (fragment.EndsWith(ODataConstants.ContextUriDeltaLink, StringComparison.Ordinal))
            {
                kind = ODataDeltaKind.Link;
                fragment = fragment.Substring(0, fragment.Length - ODataConstants.ContextUriDeltaLink.Length);
            }
            else if (fragment.EndsWith(ODataConstants.ContextUriDeletedLink, StringComparison.Ordinal))
            {
                kind = ODataDeltaKind.DeletedLink;
                fragment = fragment.Substring(0, fragment.Length - ODataConstants.ContextUriDeletedLink.Length);
            }

            this.parseResult.DeltaKind = kind;

            // Deal with query option
            if (fragment.EndsWith(")", StringComparison.Ordinal))
            {
                int index = fragment.Length - 2;
                for (int rcount = 1; rcount > 0 && index > 0; --index)
                {
                    switch (fragment[index])
                    {
                        case '(':
                            rcount--;
                            break;
                        case ')':
                            rcount++;
                            break;
                    }
                }

                if (index == 0)
                {
                    throw new ODataException(ODataErrorStrings.ODataJsonLightContextUriParser_InvalidContextUrl(UriUtils.UriToString(this.parseResult.ContextUri)));
                }

                string previous = fragment.Substring(0, index + 1);

                // Don't treat Collection(Edm.Type) as SelectExpand segment
                if (!previous.Equals("Collection"))
                {
                    string selectExpandStr = fragment.Substring(index + 2);
                    selectExpandStr = selectExpandStr.Substring(0, selectExpandStr.Length - 1);

                    // Do not treat Key as SelectExpand segment
                    if (KeyPattern.IsMatch(selectExpandStr))
                    {
                        throw new ODataException(ODataErrorStrings.ODataJsonLightContextUriParser_LastSegmentIsKeySegment(UriUtils.UriToString(this.parseResult.ContextUri)));
                    }

                    this.parseResult.SelectQueryOption = ExtractSelectQueryOption(selectExpandStr);
                    fragment = previous;
                }
            }

            ODataPayloadKind detectedPayloadKind = ODataPayloadKind.Unsupported;
            EdmTypeResolver edmTypeResolver = new EdmTypeReaderResolver(this.model, readerBehavior);

            if (!fragment.Contains(ODataConstants.UriSegmentSeparator) && !hasItemSelector && kind == ODataDeltaKind.None)
            {
                // Service document: no fragment
                if (fragment.Length == 0)
                {
                    detectedPayloadKind = ODataPayloadKind.ServiceDocument;
                }
                else if (fragment.Equals(ODataConstants.ContextUriFragmentNull, StringComparison.OrdinalIgnoreCase))
                {
                    detectedPayloadKind = ODataPayloadKind.Property;
                    this.parseResult.IsNullProperty = true;
                }
                else if (fragment.Equals(ODataConstants.EntityReferenceCollectionSegmentName + "(" + ODataConstants.EntityReferenceSegmentName + ")"))
                {
                    detectedPayloadKind = ODataPayloadKind.EntityReferenceLinks;
                }
                else if (fragment.Equals(ODataConstants.EntityReferenceSegmentName))
                {
                    detectedPayloadKind = ODataPayloadKind.EntityReferenceLink;
                }
                else
                {
                    var foundNavigationSource = this.model.FindDeclaredNavigationSource(fragment);

                    if (foundNavigationSource != null)
                    {
                        // Feed: {schema.entity-container.entity-set} or Singleton: {schema.entity-container.singleton}
                        this.parseResult.NavigationSource = foundNavigationSource;
                        this.parseResult.EdmType = edmTypeResolver.GetElementType(foundNavigationSource);
                        detectedPayloadKind = foundNavigationSource is IEdmSingleton ? ODataPayloadKind.Entry : ODataPayloadKind.Feed;
                    }
                    else
                    {
                        // Property: {schema.type} or Collection({schema.type}) where schema.type is primitive or complex.
                        detectedPayloadKind = this.ResolveType(fragment, readerBehavior);
                        Debug.Assert(
                            this.parseResult.EdmType.TypeKind == EdmTypeKind.Primitive || this.parseResult.EdmType.TypeKind == EdmTypeKind.Enum || this.parseResult.EdmType.TypeKind == EdmTypeKind.TypeDefinition || this.parseResult.EdmType.TypeKind == EdmTypeKind.Complex || this.parseResult.EdmType.TypeKind == EdmTypeKind.Collection || this.parseResult.EdmType.TypeKind == EdmTypeKind.Entity,
                            "The first context URI segment must be a set or a non-entity type.");
                    }
                }
            }
            else
            {
                Debug.Assert(this.parseResult.MetadataDocumentUri.IsAbsoluteUri, "this.parseResult.MetadataDocumentUri.IsAbsoluteUri");

                string metadataDocumentStr = UriUtils.UriToString(this.parseResult.MetadataDocumentUri);

                if (!metadataDocumentStr.EndsWith(ODataConstants.UriMetadataSegment, StringComparison.Ordinal))
                {
                    throw new ODataException(ODataErrorStrings.ODataJsonLightContextUriParser_InvalidContextUrl(UriUtils.UriToString(this.parseResult.ContextUri)));
                }

                Uri serviceRoot = new Uri(metadataDocumentStr.Substring(0, metadataDocumentStr.Length - ODataConstants.UriMetadataSegment.Length));

                ODataUriParser odataUriParser = new ODataUriParser(this.model, serviceRoot, new Uri(serviceRoot, fragment));

                ODataPath path;
                try
                {
                    path = odataUriParser.ParsePath();
                }
                catch (ODataException)
                {
                    throw new ODataException(ODataErrorStrings.ODataJsonLightContextUriParser_InvalidContextUrl(UriUtils.UriToString(this.parseResult.ContextUri)));
                }

                if (path.Count == 0)
                {
                    throw new ODataException(ODataErrorStrings.ODataJsonLightContextUriParser_InvalidContextUrl(UriUtils.UriToString(this.parseResult.ContextUri)));
                }

                this.parseResult.Path = path;

                parseResult.NavigationSource = path.NavigationSource();
                parseResult.EdmType = path.LastSegment.EdmType;

                ODataPathSegment lastSegment = path.TrimEndingTypeSegment().LastSegment;
                if (lastSegment is EntitySetSegment || lastSegment is NavigationPropertySegment)
                {
                    if (kind != ODataDeltaKind.None)
                    {
                        detectedPayloadKind = ODataPayloadKind.Delta;
                    }
                    else
                    {
                        detectedPayloadKind = hasItemSelector ? ODataPayloadKind.Entry : ODataPayloadKind.Feed;
                    }

                    if (this.parseResult.EdmType is IEdmCollectionType)
                    {
                        var collectionTypeReference = this.parseResult.EdmType.ToTypeReference().AsCollection();
                        if (collectionTypeReference != null)
                        {
                            this.parseResult.EdmType = collectionTypeReference.ElementType().Definition;
                        }
                    }
                }
                else if (lastSegment is SingletonSegment)
                {
                    detectedPayloadKind = ODataPayloadKind.Entry;
                }
                else if (path.IsIndividualProperty())
                {
                    detectedPayloadKind = ODataPayloadKind.Property;

                    IEdmCollectionType collectionType = parseResult.EdmType as IEdmCollectionType;
                    if (collectionType != null)
                    {
                        detectedPayloadKind = ODataPayloadKind.Collection;
                    }
                }
                else
                {
                    throw new ODataException(ODataErrorStrings.ODataJsonLightContextUriParser_InvalidContextUrl(UriUtils.UriToString(this.parseResult.ContextUri)));
                }
            }

            return detectedPayloadKind;
        }