예제 #1
0
        /// <summary>
        /// Creates an <see cref="ODataProperty"/> with name <paramref name="elementName"/> and value
        /// based on the object represented by <paramref name="graph"/>.
        /// </summary>
        /// <param name="serializer">The <see cref="IODataEdmTypeSerializer"/> writing the property value.</param>
        /// <param name="graph">The object to base the value of the property on.</param>
        /// <param name="expectedType">The expected EDM type of the object represented by <paramref name="graph"/>.</param>
        /// <param name="elementName">The name of the property.</param>
        /// <param name="writeContext">The <see cref="ODataSerializerContext"/>.</param>
        /// <returns>The <see cref="ODataProperty"/> created.</returns>
        public static ODataProperty CreateProperty(this IODataEdmTypeSerializer serializer, object graph, IEdmTypeReference expectedType, string elementName,
                                                   ODataSerializerContext writeContext)
        {
            if (serializer is ODataCollectionSerializer collectionSerializer)
            {
                return(CreateCollectionProperty(collectionSerializer, graph, expectedType, elementName, writeContext));
            }

            Contract.Assert(elementName != null);
            return(new ODataProperty
            {
                Name = elementName,
                Value = serializer.CreateODataValue(graph, expectedType, writeContext)
            });
        }
        private async Task WriteResourceSetAsync(IEnumerable enumerable, IEdmTypeReference resourceSetType, ODataWriter writer,
                                                 ODataSerializerContext writeContext)
        {
            Contract.Assert(writer != null);
            Contract.Assert(writeContext != null);
            Contract.Assert(enumerable != null);
            Contract.Assert(resourceSetType != null);

            IEdmStructuredTypeReference elementType = GetResourceType(resourceSetType);
            ODataResourceSet            resourceSet = CreateResourceSet(enumerable, resourceSetType.AsCollection(), writeContext);

            Func <object, Uri> nextLinkGenerator = GetNextLinkGenerator(resourceSet, enumerable, writeContext);

            if (resourceSet == null)
            {
                throw new SerializationException(Error.Format(SRResources.CannotSerializerNull, ResourceSet));
            }

            IEdmEntitySetBase entitySet = writeContext.NavigationSource as IEdmEntitySetBase;

            if (entitySet == null)
            {
                resourceSet.SetSerializationInfo(new ODataResourceSerializationInfo
                {
                    IsFromCollection = true,
                    NavigationSourceEntityTypeName = elementType.FullName(),
                    NavigationSourceKind           = EdmNavigationSourceKind.UnknownEntitySet,
                    NavigationSourceName           = null
                });
            }

            IODataEdmTypeSerializer resourceSerializer = SerializerProvider.GetEdmTypeSerializer(elementType);

            if (resourceSerializer == null)
            {
                throw new SerializationException(
                          Error.Format(SRResources.TypeCannotBeSerialized, elementType.FullName()));
            }

            // set the nextpagelink to null to support JSON odata.streaming.
            resourceSet.NextPageLink = null;
            await writer.WriteStartAsync(resourceSet).ConfigureAwait(false);

            object lastResource = null;

            foreach (object item in enumerable)
            {
                lastResource = item;
                if (item == null || item is NullEdmComplexObject)
                {
                    if (elementType.IsEntity())
                    {
                        throw new SerializationException(SRResources.NullElementInCollection);
                    }

                    // for null complex element, it can be serialized as "null" in the collection.
                    await writer.WriteStartAsync(resource : null).ConfigureAwait(false);

                    await writer.WriteEndAsync().ConfigureAwait(false);
                }
                else
                {
                    await resourceSerializer.WriteObjectInlineAsync(item, elementType, writer, writeContext).ConfigureAwait(false);
                }
            }

            // Subtle and surprising behavior: If the NextPageLink property is set before calling WriteStart(resourceSet),
            // the next page link will be written early in a manner not compatible with odata.streaming=true. Instead, if
            // the next page link is not set when calling WriteStart(resourceSet) but is instead set later on that resourceSet
            // object before calling WriteEnd(), the next page link will be written at the end, as required for
            // odata.streaming=true support.

            resourceSet.NextPageLink = nextLinkGenerator(lastResource);

            await writer.WriteEndAsync().ConfigureAwait(false);
        }
예제 #3
0
        /// <summary>
        /// Creates an <see cref="ODataCollectionValue"/> for the enumerable represented by <paramref name="enumerable"/>.
        /// </summary>
        /// <param name="enumerable">The value of the collection to be created.</param>
        /// <param name="elementType">The element EDM type of the collection.</param>
        /// <param name="writeContext">The serializer context to be used while creating the collection.</param>
        /// <returns>The created <see cref="ODataCollectionValue"/>.</returns>
        public virtual ODataCollectionValue CreateODataCollectionValue(IEnumerable enumerable, IEdmTypeReference elementType,
                                                                       ODataSerializerContext writeContext)
        {
            if (elementType == null)
            {
                throw Error.ArgumentNull(nameof(elementType));
            }

            if (writeContext == null)
            {
                throw Error.ArgumentNull(nameof(writeContext));
            }

            ArrayList valueCollection = new ArrayList();

            if (enumerable != null)
            {
                IODataEdmTypeSerializer itemSerializer = null;
                foreach (object item in enumerable)
                {
                    if (item == null)
                    {
                        if (elementType.IsNullable)
                        {
                            valueCollection.Add(value: null);
                            continue;
                        }

                        throw new SerializationException(SRResources.NullElementInCollection);
                    }

                    IEdmTypeReference actualType = writeContext.GetEdmType(item, item.GetType());
                    Contract.Assert(actualType != null);

                    itemSerializer = itemSerializer ?? SerializerProvider.GetEdmTypeSerializer(actualType);
                    if (itemSerializer == null)
                    {
                        throw new SerializationException(
                                  Error.Format(SRResources.TypeCannotBeSerialized, actualType.FullName()));
                    }

                    // ODataCollectionWriter expects the individual elements in the collection to be the underlying
                    // values and not ODataValues.
                    valueCollection.Add(
                        itemSerializer.CreateODataValue(item, actualType, writeContext).GetInnerValue());
                }
            }

            // Ideally, we'd like to do this:
            // string typeName = _edmCollectionType.FullName();
            // But ODataLib currently doesn't support .FullName() for collections. As a workaround, we construct the
            // collection type name the hard way.
            string typeName = "Collection(" + elementType.FullName() + ")";

            // ODataCollectionValue is only a V3 property, arrays inside Complex Types or Entity types are only supported in V3
            // if a V1 or V2 Client requests a type that has a collection within it ODataLib will throw.
            ODataCollectionValue value = new ODataCollectionValue
            {
                Items    = valueCollection.Cast <object>(),
                TypeName = typeName
            };

            AddTypeNameAnnotationAsNeeded(value, writeContext.MetadataLevel);
            return(value);
        }