Beispiel #1
0
        /// <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;
        }