/// <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); }
/// <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; }
/// <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())); } } }
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); }
/// <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); }
/// <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; }
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)); }
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; }
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> /// 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); }
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); }
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; }