/// <summary> /// Validates that the <paramref name="payloadComplexType"/> is assignable to the <paramref name="expectedComplexType"/> /// and fails if it's not. /// </summary> /// <param name="expectedComplexType">The expected complex type reference, the base type of the ComplexType expected.</param> /// <param name="payloadComplexType">The payload complex type reference to validate.</param> internal static void ValidateComplexTypeIsAssignable(IEdmComplexType expectedComplexType, IEdmComplexType payloadComplexType) { Debug.Assert(expectedComplexType != null, "expectedComplexType != null"); Debug.Assert(payloadComplexType != null, "payloadComplexType != null"); // Complex types could be assignable if (!EdmLibraryExtensions.IsAssignableFrom(expectedComplexType, payloadComplexType)) { throw new ODataException(Strings.ValidationUtils_IncompatibleType(payloadComplexType.FullTypeName(), expectedComplexType.FullTypeName())); } }
/// <summary> /// Validates that the <paramref name="payloadEntityTypeReference"/> is assignable to the <paramref name="expectedEntityTypeReference"/> /// and fails if it's not. /// </summary> /// <param name="expectedEntityTypeReference">The expected entity type reference, the base type of the entities expected.</param> /// <param name="payloadEntityTypeReference">The payload entity type reference to validate.</param> internal static void ValidateEntityTypeIsAssignable(IEdmEntityTypeReference expectedEntityTypeReference, IEdmEntityTypeReference payloadEntityTypeReference) { Debug.Assert(expectedEntityTypeReference != null, "expectedEntityTypeReference != null"); Debug.Assert(payloadEntityTypeReference != null, "payloadEntityTypeReference != null"); // Entity types must be assignable if (!EdmLibraryExtensions.IsAssignableFrom(expectedEntityTypeReference.EntityDefinition(), payloadEntityTypeReference.EntityDefinition())) { throw new ODataException(Strings.ValidationUtils_ResourceTypeNotAssignableToExpectedType(payloadEntityTypeReference.FullName(), expectedEntityTypeReference.FullName())); } }
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()); }
/// <summary> /// Asynchronously writes out the value of a resource (complex or entity). /// </summary> /// <param name="resourceValue">The resource (complex or entity) value to write.</param> /// <param name="metadataTypeReference">The metadata type for the resource value.</param> /// <param name="isOpenPropertyType">true if the type name belongs to an open (dynamic) property.</param> /// <param name="duplicatePropertyNamesChecker">The checker instance for duplicate property names.</param> /// <remarks>The current recursion depth should be a value, measured by the number of resource and collection values between /// this resource value and the top-level payload, not including this one.</remarks> /// <returns>A task that represents the asynchronous write operation.</returns> public virtual async Task WriteResourceValueAsync( ODataResourceValue resourceValue, IEdmTypeReference metadataTypeReference, bool isOpenPropertyType, IDuplicatePropertyNameChecker duplicatePropertyNamesChecker) { Debug.Assert(resourceValue != null, "resourceValue != null"); this.IncreaseRecursionDepth(); // Start the object scope which will represent the entire resource instance; await this.AsynchronousJsonWriter.StartObjectScopeAsync().ConfigureAwait(false); string typeName = resourceValue.TypeName; // In requests, we allow the property type reference to be null if the type name is specified in the OM if (metadataTypeReference == null && !this.WritingResponse && typeName == null && this.Model.IsUserModel()) { throw new ODataException(ODataErrorStrings.ODataJsonLightPropertyAndValueSerializer_NoExpectedTypeOrTypeNameSpecifiedForResourceValueRequest); } // Resolve the type name to the type; if no type name is specified we will use the type inferred from metadata. IEdmStructuredTypeReference resourceValueTypeReference = (IEdmStructuredTypeReference)TypeNameOracle.ResolveAndValidateTypeForResourceValue(this.Model, metadataTypeReference, resourceValue, isOpenPropertyType, this.WriterValidator); Debug.Assert( metadataTypeReference == null || resourceValueTypeReference == null || EdmLibraryExtensions.IsAssignableFrom(metadataTypeReference, resourceValueTypeReference), "Complex property types must be the same as or inherit from the ones from metadata (unless open)."); typeName = this.JsonLightOutputContext.TypeNameOracle.GetValueTypeNameForWriting(resourceValue, metadataTypeReference, resourceValueTypeReference, isOpenPropertyType); if (typeName != null) { await this.AsynchronousODataAnnotationWriter.WriteODataTypeInstanceAnnotationAsync(typeName) .ConfigureAwait(false); } // Write custom instance annotations await this.InstanceAnnotationWriter.WriteInstanceAnnotationsAsync(resourceValue.InstanceAnnotations) .ConfigureAwait(false); // Write the properties of the resource value as usual. Note we do not allow resource types to contain named stream properties. await this.PropertySerializer.WritePropertiesAsync( resourceValueTypeReference == null?null : resourceValueTypeReference.StructuredDefinition(), resourceValue.Properties, true /* isComplexValue */, duplicatePropertyNamesChecker, null).ConfigureAwait(false); // End the object scope which represents the resource instance; await this.AsynchronousJsonWriter.EndObjectScopeAsync().ConfigureAwait(false); this.DecreaseRecursionDepth(); }
public void WriteComplexValue( ODataComplexValue complexValue, IEdmTypeReference metadataTypeReference, bool isTopLevel, bool isOpenPropertyType, DuplicatePropertyNamesChecker duplicatePropertyNamesChecker) { Debug.Assert(complexValue != null, "complexValue != null"); this.IncreaseRecursionDepth(); // Start the object scope which will represent the entire complex instance; // for top-level complex properties we already wrote the object scope (and the context URI when needed). if (!isTopLevel) { this.JsonWriter.StartObjectScope(); } string typeName = complexValue.TypeName; if (isTopLevel) { Debug.Assert(metadataTypeReference == null, "Never expect a metadata type for top-level properties."); if (typeName == null) { throw new ODataException(ODataErrorStrings.ODataJsonLightValueSerializer_MissingTypeNameOnComplex); } } else { // In requests, we allow the property type reference to be null if the type name is specified in the OM if (metadataTypeReference == null && !this.WritingResponse && typeName == null && this.Model.IsUserModel()) { throw new ODataException(ODataErrorStrings.ODataJsonLightPropertyAndValueSerializer_NoExpectedTypeOrTypeNameSpecifiedForComplexValueRequest); } } // Resolve the type name to the type; if no type name is specified we will use the // type inferred from metadata. IEdmComplexTypeReference complexValueTypeReference = (IEdmComplexTypeReference)TypeNameOracle.ResolveAndValidateTypeNameForValue(this.Model, metadataTypeReference, complexValue, isOpenPropertyType); Debug.Assert( metadataTypeReference == null || complexValueTypeReference == null || EdmLibraryExtensions.IsAssignableFrom(metadataTypeReference, complexValueTypeReference), "Complex property types must be the same as or inherit from the ones from metadata (unless open)."); typeName = this.JsonLightOutputContext.TypeNameOracle.GetValueTypeNameForWriting(complexValue, metadataTypeReference, complexValueTypeReference, isOpenPropertyType); if (typeName != null) { ODataJsonLightWriterUtils.WriteODataTypeInstanceAnnotation(this.JsonWriter, typeName); } // Write the properties of the complex value as usual. Note we do not allow complex types to contain named stream properties. this.PropertySerializer.WriteProperties( complexValueTypeReference == null ? null : complexValueTypeReference.ComplexDefinition(), complexValue.Properties, true /* isComplexValue */, duplicatePropertyNamesChecker, null /*projectedProperties */); // End the object scope which represents the complex instance; // for top-level complex properties we already wrote the end object scope. if (!isTopLevel) { this.JsonWriter.EndObjectScope(); } this.DecreaseRecursionDepth(); }