Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 34 additions & 4 deletions src/Microsoft.OData.Core/JsonLight/ODataJsonLightReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ namespace Microsoft.OData.JsonLight
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.OData.Edm;
using Microsoft.OData.Evaluation;
Expand Down Expand Up @@ -2253,17 +2254,14 @@ private void ReadNextResourceSetItem()

private bool TryReadPrimitiveAsStream(IEdmType resourceType)
{
Func<IEdmPrimitiveType, bool, string, IEdmProperty, bool> readAsStream = this.jsonLightInputContext.MessageReaderSettings.ReadAsStreamFunc;

// Should stream primitive if
// 1. Primitive is a stream value
// 2. Primitive is a string or binary value (within an untyped or streamed collection) that the reader wants to read as a stream
if (
(resourceType != null && resourceType.IsStream()) ||
(resourceType != null
&& readAsStream != null
&& (resourceType.IsBinary() || resourceType.IsString())
&& readAsStream(resourceType as IEdmPrimitiveType, false, null, null)))
&& ShouldReadPrimitiveCollectionItemAsStream(resourceType as IEdmPrimitiveType)))
{
if (resourceType == null || resourceType.IsUntyped())
{
Expand Down Expand Up @@ -3831,6 +3829,38 @@ await this.ReadNextResourceSetItemAsync()
return true;
}

/// <summary>
/// Determines whether a primitive value within a collection should be read as a stream.
/// </summary>
/// <param name="primitiveType">The primitive type of the value, or null if unknown.</param>
/// <returns>True if the primitive should be read as a stream; otherwise, false.</returns>
private bool ShouldReadPrimitiveCollectionItemAsStream(IEdmPrimitiveType primitiveType)
{
Func<ODataPropertyStreamingContext, bool> shouldReadAsStream = this.jsonLightInputContext.MessageReaderSettings.ShouldReadPropertyAsStream;
if (shouldReadAsStream != null)
{
ODataPropertyStreamingContext propertyReadingContext = new ODataPropertyStreamingContext
{
PrimitiveType = primitiveType,
IsCollection = false,
PropertyName = null, // No property name for primitives within collection
Property = null, // No EDM property for primitives within collection
CustomPropertyAnnotations = Enumerable.Empty<KeyValuePair<string, object>>() // No custom annotations for primitives within collection
};

return shouldReadAsStream(propertyReadingContext);
}

// Fallback to ReadAsStreamFunc
Func<IEdmPrimitiveType, bool, string, IEdmProperty, bool> readAsStream = this.jsonLightInputContext.MessageReaderSettings.ReadAsStreamFunc;
if (readAsStream != null)
{
return readAsStream(primitiveType, false, null, null);
}

return false;
}

#endregion private async methods

#region scopes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1321,25 +1321,21 @@ private ODataJsonLightReaderNestedInfo TryReadAsStream(IODataJsonLightReaderReso
isCollection = this.JsonReader.NodeType != JsonNodeType.PrimitiveValue;
}

Func<IEdmPrimitiveType, bool, string, IEdmProperty, bool> readAsStream = this.MessageReaderSettings.ReadAsStreamFunc;

