/// <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); }
/// <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); }