예제 #1
0
        /// <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()));
            }
        }
예제 #2
0
        /// <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()));
            }
        }
예제 #3
0
        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();
        }
예제 #5
0
        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();
        }