// is the property a stream or a stream collection,
// untyped collection,
// or a binary or binary collection the client wants to read as a stream...
if (
(primitiveType != null &&
(primitiveType.IsStream() ||
(readAsStream != null
&& (property == null || !property.IsKey()) // don't stream key properties
((property == null || !property.IsKey()) // don't stream key properties
&& (primitiveType.IsBinary() || primitiveType.IsString() || isCollection))
&& readAsStream(primitiveType, isCollection, propertyName, property))) ||
&& ShouldReadPrimitiveValueAsStream(resourceState, primitiveType, isCollection, propertyName, property))) ||
(propertyType != null &&
isCollection &&
propertyType.Definition.AsElementType().IsUntyped()) ||
(propertyType == null
&& (isCollection || this.JsonReader.CanStream())
&& readAsStream != null
&& readAsStream(null, isCollection, propertyName, property)))
&& ShouldReadPrimitiveValueAsStream(resourceState, null, isCollection, propertyName, property)))
{
if (isCollection)
{
Expand Down Expand Up @@ -3589,25 +3585,21 @@ private async Task<ODataJsonLightReaderNestedInfo> TryReadAsStreamAsync(
isCollection = this.JsonReader.NodeType != JsonNodeType.PrimitiveValue;
}

Func<IEdmPrimitiveType, bool, string, IEdmProperty, bool> readAsStream = this.MessageReaderSettings.ReadAsStreamFunc;

// Is the property a stream or a stream collection,
// untyped collection,
// or a binary or binary collection the client wants to read as a stream...
if (
(primitiveType != null &&
(primitiveType.IsStream() ||
(readAsStream != null
&& (property == null || !property.IsKey()) // don't stream key properties
((property == null || !property.IsKey()) // don't stream key properties
&& (primitiveType.IsBinary() || primitiveType.IsString() || isCollection))
&& readAsStream(primitiveType, isCollection, propertyName, property))) ||
&& ShouldReadPrimitiveValueAsStream(resourceState, primitiveType, isCollection, propertyName, property))) ||
(propertyType != null &&
isCollection &&
propertyType.Definition.AsElementType().IsUntyped()) ||
(propertyType == null
&& (isCollection || await this.JsonReader.CanStreamAsync().ConfigureAwait(false))
&& readAsStream != null
&& readAsStream(null, isCollection, propertyName, property)))
&& ShouldReadPrimitiveValueAsStream(resourceState, null, isCollection, propertyName, property)))
{
if (isCollection)
{
Expand Down Expand Up @@ -4328,5 +4320,44 @@ await this.JsonReader.ReadEndArrayAsync()
this.JsonReader.AssertNotBuffering();
this.AssertJsonCondition(JsonNodeType.Property, JsonNodeType.EndObject);
}

/// <summary>
/// Determines whether a primitive value should be read as a stream.
/// </summary>
/// <param name="resourceState">The current resource state containing property annotations.</param>
/// <param name="primitiveType">The primitive type of the property, or null if unknown.</param>
/// <param name="isCollection">Whether the property is a collection.</param>
/// <param name="propertyName">The name of the property.</param>
/// <param name="property">The EDM property, or null for dynamic properties.</param>
/// <returns>True if the property should be read as a stream; otherwise, false.</returns>
private bool ShouldReadPrimitiveValueAsStream(IODataJsonLightReaderResourceState resourceState, IEdmPrimitiveType primitiveType, bool isCollection, string propertyName, IEdmProperty property)
{
Func<ODataPropertyStreamingContext, bool> shouldReadAsStream = this.MessageReaderSettings.ShouldReadPropertyAsStream;
if (shouldReadAsStream != null)
{
IEnumerable<KeyValuePair<string, object>> customPropertyAnnotations = resourceState?.PropertyAndAnnotationCollector?.GetCustomPropertyAnnotations(propertyName)
?? Enumerable.Empty<KeyValuePair<string, object>>();

ODataPropertyStreamingContext propertyReadingContext = new ODataPropertyStreamingContext
{
PrimitiveType = primitiveType,
IsCollection = isCollection,
PropertyName = propertyName,
Property = property,
CustomPropertyAnnotations = customPropertyAnnotations
};

return shouldReadAsStream(propertyReadingContext);
}

// Fallback to ReadAsStreamFunc
Func<IEdmPrimitiveType, bool, string, IEdmProperty, bool> readAsStream = this.MessageReaderSettings.ReadAsStreamFunc;
if (readAsStream != null)
{
return readAsStream(primitiveType, isCollection, propertyName, property);
}

return false;
}
}
}
16 changes: 16 additions & 0 deletions src/Microsoft.OData.Core/ODataMessageReaderSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -207,8 +207,23 @@ public ODataMessageQuotas MessageQuotas
/// Function returns:
/// * True, to have the value streamed, otherwise false
/// </Remarks>
[Obsolete("Use 'ShouldReadPropertyAsStream' instead, which provides access to property annotations. ReadAsStreamFunc will be removed in a future ODL 9 release.")]
public Func<IEdmPrimitiveType, bool, string, IEdmProperty, bool> ReadAsStreamFunc { get; set; }

