/// <summary> /// Validates that the expected primitive type or type definition matches the actual primitive type. /// </summary> /// <param name="expectedTypeReference">The expected type.</param> /// <param name="typeReferenceFromValue">The actual type.</param> internal static void ValidateMetadataPrimitiveType(IEdmTypeReference expectedTypeReference, IEdmTypeReference typeReferenceFromValue) { Debug.Assert(expectedTypeReference != null && (expectedTypeReference.IsODataPrimitiveTypeKind() || expectedTypeReference.IsODataTypeDefinitionTypeKind()), "expectedTypeReference must be a primitive type or type definition."); Debug.Assert(typeReferenceFromValue != null && typeReferenceFromValue.IsODataPrimitiveTypeKind(), "typeReferenceFromValue must be a primitive type."); IEdmType expectedType = expectedTypeReference.Definition; IEdmPrimitiveType typeFromValue = (IEdmPrimitiveType)typeReferenceFromValue.Definition; // The two primitive types match if they have the same definition and either both or only the // expected type is nullable // NOTE: for strings and binary values we must not check nullability here because the type reference // from the value is always nullable since C# has no way to express non-nullable strings. // However, this codepath is only hit if the value is not 'null' so we can assign a non-null // value to both nullable and non-nullable string/binary types. bool nullableCompatible = expectedTypeReference.IsNullable == typeReferenceFromValue.IsNullable || expectedTypeReference.IsNullable && !typeReferenceFromValue.IsNullable || !MetadataUtilsCommon.IsODataValueType(typeReferenceFromValue); bool typeCompatible = expectedType.IsAssignableFrom(typeFromValue); if (!nullableCompatible || !typeCompatible) { // incompatible type name for value! throw new ODataException(Strings.ValidationUtils_IncompatiblePrimitiveItemType( typeReferenceFromValue.FullName(), typeReferenceFromValue.IsNullable, expectedTypeReference.FullName(), expectedTypeReference.IsNullable)); } }
/// <summary> /// If the source node is not of the specified type, then we check if type promotion is possible and inject a convert node. /// If the source node is the same type as the target type (or if the target type is null), we just return the source node as is. /// </summary> /// <param name="source">The source node to apply the convertion to.</param> /// <param name="targetTypeReference">The target primitive type. May be null - this method will do nothing in that case.</param> /// <returns>The converted query node, or the original source node unchanged.</returns> internal static SingleValueNode ConvertToTypeIfNeeded(SingleValueNode source, IEdmTypeReference targetTypeReference) { Debug.Assert(source != null, "source != null"); if (targetTypeReference == null) { return(source); } if (source.TypeReference != null) { if (source.TypeReference.IsEquivalentTo(targetTypeReference)) { return(source); } if (!TypePromotionUtils.CanConvertTo(source, source.TypeReference, targetTypeReference)) { throw new ODataException(ODataErrorStrings.MetadataBinder_CannotConvertToType(source.TypeReference.ODataFullName(), targetTypeReference.ODataFullName())); } else { ConstantNode constantNode = source as ConstantNode; if (source.TypeReference.IsEnum() && constantNode != null) { return(new ConstantNode(constantNode.Value, ODataUriUtils.ConvertToUriLiteral(constantNode.Value, ODataVersion.V4), targetTypeReference)); } object primitiveValue; if (MetadataUtilsCommon.TryGetConstantNodePrimitiveLDMF(source, out primitiveValue) && (primitiveValue != null)) { // L F D M types : directly create a ConvertNode. // 1. NodeToExpressionTranslator.cs won't allow implicitly converting single/double to decimal, which should be done here at Node tree level. // 2. And prevent losing precision in float -> double, e.g. (double)1.234f => 1.2339999675750732d not 1.234d object primitiveValue2 = ODataUriConversionUtils.CoerceNumericType(primitiveValue, targetTypeReference.AsPrimitive().Definition as IEdmPrimitiveType); if (string.IsNullOrEmpty(constantNode.LiteralText)) { return(new ConstantNode(primitiveValue2)); } return(new ConstantNode(primitiveValue2, constantNode.LiteralText)); } else { // other type conversion : ConvertNode return(new ConvertNode(source, targetTypeReference)); } } } else { // If the source doesn't have a type (possibly an open property), then it's possible to convert it // cause we don't know for sure. return(new ConvertNode(source, targetTypeReference)); } }
internal static bool CanConvertTo(SingleValueNode sourceNodeOrNull, IEdmTypeReference sourceReference, IEdmTypeReference targetReference) { Debug.Assert(sourceReference != null, "sourceReference != null"); Debug.Assert(targetReference != null, "targetReference != null"); //// Copy of the ResourceQueryParser.ExpressionParser.IsCompatibleWith method. if (sourceReference.IsEquivalentTo(targetReference)) { return true; } if (targetReference.IsODataComplexTypeKind() || targetReference.IsODataEntityTypeKind()) { // for structured types, use IsAssignableFrom return EdmLibraryExtensions.IsAssignableFrom( (IEdmStructuredType)targetReference.Definition, (IEdmStructuredType)sourceReference.Definition); } //// This rule stops the parser from considering nullable types as incompatible //// with non-nullable types. We have however implemented this rule because we just //// access underlying rules. C# requires an explicit .Value access, and EDM has no //// nullablity on types and (at the model level) implements null propagation. //// //// if (sourceReference.IsNullable && !targetReference.IsNullable) //// { //// return false; //// } if (sourceReference.IsEnum() && targetReference.IsEnum()) { if (sourceReference.Definition.IsEquivalentTo(targetReference.Definition)) { return targetReference.IsNullable() || (!sourceReference.IsNullable()); } return false; } IEdmPrimitiveTypeReference sourcePrimitiveTypeReference = sourceReference.AsPrimitiveOrNull(); IEdmPrimitiveTypeReference targetPrimitiveTypeReference = targetReference.AsPrimitiveOrNull(); if (sourcePrimitiveTypeReference == null || targetPrimitiveTypeReference == null) { return false; } return MetadataUtilsCommon.CanConvertPrimitiveTypeTo(sourceNodeOrNull, sourcePrimitiveTypeReference.PrimitiveDefinition(), targetPrimitiveTypeReference.PrimitiveDefinition()); }
internal static bool CanConvertTo(IEdmTypeReference sourceReference, IEdmTypeReference targetReference) { if (sourceReference.IsEquivalentTo(targetReference)) { return(true); } if (targetReference.IsODataComplexTypeKind() || targetReference.IsODataEntityTypeKind()) { return(((IEdmStructuredType)targetReference.Definition).IsAssignableFrom(((IEdmStructuredType)sourceReference.Definition))); } if (IsOpenPropertyType(targetReference)) { return(true); } IEdmPrimitiveTypeReference type = sourceReference.AsPrimitiveOrNull(); IEdmPrimitiveTypeReference reference2 = targetReference.AsPrimitiveOrNull(); return(((type != null) && (reference2 != null)) && MetadataUtilsCommon.CanConvertPrimitiveTypeTo(type.PrimitiveDefinition(), reference2.PrimitiveDefinition())); }
/// <summary> /// If the source node is not of the specified type, then we check if type promotion is possible and inject a convert node. /// If the source node is the same type as the target type (or if the target type is null), we just return the source node as is. /// </summary> /// <param name="source">The source node to apply the conversion to.</param> /// <param name="targetTypeReference">The target primitive type. May be null - this method will do nothing in that case.</param> /// <returns>The converted query node, or the original source node unchanged.</returns> internal static SingleValueNode ConvertToTypeIfNeeded(SingleValueNode source, IEdmTypeReference targetTypeReference) { Debug.Assert(source != null, "source != null"); if (targetTypeReference == null) { return(source); } if (source.TypeReference != null) { if (source.TypeReference.IsEquivalentTo(targetTypeReference)) { // For source is type definition, if source's underlying type == target type. // We create a conversion node from source to its underlying type (target type) // so that the service can convert value of source clr type to underlying clr type. if (source.TypeReference.IsTypeDefinition()) { return(new ConvertNode(source, targetTypeReference)); } return(source); } // Structured type in url will be translated into a node with raw string value. // We create a conversion node from string to structured type. if (targetTypeReference.IsStructured() || targetTypeReference.IsStructuredCollectionType()) { return(new ConvertNode(source, targetTypeReference)); } ConstantNode constantNode = source as ConstantNode; if (constantNode != null && constantNode.Value != null && source.TypeReference.IsString() && targetTypeReference.IsEnum()) { string memberName = constantNode.Value.ToString(); IEdmEnumType enumType = targetTypeReference.Definition as IEdmEnumType; if (enumType.Members.Any(m => string.Compare(m.Name, memberName, StringComparison.Ordinal) == 0)) { string literalText = ODataUriUtils.ConvertToUriLiteral(constantNode.Value, default(ODataVersion)); return(new ConstantNode(new ODataEnumValue(constantNode.Value.ToString(), targetTypeReference.Definition.ToString()), literalText, targetTypeReference)); } else { throw new ODataException(ODataErrorStrings.Binder_IsNotValidEnumConstant(memberName)); } } if (!TypePromotionUtils.CanConvertTo(source, source.TypeReference, targetTypeReference)) { throw new ODataException(ODataErrorStrings.MetadataBinder_CannotConvertToType(source.TypeReference.FullName(), targetTypeReference.FullName())); } else { if (source.TypeReference.IsEnum() && constantNode != null) { return(new ConstantNode(constantNode.Value, ODataUriUtils.ConvertToUriLiteral(constantNode.Value, ODataVersion.V4), targetTypeReference)); } object originalPrimitiveValue; if (MetadataUtilsCommon.TryGetConstantNodePrimitiveLDMF(source, out originalPrimitiveValue) && (originalPrimitiveValue != null)) { // L F D M types : directly create a ConvertNode. // 1. NodeToExpressionTranslator.cs won't allow implicitly converting single/double to decimal, which should be done here at Node tree level. // 2. And prevent losing precision in float -> double, e.g. (double)1.234f => 1.2339999675750732d not 1.234d object targetPrimitiveValue = ODataUriConversionUtils.CoerceNumericType(originalPrimitiveValue, targetTypeReference.AsPrimitive().Definition as IEdmPrimitiveType); if (string.IsNullOrEmpty(constantNode.LiteralText)) { return(new ConstantNode(targetPrimitiveValue)); } var candidate = new ConstantNode(targetPrimitiveValue, constantNode.LiteralText); var decimalType = candidate.TypeReference as IEdmDecimalTypeReference; if (decimalType != null) { var targetDecimalType = (IEdmDecimalTypeReference)targetTypeReference; return(decimalType.Precision == targetDecimalType.Precision && decimalType.Scale == targetDecimalType.Scale ? (SingleValueNode)candidate : (SingleValueNode)(new ConvertNode(candidate, targetTypeReference))); } else { return(candidate); } } else { // other type conversion : ConvertNode return(new ConvertNode(source, targetTypeReference)); } } } else { // If the source doesn't have a type (possibly an open property), then it's possible to convert it // cause we don't know for sure. return(new ConvertNode(source, targetTypeReference)); } }
/// <summary> /// If the source node is not of the specified type, then we check if type promotion is possible and inject a convert node. /// If the source node is the same type as the target type (or if the target type is null), we just return the source node as is. /// </summary> /// <param name="source">The source node to apply the convertion to.</param> /// <param name="targetTypeReference">The target primitive type. May be null - this method will do nothing in that case.</param> /// <returns>The converted query node, or the original source node unchanged.</returns> internal static SingleValueNode ConvertToTypeIfNeeded(SingleValueNode source, IEdmTypeReference targetTypeReference) { Debug.Assert(source != null, "source != null"); if (targetTypeReference == null) { return(source); } if (source.TypeReference != null) { if (source.TypeReference.IsEquivalentTo(targetTypeReference)) { // For source is type definition, if source's underlying type == target type. // We create a conversion node from source to its underlying type (target type) // so that the service can convert value of source clr type to underlying clr type. if (source.TypeReference.IsTypeDefinition()) { return(new ConvertNode(source, targetTypeReference)); } return(source); } if (!TypePromotionUtils.CanConvertTo(source, source.TypeReference, targetTypeReference)) { throw new ODataException(ODataErrorStrings.MetadataBinder_CannotConvertToType(source.TypeReference.ODataFullName(), targetTypeReference.ODataFullName())); } else { ConstantNode constantNode = source as ConstantNode; if (source.TypeReference.IsEnum() && constantNode != null) { return(new ConstantNode(constantNode.Value, ODataUriUtils.ConvertToUriLiteral(constantNode.Value, ODataVersion.V4), targetTypeReference)); } object originalPrimitiveValue; if (MetadataUtilsCommon.TryGetConstantNodePrimitiveDate(source, out originalPrimitiveValue) && (originalPrimitiveValue != null)) { // DateTimeOffset -> Date when (target is Date) and (originalValue match Date format) and (ConstantNode) object targetPrimitiveValue = ODataUriConversionUtils.CoerceTemporalType(originalPrimitiveValue, targetTypeReference.AsPrimitive().Definition as IEdmPrimitiveType); if (targetPrimitiveValue != null) { if (string.IsNullOrEmpty(constantNode.LiteralText)) { return(new ConstantNode(targetPrimitiveValue)); } return(new ConstantNode(targetPrimitiveValue, constantNode.LiteralText, targetTypeReference)); } } if (MetadataUtilsCommon.TryGetConstantNodePrimitiveLDMF(source, out originalPrimitiveValue) && (originalPrimitiveValue != null)) { // L F D M types : directly create a ConvertNode. // 1. NodeToExpressionTranslator.cs won't allow implicitly converting single/double to decimal, which should be done here at Node tree level. // 2. And prevent losing precision in float -> double, e.g. (double)1.234f => 1.2339999675750732d not 1.234d object targetPrimitiveValue = ODataUriConversionUtils.CoerceNumericType(originalPrimitiveValue, targetTypeReference.AsPrimitive().Definition as IEdmPrimitiveType); if (string.IsNullOrEmpty(constantNode.LiteralText)) { return(new ConstantNode(targetPrimitiveValue)); } return(new ConstantNode(targetPrimitiveValue, constantNode.LiteralText)); } else { // other type conversion : ConvertNode return(new ConvertNode(source, targetTypeReference)); } } } else { // If the source doesn't have a type (possibly an open property), then it's possible to convert it // cause we don't know for sure. return(new ConvertNode(source, targetTypeReference)); } }
public void ValidateSpecificSpatialTypesAreConvertable() { Assert.Equal(true, MetadataUtilsCommon.CanConvertPrimitiveTypeTo(null, EdmCoreModel.Instance.GetPrimitiveType(EdmPrimitiveTypeKind.GeographyCollection), EdmCoreModel.Instance.GetPrimitiveType(EdmPrimitiveTypeKind.Geography))); }
internal static IEdmTypeReference ResolveAndValidatePrimitiveTargetType(IEdmTypeReference expectedTypeReference, EdmTypeKind payloadTypeKind, IEdmType payloadType, string payloadTypeName, IEdmType defaultPayloadType, IEdmModel model, ODataMessageReaderSettings messageReaderSettings, ODataVersion version) { bool flag = (messageReaderSettings.ReaderBehavior.TypeResolver != null) && (payloadType != null); if ((expectedTypeReference != null) && !flag) { ValidateTypeSupported(expectedTypeReference, version); } if ((payloadTypeKind != EdmTypeKind.None) && (messageReaderSettings.DisablePrimitiveTypeConversion || !messageReaderSettings.DisableStrictMetadataValidation)) { ValidationUtils.ValidateTypeKind(payloadTypeKind, EdmTypeKind.Primitive, payloadTypeName); } if (!model.IsUserModel()) { return(GetNullablePayloadTypeReference(payloadType ?? defaultPayloadType)); } if (((expectedTypeReference == null) || flag) || messageReaderSettings.DisablePrimitiveTypeConversion) { return(GetNullablePayloadTypeReference(payloadType ?? defaultPayloadType)); } if (!messageReaderSettings.DisableStrictMetadataValidation && ((payloadType != null) && !MetadataUtilsCommon.CanConvertPrimitiveTypeTo((IEdmPrimitiveType)payloadType, (IEdmPrimitiveType)expectedTypeReference.Definition))) { throw new ODataException(Microsoft.Data.OData.Strings.ValidationUtils_IncompatibleType(payloadTypeName, expectedTypeReference.ODataFullName())); } return(expectedTypeReference); }