/// <summary> /// Returns true if the compared type is semantically equivalent to this type. /// Schema types (<see cref="IEdmSchemaType"/>) are compared by their object refs. /// </summary> /// <param name="thisType">Type being compared.</param> /// <param name="otherType">Type being compared to.</param> /// <returns>Equivalence of the two types.</returns> public static bool IsEquivalentTo(this IEdmType thisType, IEdmType otherType) { if (thisType == otherType) { return(true); } if (thisType == null || otherType == null) { return(false); } thisType = thisType.AsActualType(); otherType = otherType.AsActualType(); if (thisType.TypeKind != otherType.TypeKind) { return(false); } switch (thisType.TypeKind) { case EdmTypeKind.Primitive: return(((IEdmPrimitiveType)thisType).IsEquivalentTo((IEdmPrimitiveType)otherType)); case EdmTypeKind.Complex: case EdmTypeKind.Entity: return(((IEdmSchemaType)thisType).IsEquivalentTo((IEdmSchemaType)otherType)); case EdmTypeKind.Enum: return(((IEdmEnumType)thisType).IsEquivalentTo((IEdmEnumType)otherType)); case EdmTypeKind.Collection: return(((IEdmCollectionType)thisType).IsEquivalentTo((IEdmCollectionType)otherType)); case EdmTypeKind.EntityReference: return(((IEdmEntityReferenceType)thisType).IsEquivalentTo((IEdmEntityReferenceType)otherType)); case EdmTypeKind.None: return(otherType.TypeKind == EdmTypeKind.None); default: throw new InvalidOperationException(Edm.Strings.UnknownEnumVal_TypeKind(thisType.TypeKind)); } }
/// <summary> /// Returns true if the compared type is semantically equivalent to this type. /// Schema types (<see cref="IEdmSchemaType"/>) are compared by their object refs. /// </summary> /// <param name="thisType">Type being compared.</param> /// <param name="otherType">Type being compared to.</param> /// <returns>Equivalence of the two types.</returns> public static bool IsEquivalentTo(this IEdmType thisType, IEdmType otherType) { if (thisType == otherType) { return true; } if (thisType == null || otherType == null) { return false; } thisType = thisType.AsActualType(); otherType = otherType.AsActualType(); if (thisType.TypeKind != otherType.TypeKind) { return false; } switch (thisType.TypeKind) { case EdmTypeKind.Primitive: return ((IEdmPrimitiveType)thisType).IsEquivalentTo((IEdmPrimitiveType)otherType); case EdmTypeKind.Complex: case EdmTypeKind.Entity: case EdmTypeKind.Enum: return ((IEdmSchemaType)thisType).IsEquivalentTo((IEdmSchemaType)otherType); case EdmTypeKind.Collection: return ((IEdmCollectionType)thisType).IsEquivalentTo((IEdmCollectionType)otherType); case EdmTypeKind.EntityReference: return ((IEdmEntityReferenceType)thisType).IsEquivalentTo((IEdmEntityReferenceType)otherType); case EdmTypeKind.None: return otherType.TypeKind == EdmTypeKind.None; default: throw new InvalidOperationException(Edm.Strings.UnknownEnumVal_TypeKind(thisType.TypeKind)); } }
/// <summary> /// Resolves the primitive payload type versus the expected type and validates that such combination is allowed. /// </summary> /// <param name="expectedTypeReference">The expected type reference, if any.</param> /// <param name="payloadTypeKind">The kind of the payload type, or None if the detection was not possible.</param> /// <param name="payloadType">The resolved payload type, or null if no payload type was specified.</param> /// <param name="payloadTypeName">The name of the payload type, or null if no payload type was specified.</param> /// <param name="defaultPayloadType">The default payload type if none is specified in the payload; /// for ATOM this is Edm.String, for JSON it is null since there is no payload type name for primitive types in the payload.</param> /// <param name="model">The model to use.</param> /// <param name="messageReaderSettings">The message reader settings to use.</param> /// <returns>The target type reference to use for parsing the value. This method never returns null.</returns> internal static IEdmTypeReference ResolveAndValidatePrimitiveTargetType( IEdmTypeReference expectedTypeReference, EdmTypeKind payloadTypeKind, IEdmType payloadType, string payloadTypeName, IEdmType defaultPayloadType, IEdmModel model, ODataMessageReaderSettings messageReaderSettings) { Debug.Assert(messageReaderSettings != null, "messageReaderSettings != null"); Debug.Assert( payloadTypeKind == EdmTypeKind.Primitive || payloadTypeKind == EdmTypeKind.Complex || payloadTypeKind == EdmTypeKind.Entity || payloadTypeKind == EdmTypeKind.Collection || payloadTypeKind == EdmTypeKind.None || payloadTypeKind == EdmTypeKind.TypeDefinition, "The payload type kind must be one of None, Primitive, Complex, Entity, Collection or TypeDefinition."); Debug.Assert( expectedTypeReference == null || expectedTypeReference.TypeKind() == EdmTypeKind.Primitive, "This method only works for primitive expected type."); Debug.Assert( payloadType == null || payloadType.TypeKind == payloadTypeKind, "The payload type kind must match the payload type if that is available."); Debug.Assert(payloadType == null || payloadTypeName != null, "If we have a payload type, we must have its name as well."); bool useExpectedTypeOnlyForTypeResolution = messageReaderSettings.ReaderBehavior.TypeResolver != null && payloadType != null; if (expectedTypeReference != null && !useExpectedTypeOnlyForTypeResolution) { ValidateTypeSupported(expectedTypeReference); } // Validate type kinds except for open properties or when in lax mode, but only if primitive type conversion is enabled. // If primitive type conversion is disabled, the type kind must match, no matter what validation mode is used. // The rules for primitive types are: // - In the strict mode the payload value must be convertible to the expected type. So the payload type must be a primitive type. // - In the lax mode the payload type is ignored, so its type kind is not verified in any way // - If the DisablePrimitiveTypeConversion == true, the lax/strict mode doesn't matter and we will read the payload value on its own. // In this case we require the payload value to always be a primitive type (so type kinds must match), but it may not be convertible // to the expected type, it will still be reported to the caller. if (payloadTypeKind != EdmTypeKind.None && (messageReaderSettings.DisablePrimitiveTypeConversion || !messageReaderSettings.DisableStrictMetadataValidation)) { // Make sure that the type kinds match. ValidationUtils.ValidateTypeKind(payloadTypeKind, EdmTypeKind.Primitive, payloadTypeName); } if (!model.IsUserModel()) { // If there's no model, it means we should not have the expected type either, and that there's no type to use, // no metadata validation to perform. Debug.Assert(expectedTypeReference == null, "If we don't have a model, we must not have expected type either."); return MetadataUtils.GetNullablePayloadTypeReference(payloadType ?? defaultPayloadType); } // If the primitive type conversion is off, use the payload type always. // If there's no expected type or the expected type is ignored, use the payload type as well. if (expectedTypeReference == null || useExpectedTypeOnlyForTypeResolution || messageReaderSettings.DisablePrimitiveTypeConversion) { // If there's no payload type, use the default payload type. // Note that in collections the items without type should inherit the type name from the collection, in that case the expectedTypeReference // is never null (assuming we do have a model), so we won't get here. return MetadataUtils.GetNullablePayloadTypeReference(payloadType ?? defaultPayloadType); } // The server ignores the payload type when expected type is specified // The server is going to use lax mode everywhere so this is not an issue. if (messageReaderSettings.DisableStrictMetadataValidation) { // Lax validation logic // Always use the expected type, the payload type is ignored. return expectedTypeReference; } // Strict validation logic // We assume the expected type in the case where no payload type is specified // for a primitive value (in strict mode); if no expected type is available we assume Edm.String. if (payloadType != null) { // The payload type must be convertible to the expected type. // Note that we compare the type definitions, since we want to ignore nullability (the payload type doesn't specify nullability). if (!MetadataUtilsCommon.CanConvertPrimitiveTypeTo( null /* sourceNodeOrNull */, (IEdmPrimitiveType)payloadType.AsActualType(), (IEdmPrimitiveType)(expectedTypeReference.Definition))) { throw new ODataException(Strings.ValidationUtils_IncompatibleType(payloadTypeName, expectedTypeReference.ODataFullName())); } } // Read using the expected type. // If there was a payload type we verified that it's convertible and thus we can safely read // the content of the value as the expected type. return expectedTypeReference; }