/// <summary>
/// Func to evaluate whether a property should be read as a stream. This function provides access to property annotations, enabling
/// scenarios where the decision to read as stream depends on annotations. Note that IEdmProperty may be null when reading within a collection.
/// </summary>
/// <remarks>
/// <see cref="ODataPropertyStreamingContext"/> provides:
/// * Primitive type of the value being read, or null if unknown
/// * Whether the value being read is a collection
/// * The name of the property being read (null for values within a collection)
/// * The property being read (null for dynamic property or value within a collection)
/// * Custom property annotations (non-OData annotations) that have been read so far.
/// </remarks>
public Func<ODataPropertyStreamingContext, bool> ShouldReadPropertyAsStream { get; set; }

/// <summary>
/// Func to evaluate whether an annotation should be read or skipped by the reader. The func should return true if the annotation should
/// be read and false if the annotation should be skipped. A null value indicates that all annotations should be skipped.
Expand Down Expand Up @@ -299,6 +314,7 @@ private void CopyFrom(ODataMessageReaderSettings other)
this.LibraryCompatibility = other.LibraryCompatibility;
this.Version = other.Version;
this.ReadAsStreamFunc = other.ReadAsStreamFunc;
this.ShouldReadPropertyAsStream = other.ShouldReadPropertyAsStream;
this.ArrayPool = other.ArrayPool;
this.EnablePropertyNameCaseInsensitive = other.EnablePropertyNameCaseInsensitive;
this.EnableUntypedCollections = other.EnableUntypedCollections;
Expand Down
44 changes: 44 additions & 0 deletions src/Microsoft.OData.Core/ODataPropertyStreamingContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
//---------------------------------------------------------------------
// <copyright file="ODataPropertyStreamingContext.cs" company="Microsoft">
// Copyright (C) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
// </copyright>
//---------------------------------------------------------------------

using System.Collections.Generic;
using Microsoft.OData.Edm;

namespace Microsoft.OData
{
/// <summary>
/// Context information for reading an OData property
/// </summary>
public sealed class ODataPropertyStreamingContext
{
/// <summary>
/// The primitive type of the property being read, or null if unknown.
/// </summary>
public IEdmPrimitiveType PrimitiveType { get; internal set; }

/// <summary>
/// Indicates whether the value being read is a collection.
/// </summary>
public bool IsCollection { get; internal set; }

/// <summary>
/// The name of the property being read (null for values within a collection)
/// </summary>
public string PropertyName { get; internal set; }

/// <summary>
/// The EDM property being read (null for dynamic property or value within a collection)
/// </summary>
public IEdmProperty Property { get; internal set; }

/// <summary>
/// The custom annotations associated with this property.
/// These are annotations that do not correspond to reserved OData annotations,
/// regardless of whether the optional "odata." prefix is present.
/// </summary>
public IEnumerable<KeyValuePair<string, object>> CustomPropertyAnnotations { get; internal set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
Microsoft.OData.ODataMessageReaderSettings.ShouldReadPropertyAsStream.get -> System.Func<Microsoft.OData.ODataPropertyStreamingContext, bool>
Microsoft.OData.ODataMessageReaderSettings.ShouldReadPropertyAsStream.set -> void
Microsoft.OData.ODataPropertyStreamingContext
Microsoft.OData.ODataPropertyStreamingContext.CustomPropertyAnnotations.get -> System.Collections.Generic.IEnumerable<System.Collections.Generic.KeyValuePair<string, object>>
Microsoft.OData.ODataPropertyStreamingContext.IsCollection.get -> bool
Microsoft.OData.ODataPropertyStreamingContext.ODataPropertyStreamingContext() -> void
Microsoft.OData.ODataPropertyStreamingContext.PrimitiveType.get -> Microsoft.OData.Edm.IEdmPrimitiveType
Microsoft.OData.ODataPropertyStreamingContext.Property.get -> Microsoft.OData.Edm.IEdmProperty
Microsoft.OData.ODataPropertyStreamingContext.PropertyName.get -> string
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
Microsoft.OData.ODataMessageReaderSettings.ShouldReadPropertyAsStream.get -> System.Func<Microsoft.OData.ODataPropertyStreamingContext, bool>
Microsoft.OData.ODataMessageReaderSettings.ShouldReadPropertyAsStream.set -> void
Microsoft.OData.ODataPropertyStreamingContext
Microsoft.OData.ODataPropertyStreamingContext.CustomPropertyAnnotations.get -> System.Collections.Generic.IEnumerable<System.Collections.Generic.KeyValuePair<string, object>>
Microsoft.OData.ODataPropertyStreamingContext.IsCollection.get -> bool
Microsoft.OData.ODataPropertyStreamingContext.ODataPropertyStreamingContext() -> void
Microsoft.OData.ODataPropertyStreamingContext.PrimitiveType.get -> Microsoft.OData.Edm.IEdmPrimitiveType
Microsoft.OData.ODataPropertyStreamingContext.Property.get -> Microsoft.OData.Edm.IEdmProperty
Microsoft.OData.ODataPropertyStreamingContext.PropertyName.get -> string
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
Microsoft.OData.ODataMessageReaderSettings.ShouldReadPropertyAsStream.get -> System.Func<Microsoft.OData.ODataPropertyStreamingContext, bool>
Microsoft.OData.ODataMessageReaderSettings.ShouldReadPropertyAsStream.set -> void
Microsoft.OData.ODataPropertyStreamingContext
Microsoft.OData.ODataPropertyStreamingContext.CustomPropertyAnnotations.get -> System.Collections.Generic.IEnumerable<System.Collections.Generic.KeyValuePair<string, object>>
Microsoft.OData.ODataPropertyStreamingContext.IsCollection.get -> bool
Microsoft.OData.ODataPropertyStreamingContext.ODataPropertyStreamingContext() -> void
Microsoft.OData.ODataPropertyStreamingContext.PrimitiveType.get -> Microsoft.OData.Edm.IEdmPrimitiveType
Microsoft.OData.ODataPropertyStreamingContext.Property.get -> Microsoft.OData.Edm.IEdmProperty
Microsoft.OData.ODataPropertyStreamingContext.PropertyName.get -> string
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
Microsoft.OData.ODataMessageReaderSettings.ShouldReadPropertyAsStream.get -> System.Func<Microsoft.OData.ODataPropertyStreamingContext, bool>
Microsoft.OData.ODataMessageReaderSettings.ShouldReadPropertyAsStream.set -> void
Microsoft.OData.ODataPropertyStreamingContext
Microsoft.OData.ODataPropertyStreamingContext.CustomPropertyAnnotations.get -> System.Collections.Generic.IEnumerable<System.Collections.Generic.KeyValuePair<string, object>>
Microsoft.OData.ODataPropertyStreamingContext.IsCollection.get -> bool
Microsoft.OData.ODataPropertyStreamingContext.ODataPropertyStreamingContext() -> void
Microsoft.OData.ODataPropertyStreamingContext.PrimitiveType.get -> Microsoft.OData.Edm.IEdmPrimitiveType
Microsoft.OData.ODataPropertyStreamingContext.Property.get -> Microsoft.OData.Edm.IEdmProperty
Microsoft.OData.ODataPropertyStreamingContext.PropertyName.get -> string
Loading