From 5d32ad51a8325e4b0ad6b287c698a8a4ff483b99 Mon Sep 17 00:00:00 2001 From: Henning Krause Date: Sun, 10 Oct 2021 20:54:45 +0200 Subject: [PATCH 1/9] Performance improvements * Replaced a few "async Task" methods with simple "Task" methods to remove the async overhead * Made a few allocations lazy to avoid creating variable members when they aren't needed. * Removed the necessity to use a class based enumerator in two instances --- .../InstanceAnnotationWriteTracker.cs | 14 ++--- .../Json/JsonValueUtilsAsync.cs | 52 +++++++++---------- .../Json/JsonWriterAsync.cs | 11 ++-- .../ODataJsonLightPropertySerializer.cs | 30 +++++------ src/Microsoft.OData.Core/ODataAnnotatable.cs | 23 ++++---- src/Microsoft.OData.Core/ODataWriterCore.cs | 8 +-- 6 files changed, 65 insertions(+), 73 deletions(-) diff --git a/src/Microsoft.OData.Core/InstanceAnnotationWriteTracker.cs b/src/Microsoft.OData.Core/InstanceAnnotationWriteTracker.cs index c5ba00efcf..b25ac39a3c 100644 --- a/src/Microsoft.OData.Core/InstanceAnnotationWriteTracker.cs +++ b/src/Microsoft.OData.Core/InstanceAnnotationWriteTracker.cs @@ -18,15 +18,7 @@ internal sealed class InstanceAnnotationWriteTracker /// Maintains the write status for each annotation using its key. /// If a key exists in the list then it is considered written. /// - private readonly HashSet writeStatus; - - /// - /// Creates a new to hold write status for instance annotations. - /// - public InstanceAnnotationWriteTracker() - { - this.writeStatus = new HashSet(StringComparer.Ordinal); - } + private readonly Lazy> writeStatus = new Lazy>(() => new HashSet(StringComparer.Ordinal)); /// /// Check if an annotation is already written. @@ -35,7 +27,7 @@ public InstanceAnnotationWriteTracker() /// The key of the element to check if its written. public bool IsAnnotationWritten(string key) { - return this.writeStatus.Contains(key); + return this.writeStatus.Value.Contains(key); } /// @@ -45,7 +37,7 @@ public bool IsAnnotationWritten(string key) /// The key of the element to mark as written. public bool MarkAnnotationWritten(string key) { - return this.writeStatus.Add(key); + return this.writeStatus.Value.Add(key); } } } diff --git a/src/Microsoft.OData.Core/Json/JsonValueUtilsAsync.cs b/src/Microsoft.OData.Core/Json/JsonValueUtilsAsync.cs index 756196e401..a7c78a6467 100644 --- a/src/Microsoft.OData.Core/Json/JsonValueUtilsAsync.cs +++ b/src/Microsoft.OData.Core/Json/JsonValueUtilsAsync.cs @@ -30,7 +30,7 @@ internal static partial class JsonValueUtils /// The text writer to write the output to. /// The char value to write. /// The ODataStringEscapeOption to use in escaping the string. - internal static async Task WriteValueAsync(this TextWriter writer, char value, ODataStringEscapeOption stringEscapeOption) + internal static Task WriteValueAsync(this TextWriter writer, char value, ODataStringEscapeOption stringEscapeOption) { Debug.Assert(writer != null, "writer != null"); @@ -39,12 +39,11 @@ internal static async Task WriteValueAsync(this TextWriter writer, char value, O string escapedString = SpecialCharToEscapedStringMap[value]; if (escapedString != null) { - await writer.WriteAsync(escapedString).ConfigureAwait(false); - return; + return writer.WriteAsync(escapedString); } } - await writer.WriteAsync(value).ConfigureAwait(false); + return writer.WriteAsync(value); } /// @@ -122,27 +121,26 @@ internal static Task WriteValueAsync(this TextWriter writer, long value) /// /// The text writer to write the output to. /// Double value to be written. - internal static async Task WriteValueAsync(this TextWriter writer, double value) + internal static Task WriteValueAsync(this TextWriter writer, double value) { Debug.Assert(writer != null, "writer != null"); if (JsonSharedUtils.IsDoubleValueSerializedAsString(value)) { - await writer.WriteQuotedAsync(value.ToString(ODataNumberFormatInfo)).ConfigureAwait(false); + return writer.WriteQuotedAsync(value.ToString(ODataNumberFormatInfo)); } - else - { - // double.ToString() supports a max scale of 14, - // whereas double.MinValue and double.MaxValue have 16 digits scale. Hence we need - // to use XmlConvert in all other cases, except infinity - string valueToWrite = XmlConvert.ToString(value); - await writer.WriteAsync(valueToWrite).ConfigureAwait(false); - if (valueToWrite.IndexOfAny(DoubleIndicatingCharacters) < 0) - { - await writer.WriteAsync(".0").ConfigureAwait(false); - } + // double.ToString() supports a max scale of 14, + // whereas double.MinValue and double.MaxValue have 16 digits scale. Hence we need + // to use XmlConvert in all other cases, except infinity + var valueToWrite = XmlConvert.ToString(value); + + if (valueToWrite.IndexOfAny(DoubleIndicatingCharacters) < 0) + { + valueToWrite += ".0"; } + + return writer.WriteAsync(valueToWrite); } /// @@ -176,7 +174,7 @@ internal static Task WriteValueAsync(this TextWriter writer, decimal value) /// DateTimeOffset value to be written. /// The format to write out the DateTime value in. [SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", MessageId = "Microsoft.OData.Json.JsonValueUtils.WriteQuotedAsync(System.IO.TextWriter,System.String)", Justification = "Constant defined by the JSON spec.")] - internal static async Task WriteValueAsync(this TextWriter writer, DateTimeOffset value, ODataJsonDateTimeFormat dateTimeFormat) + internal static Task WriteValueAsync(this TextWriter writer, DateTimeOffset value, ODataJsonDateTimeFormat dateTimeFormat) { Debug.Assert(writer != null, "writer != null"); @@ -192,11 +190,9 @@ internal static async Task WriteValueAsync(this TextWriter writer, DateTimeOffse // // offset = 4DIGIT string textValue = XmlConvert.ToString(value); - await writer.WriteQuotedAsync(textValue).ConfigureAwait(false); + return writer.WriteQuotedAsync(textValue); } - break; - case ODataJsonDateTimeFormat.ODataDateTime: { // Uses the same format as DateTime but with offset: @@ -210,11 +206,11 @@ internal static async Task WriteValueAsync(this TextWriter writer, DateTimeOffse // ticks = *DIGIT // offset = 4DIGIT string textValue = FormatDateTimeAsJsonTicksString(value); - await writer.WriteQuotedAsync(textValue).ConfigureAwait(false); + return writer.WriteQuotedAsync(textValue); } - - break; } + + return TaskUtils.CompletedTask; } /// @@ -321,7 +317,7 @@ internal static async Task WriteValueAsync(this TextWriter writer, byte[] value, } else { - await writer.WriteAsync(JsonConstants.QuoteCharacter).ConfigureAwait(false); + await writer.WriteAsync(JsonConstants.QuoteCharacter).ConfigureAwait(false); await writer.WriteBinaryStringAsync(value, buffer, arrayPool).ConfigureAwait(false); await writer.WriteAsync(JsonConstants.QuoteCharacter).ConfigureAwait(false); } @@ -451,7 +447,7 @@ internal static async Task WriteEscapedJsonStringValueAsync( /// The string escape option. /// Char buffer to use for streaming data. /// Character buffer pool. - internal static async Task WriteEscapedCharArrayAsync( + internal static Task WriteEscapedCharArrayAsync( this TextWriter writer, char[] inputArray, int inputArrayOffset, @@ -468,8 +464,10 @@ internal static async Task WriteEscapedCharArrayAsync( // write remaining bytes in buffer if (bufferIndex > 0) { - await writer.WriteAsync(buffer.Value, 0, bufferIndex).ConfigureAwait(false); + return writer.WriteAsync(buffer.Value, 0, bufferIndex); } + + return TaskUtils.CompletedTask; } /// diff --git a/src/Microsoft.OData.Core/Json/JsonWriterAsync.cs b/src/Microsoft.OData.Core/Json/JsonWriterAsync.cs index 6df34d76bc..fa00662028 100644 --- a/src/Microsoft.OData.Core/Json/JsonWriterAsync.cs +++ b/src/Microsoft.OData.Core/Json/JsonWriterAsync.cs @@ -317,24 +317,25 @@ public Task EndTextWriterValueScopeAsync() /// Asynchronously writes a separator of a value if it's needed for the next value to be written. /// /// A task that represents the asynchronous write operation. - private async Task WriteValueSeparatorAsync() + private Task WriteValueSeparatorAsync() { if (this.scopes.Count == 0) { - return; + return TaskUtils.CompletedTask; } Scope currentScope = this.scopes.Peek(); if (currentScope.Type == ScopeType.Array) { + currentScope.ObjectCount++; if (currentScope.ObjectCount != 0) { - await this.writer.WriteAsync(JsonConstants.ArrayElementSeparator) - .ConfigureAwait(false); + return this.writer.WriteAsync(JsonConstants.ArrayElementSeparator); } - currentScope.ObjectCount++; + } + return TaskUtils.CompletedTask; } /// diff --git a/src/Microsoft.OData.Core/JsonLight/ODataJsonLightPropertySerializer.cs b/src/Microsoft.OData.Core/JsonLight/ODataJsonLightPropertySerializer.cs index 66e6fc96bb..b66a74f81d 100644 --- a/src/Microsoft.OData.Core/JsonLight/ODataJsonLightPropertySerializer.cs +++ b/src/Microsoft.OData.Core/JsonLight/ODataJsonLightPropertySerializer.cs @@ -916,21 +916,20 @@ await this.WriteStreamInfoAsync(propertyName, streamInfo) /// If writing top level property. /// If writing an undeclared property. /// A task that represents the asynchronous write operation. - private async Task WriteInstanceAnnotationAsync(ODataPropertyInfo property, bool isTopLevel, bool isUndeclaredProperty) + private Task WriteInstanceAnnotationAsync(ODataPropertyInfo property, bool isTopLevel, bool isUndeclaredProperty) { if (property.InstanceAnnotations.Count != 0) { if (isTopLevel) { - await this.InstanceAnnotationWriter.WriteInstanceAnnotationsAsync(property.InstanceAnnotations) - .ConfigureAwait(false); + return this.InstanceAnnotationWriter.WriteInstanceAnnotationsAsync(property.InstanceAnnotations); } else { - await this.InstanceAnnotationWriter.WriteInstanceAnnotationsAsync(property.InstanceAnnotations, property.Name, isUndeclaredProperty) - .ConfigureAwait(false); + return this.InstanceAnnotationWriter.WriteInstanceAnnotationsAsync(property.InstanceAnnotations, property.Name, isUndeclaredProperty); } } + return TaskUtils.CompletedTask; } /// @@ -939,7 +938,7 @@ await this.InstanceAnnotationWriter.WriteInstanceAnnotationsAsync(property.Insta /// The property to handle. /// If writing top level property. /// A task that represents the asynchronous write operation. - private async Task WriteODataTypeAnnotationAsync(ODataPropertyInfo property, bool isTopLevel) + private Task WriteODataTypeAnnotationAsync(ODataPropertyInfo property, bool isTopLevel) { if (property.TypeAnnotation != null && property.TypeAnnotation.TypeName != null) { @@ -952,16 +951,17 @@ private async Task WriteODataTypeAnnotationAsync(ODataPropertyInfo property, boo { if (isTopLevel) { - await this.AsynchronousODataAnnotationWriter.WriteODataTypeInstanceAnnotationAsync(typeName) - .ConfigureAwait(false); + return this.AsynchronousODataAnnotationWriter.WriteODataTypeInstanceAnnotationAsync(typeName); } else { - await this.AsynchronousODataAnnotationWriter.WriteODataTypePropertyAnnotationAsync(property.Name, typeName) - .ConfigureAwait(false); + return this.AsynchronousODataAnnotationWriter.WriteODataTypePropertyAnnotationAsync(property.Name, typeName); + } } } + + return TaskUtils.CompletedTask; } /// @@ -1162,7 +1162,7 @@ await this.JsonLightValueSerializer.WritePrimitiveValueAsync(primitiveValue.Valu /// Asynchronously writes the type name on the wire. /// /// A task that represents the asynchronous write operation. - private async Task WritePropertyTypeNameAsync() + private Task WritePropertyTypeNameAsync() { string typeNameToWrite = this.currentPropertyInfo.TypeNameToWrite; if (typeNameToWrite != null) @@ -1170,15 +1170,15 @@ private async Task WritePropertyTypeNameAsync() // We write the type name as an instance annotation (named "odata.type") for top-level properties, but as a property annotation (e.g., "...@odata.type") if not top level. if (this.currentPropertyInfo.IsTopLevel) { - await this.AsynchronousODataAnnotationWriter.WriteODataTypeInstanceAnnotationAsync(typeNameToWrite) - .ConfigureAwait(false); + return this.AsynchronousODataAnnotationWriter.WriteODataTypeInstanceAnnotationAsync(typeNameToWrite); } else { - await this.AsynchronousODataAnnotationWriter.WriteODataTypePropertyAnnotationAsync(this.currentPropertyInfo.PropertyName, typeNameToWrite) - .ConfigureAwait(false); + return this.AsynchronousODataAnnotationWriter.WriteODataTypePropertyAnnotationAsync(this.currentPropertyInfo.PropertyName, typeNameToWrite); } } + + return TaskUtils.CompletedTask; } } } diff --git a/src/Microsoft.OData.Core/ODataAnnotatable.cs b/src/Microsoft.OData.Core/ODataAnnotatable.cs index 8f3ab69fe8..6eb75aba39 100644 --- a/src/Microsoft.OData.Core/ODataAnnotatable.cs +++ b/src/Microsoft.OData.Core/ODataAnnotatable.cs @@ -11,21 +11,22 @@ namespace Microsoft.OData { - /// - /// Base class for all annotatable types in OData library. - /// + /// + /// Base class for all annotatable types in OData library. + /// #if ORCAS [Serializable] #endif - public abstract class ODataAnnotatable - { - /// - /// Collection of custom instance annotations. - /// + public abstract class ODataAnnotatable + { + private static readonly ICollection Empty = new ODataInstanceAnnotation[0]; + /// + /// Collection of custom instance annotations. + /// #if ORCAS [NonSerialized] #endif - private ICollection instanceAnnotations = new Collection(); + private ICollection instanceAnnotations; /// /// The annotation for storing @odata.type. @@ -38,8 +39,8 @@ public abstract class ODataAnnotatable /// The custom instance annotations. internal ICollection GetInstanceAnnotations() { - Debug.Assert(this.instanceAnnotations != null, "this.instanceAnnotations != null"); - return this.instanceAnnotations; + //Debug.Assert(this.instanceAnnotations != null, "this.instanceAnnotations != null"); + return this.instanceAnnotations ?? Empty; } /// diff --git a/src/Microsoft.OData.Core/ODataWriterCore.cs b/src/Microsoft.OData.Core/ODataWriterCore.cs index f18d152a5f..25db3185eb 100644 --- a/src/Microsoft.OData.Core/ODataWriterCore.cs +++ b/src/Microsoft.OData.Core/ODataWriterCore.cs @@ -85,7 +85,7 @@ protected ODataWriterCore( this.listener = listener; this.scopeStack.Push(new Scope(WriterState.Start, /*item*/null, navigationSource, resourceType, /*skipWriting*/false, outputContext.MessageWriterSettings.SelectedProperties, odataUri)); - this.CurrentScope.DerivedTypeConstraints = this.outputContext.Model.GetDerivedTypeConstraints(navigationSource); + this.CurrentScope.DerivedTypeConstraints = this.outputContext.Model.GetDerivedTypeConstraints(navigationSource)?.ToList(); } /// @@ -285,7 +285,7 @@ protected Scope ParentScope get { Debug.Assert(this.scopeStack.Count > 1); - return this.scopeStack.Scopes.Skip(1).First(); + return this.scopeStack.Parent; } } @@ -3183,7 +3183,7 @@ private void PushScope(WriterState state, ODataItem item, IEdmNavigationSource n throw new ODataException(errorMessage); } - scope.DerivedTypeConstraints = derivedTypeConstraints; + scope.DerivedTypeConstraints = derivedTypeConstraints?.ToList(); this.scopeStack.Push(scope); } @@ -3991,7 +3991,7 @@ internal bool SkipWriting } /// Gets or sets the derived type constraints for the current scope. - internal IEnumerable DerivedTypeConstraints { get; set; } + internal List DerivedTypeConstraints { get; set; } } /// From 8807a9c4eb0b265467084ddeab0a77adfee0f759 Mon Sep 17 00:00:00 2001 From: Henning Krause Date: Sun, 17 Oct 2021 16:13:39 +0200 Subject: [PATCH 2/9] Fixed failing unit tests --- src/Microsoft.OData.Core/Json/JsonWriterAsync.cs | 2 +- src/Microsoft.OData.Core/ODataAnnotatable.cs | 12 ++++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/Microsoft.OData.Core/Json/JsonWriterAsync.cs b/src/Microsoft.OData.Core/Json/JsonWriterAsync.cs index fa00662028..90802698fc 100644 --- a/src/Microsoft.OData.Core/Json/JsonWriterAsync.cs +++ b/src/Microsoft.OData.Core/Json/JsonWriterAsync.cs @@ -328,7 +328,7 @@ private Task WriteValueSeparatorAsync() if (currentScope.Type == ScopeType.Array) { currentScope.ObjectCount++; - if (currentScope.ObjectCount != 0) + if (currentScope.ObjectCount > 1) { return this.writer.WriteAsync(JsonConstants.ArrayElementSeparator); } diff --git a/src/Microsoft.OData.Core/ODataAnnotatable.cs b/src/Microsoft.OData.Core/ODataAnnotatable.cs index 6eb75aba39..85e22b9773 100644 --- a/src/Microsoft.OData.Core/ODataAnnotatable.cs +++ b/src/Microsoft.OData.Core/ODataAnnotatable.cs @@ -19,8 +19,7 @@ namespace Microsoft.OData #endif public abstract class ODataAnnotatable { - private static readonly ICollection Empty = new ODataInstanceAnnotation[0]; - /// + /// /// Collection of custom instance annotations. /// #if ORCAS @@ -39,8 +38,13 @@ public abstract class ODataAnnotatable /// The custom instance annotations. internal ICollection GetInstanceAnnotations() { - //Debug.Assert(this.instanceAnnotations != null, "this.instanceAnnotations != null"); - return this.instanceAnnotations ?? Empty; + //Debug.Assert(this.instanceAnnotations != null, "this.instanceAnnotations != null"); + if (this.instanceAnnotations == null) + { + this.instanceAnnotations = new List(); + } + + return this.instanceAnnotations; } /// From 71bf1c0279f4ca28d140071d3af3255998698bd8 Mon Sep 17 00:00:00 2001 From: Henning Krause Date: Mon, 25 Oct 2021 18:48:29 +0200 Subject: [PATCH 3/9] Reverted changes to InstanceAnnotationWriteTracker to avoid merge issues --- .../InstanceAnnotationWriteTracker.cs | 72 ++++++++++--------- 1 file changed, 40 insertions(+), 32 deletions(-) diff --git a/src/Microsoft.OData.Core/InstanceAnnotationWriteTracker.cs b/src/Microsoft.OData.Core/InstanceAnnotationWriteTracker.cs index b25ac39a3c..0922f878ba 100644 --- a/src/Microsoft.OData.Core/InstanceAnnotationWriteTracker.cs +++ b/src/Microsoft.OData.Core/InstanceAnnotationWriteTracker.cs @@ -6,38 +6,46 @@ namespace Microsoft.OData { - using System; - using System.Collections.Generic; + using System; + using System.Collections.Generic; - /// - /// Helper class to track if an annotation has been written. - /// - internal sealed class InstanceAnnotationWriteTracker - { - /// - /// Maintains the write status for each annotation using its key. - /// If a key exists in the list then it is considered written. - /// - private readonly Lazy> writeStatus = new Lazy>(() => new HashSet(StringComparer.Ordinal)); + /// + /// Helper class to track if an annotation has been written. + /// + internal sealed class InstanceAnnotationWriteTracker + { + /// + /// Maintains the write status for each annotation using its key. + /// If a key exists in the list then it is considered written. + /// + private readonly HashSet writeStatus; - /// - /// Check if an annotation is already written. - /// - /// true if the annotation is written; otherwise false. - /// The key of the element to check if its written. - public bool IsAnnotationWritten(string key) - { - return this.writeStatus.Value.Contains(key); - } + /// + /// Creates a new to hold write status for instance annotations. + /// + public InstanceAnnotationWriteTracker() + { + this.writeStatus = new HashSet(StringComparer.Ordinal); + } - /// - /// Mark an annotation as written. - /// - /// true if the annotation was unmarked before; otherwise false. - /// The key of the element to mark as written. - public bool MarkAnnotationWritten(string key) - { - return this.writeStatus.Value.Add(key); - } - } -} + /// + /// Check if an annotation is already written. + /// + /// true if the annotation is written; otherwise false. + /// The key of the element to check if its written. + public bool IsAnnotationWritten(string key) + { + return this.writeStatus.Contains(key); + } + + /// + /// Mark an annotation as written. + /// + /// true if the annotation was unmarked before; otherwise false. + /// The key of the element to mark as written. + public bool MarkAnnotationWritten(string key) + { + return this.writeStatus.Add(key); + } + } +} \ No newline at end of file From 5f836e706648c3bb84b517a1699ddd1e688c9107 Mon Sep 17 00:00:00 2001 From: Henning Krause Date: Mon, 8 Nov 2021 17:40:18 +0100 Subject: [PATCH 4/9] Reverted changes in class InstanceAnnotationWriteTracker --- .../InstanceAnnotationWriteTracker.cs | 78 +++++++++---------- 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/src/Microsoft.OData.Core/InstanceAnnotationWriteTracker.cs b/src/Microsoft.OData.Core/InstanceAnnotationWriteTracker.cs index 0922f878ba..c5ba00efcf 100644 --- a/src/Microsoft.OData.Core/InstanceAnnotationWriteTracker.cs +++ b/src/Microsoft.OData.Core/InstanceAnnotationWriteTracker.cs @@ -6,46 +6,46 @@ namespace Microsoft.OData { - using System; - using System.Collections.Generic; + using System; + using System.Collections.Generic; - /// - /// Helper class to track if an annotation has been written. - /// - internal sealed class InstanceAnnotationWriteTracker - { - /// - /// Maintains the write status for each annotation using its key. - /// If a key exists in the list then it is considered written. - /// - private readonly HashSet writeStatus; + /// + /// Helper class to track if an annotation has been written. + /// + internal sealed class InstanceAnnotationWriteTracker + { + /// + /// Maintains the write status for each annotation using its key. + /// If a key exists in the list then it is considered written. + /// + private readonly HashSet writeStatus; - /// - /// Creates a new to hold write status for instance annotations. - /// - public InstanceAnnotationWriteTracker() - { - this.writeStatus = new HashSet(StringComparer.Ordinal); - } + /// + /// Creates a new to hold write status for instance annotations. + /// + public InstanceAnnotationWriteTracker() + { + this.writeStatus = new HashSet(StringComparer.Ordinal); + } - /// - /// Check if an annotation is already written. - /// - /// true if the annotation is written; otherwise false. - /// The key of the element to check if its written. - public bool IsAnnotationWritten(string key) - { - return this.writeStatus.Contains(key); - } + /// + /// Check if an annotation is already written. + /// + /// true if the annotation is written; otherwise false. + /// The key of the element to check if its written. + public bool IsAnnotationWritten(string key) + { + return this.writeStatus.Contains(key); + } - /// - /// Mark an annotation as written. - /// - /// true if the annotation was unmarked before; otherwise false. - /// The key of the element to mark as written. - public bool MarkAnnotationWritten(string key) - { - return this.writeStatus.Add(key); - } - } -} \ No newline at end of file + /// + /// Mark an annotation as written. + /// + /// true if the annotation was unmarked before; otherwise false. + /// The key of the element to mark as written. + public bool MarkAnnotationWritten(string key) + { + return this.writeStatus.Add(key); + } + } +} From 9ddb010c7855d566d5981836f63d33112745374f Mon Sep 17 00:00:00 2001 From: Henning Krause Date: Wed, 10 Nov 2021 18:07:13 +0100 Subject: [PATCH 5/9] Fixed issues from code review --- .../Json/JsonValueUtilsAsync.cs | 21 ++++++++----- .../Json/JsonWriterAsync.cs | 5 ++-- .../JsonLight/ODataJsonLightBatchWriter.cs | 2 +- .../ODataJsonLightPropertySerializer.cs | 9 +++--- src/Microsoft.OData.Core/ODataAnnotatable.cs | 30 +++++++++---------- .../ODataParameterWriterCore.cs | 30 +++++++++---------- 6 files changed, 53 insertions(+), 44 deletions(-) diff --git a/src/Microsoft.OData.Core/Json/JsonValueUtilsAsync.cs b/src/Microsoft.OData.Core/Json/JsonValueUtilsAsync.cs index a7c78a6467..c08001c907 100644 --- a/src/Microsoft.OData.Core/Json/JsonValueUtilsAsync.cs +++ b/src/Microsoft.OData.Core/Json/JsonValueUtilsAsync.cs @@ -39,7 +39,7 @@ internal static Task WriteValueAsync(this TextWriter writer, char value, ODataSt string escapedString = SpecialCharToEscapedStringMap[value]; if (escapedString != null) { - return writer.WriteAsync(escapedString); + return writer.WriteAsync(escapedString); } } @@ -127,7 +127,7 @@ internal static Task WriteValueAsync(this TextWriter writer, double value) if (JsonSharedUtils.IsDoubleValueSerializedAsString(value)) { - return writer.WriteQuotedAsync(value.ToString(ODataNumberFormatInfo)); + return writer.WriteQuotedAsync(value.ToString(ODataNumberFormatInfo)); } // double.ToString() supports a max scale of 14, @@ -137,7 +137,7 @@ internal static Task WriteValueAsync(this TextWriter writer, double value) if (valueToWrite.IndexOfAny(DoubleIndicatingCharacters) < 0) { - valueToWrite += ".0"; + valueToWrite += ".0"; } return writer.WriteAsync(valueToWrite); @@ -317,7 +317,7 @@ internal static async Task WriteValueAsync(this TextWriter writer, byte[] value, } else { - await writer.WriteAsync(JsonConstants.QuoteCharacter).ConfigureAwait(false); + await writer.WriteAsync(JsonConstants.QuoteCharacter).ConfigureAwait(false); await writer.WriteBinaryStringAsync(value, buffer, arrayPool).ConfigureAwait(false); await writer.WriteAsync(JsonConstants.QuoteCharacter).ConfigureAwait(false); } @@ -457,14 +457,21 @@ internal static Task WriteEscapedCharArrayAsync( ICharArrayPool bufferPool) { int bufferIndex = 0; - buffer.Value = BufferUtils.InitializeBufferIfRequired(bufferPool, buffer.Value); + try + { + buffer.Value = BufferUtils.InitializeBufferIfRequired(bufferPool, buffer.Value); - WriteEscapedCharArrayToBuffer(writer, inputArray, ref inputArrayOffset, inputArrayCount, buffer.Value, ref bufferIndex, stringEscapeOption); + WriteEscapedCharArrayToBuffer(writer, inputArray, ref inputArrayOffset, inputArrayCount, buffer.Value, ref bufferIndex, stringEscapeOption); + } + catch (Exception ex) + { + return TaskUtils.GetFaultedTask(ex); + } // write remaining bytes in buffer if (bufferIndex > 0) { - return writer.WriteAsync(buffer.Value, 0, bufferIndex); + return writer.WriteAsync(buffer.Value, 0, bufferIndex); } return TaskUtils.CompletedTask; diff --git a/src/Microsoft.OData.Core/Json/JsonWriterAsync.cs b/src/Microsoft.OData.Core/Json/JsonWriterAsync.cs index 90802698fc..4eed11ba11 100644 --- a/src/Microsoft.OData.Core/Json/JsonWriterAsync.cs +++ b/src/Microsoft.OData.Core/Json/JsonWriterAsync.cs @@ -327,14 +327,15 @@ private Task WriteValueSeparatorAsync() Scope currentScope = this.scopes.Peek(); if (currentScope.Type == ScopeType.Array) { - currentScope.ObjectCount++; + currentScope.ObjectCount++; if (currentScope.ObjectCount > 1) { - return this.writer.WriteAsync(JsonConstants.ArrayElementSeparator); + return this.writer.WriteAsync(JsonConstants.ArrayElementSeparator); } } + return TaskUtils.CompletedTask; } diff --git a/src/Microsoft.OData.Core/JsonLight/ODataJsonLightBatchWriter.cs b/src/Microsoft.OData.Core/JsonLight/ODataJsonLightBatchWriter.cs index f8fe026bfe..6dc3f41ec4 100644 --- a/src/Microsoft.OData.Core/JsonLight/ODataJsonLightBatchWriter.cs +++ b/src/Microsoft.OData.Core/JsonLight/ODataJsonLightBatchWriter.cs @@ -503,7 +503,7 @@ await asynchronousJsonWriter.EndObjectScopeAsync() /// Asynchronously starts a new changeset. /// /// The atomic group id of the changeset to start. - /// If it is null for Json batch, an GUID will be generated and used as the atomic group id. + /// If it is null for Json batch, an GUID will be generated and used as the atomic group id. /// A task that represents the asynchronous write operation. protected override async Task WriteStartChangesetImplementationAsync(string groupId) { diff --git a/src/Microsoft.OData.Core/JsonLight/ODataJsonLightPropertySerializer.cs b/src/Microsoft.OData.Core/JsonLight/ODataJsonLightPropertySerializer.cs index b66a74f81d..c1c7cd5a99 100644 --- a/src/Microsoft.OData.Core/JsonLight/ODataJsonLightPropertySerializer.cs +++ b/src/Microsoft.OData.Core/JsonLight/ODataJsonLightPropertySerializer.cs @@ -922,13 +922,14 @@ private Task WriteInstanceAnnotationAsync(ODataPropertyInfo property, bool isTop { if (isTopLevel) { - return this.InstanceAnnotationWriter.WriteInstanceAnnotationsAsync(property.InstanceAnnotations); + return this.InstanceAnnotationWriter.WriteInstanceAnnotationsAsync(property.InstanceAnnotations); } else { - return this.InstanceAnnotationWriter.WriteInstanceAnnotationsAsync(property.InstanceAnnotations, property.Name, isUndeclaredProperty); + return this.InstanceAnnotationWriter.WriteInstanceAnnotationsAsync(property.InstanceAnnotations, property.Name, isUndeclaredProperty); } } + return TaskUtils.CompletedTask; } @@ -955,7 +956,7 @@ private Task WriteODataTypeAnnotationAsync(ODataPropertyInfo property, bool isTo } else { - return this.AsynchronousODataAnnotationWriter.WriteODataTypePropertyAnnotationAsync(property.Name, typeName); + return this.AsynchronousODataAnnotationWriter.WriteODataTypePropertyAnnotationAsync(property.Name, typeName); } } @@ -1170,7 +1171,7 @@ private Task WritePropertyTypeNameAsync() // We write the type name as an instance annotation (named "odata.type") for top-level properties, but as a property annotation (e.g., "...@odata.type") if not top level. if (this.currentPropertyInfo.IsTopLevel) { - return this.AsynchronousODataAnnotationWriter.WriteODataTypeInstanceAnnotationAsync(typeNameToWrite); + return this.AsynchronousODataAnnotationWriter.WriteODataTypeInstanceAnnotationAsync(typeNameToWrite); } else { diff --git a/src/Microsoft.OData.Core/ODataAnnotatable.cs b/src/Microsoft.OData.Core/ODataAnnotatable.cs index 85e22b9773..ccab9f2167 100644 --- a/src/Microsoft.OData.Core/ODataAnnotatable.cs +++ b/src/Microsoft.OData.Core/ODataAnnotatable.cs @@ -11,21 +11,21 @@ namespace Microsoft.OData { - /// - /// Base class for all annotatable types in OData library. - /// + /// + /// Base class for all annotatable types in OData library. + /// #if ORCAS [Serializable] #endif - public abstract class ODataAnnotatable - { - /// - /// Collection of custom instance annotations. - /// + public abstract class ODataAnnotatable + { + /// + /// Collection of custom instance annotations. + /// #if ORCAS [NonSerialized] #endif - private ICollection instanceAnnotations; + private ICollection instanceAnnotations; /// /// The annotation for storing @odata.type. @@ -38,13 +38,13 @@ public abstract class ODataAnnotatable /// The custom instance annotations. internal ICollection GetInstanceAnnotations() { - //Debug.Assert(this.instanceAnnotations != null, "this.instanceAnnotations != null"); - if (this.instanceAnnotations == null) - { - this.instanceAnnotations = new List(); - } + //Debug.Assert(this.instanceAnnotations != null, "this.instanceAnnotations != null"); + if (this.instanceAnnotations == null) + { + this.instanceAnnotations = new List(); + } - return this.instanceAnnotations; + return this.instanceAnnotations; } /// diff --git a/src/Microsoft.OData.Core/ODataParameterWriterCore.cs b/src/Microsoft.OData.Core/ODataParameterWriterCore.cs index dc7f20d4f6..690f70d3c6 100644 --- a/src/Microsoft.OData.Core/ODataParameterWriterCore.cs +++ b/src/Microsoft.OData.Core/ODataParameterWriterCore.cs @@ -411,7 +411,7 @@ Task IODataOutputInStreamErrorListener.OnInStreamErrorAsync() /// /// Asynchronously finish writing an OData payload. /// - /// A task that represents the asynchronous write operation. + /// A task that represents the asynchronous write operation. protected abstract Task EndPayloadAsync(); /// @@ -420,7 +420,7 @@ Task IODataOutputInStreamErrorListener.OnInStreamErrorAsync() /// The name of the parameter to write. /// The value of the parameter to write. /// The expected type reference of the parameter value. - /// A task that represents the asynchronous write operation. + /// A task that represents the asynchronous write operation. protected abstract Task WriteValueParameterAsync(string parameterName, object parameterValue, IEdmTypeReference expectedTypeReference); /// @@ -428,25 +428,25 @@ Task IODataOutputInStreamErrorListener.OnInStreamErrorAsync() /// /// The name of the collection parameter to write. /// The type reference of the expected item type or null if no expected item type exists. - /// A task that represents the asynchronous operation. + /// A task that represents the asynchronous operation. /// The value of the TResult parameter contains the newly created . protected abstract Task CreateFormatCollectionWriterAsync(string parameterName, IEdmTypeReference expectedItemType); /// - /// Asynchronously creates a format specific to write a resource. - /// + /// Asynchronously creates a format specific to write a resource. + /// /// The name of the parameter to write. /// The type reference of the expected item type or null if no expected item type exists. - /// A task that represents the asynchronous operation. + /// A task that represents the asynchronous operation. /// The value of the TResult parameter contains the newly created . protected abstract Task CreateFormatResourceWriterAsync(string parameterName, IEdmTypeReference expectedItemType); /// - /// Asynchronously creates a format specific to write a resource set. - /// + /// Asynchronously creates a format specific to write a resource set. + /// /// The name of the parameter to write. /// The type reference of the expected item type or null if no expected item type exists. - /// A task that represents the asynchronous operation. + /// A task that represents the asynchronous operation. /// The value of the TResult parameter contains the newly created . protected abstract Task CreateFormatResourceSetWriterAsync(string parameterName, IEdmTypeReference expectedItemType); @@ -1025,7 +1025,7 @@ private async Task InterceptExceptionAsync(Func /// Asynchronously start writing a parameter payload - implementation of the actual functionality. /// - /// A task that represents the asynchronous write operation. + /// A task that represents the asynchronous write operation. private async Task WriteStartImplementationAsync() { Debug.Assert(this.State == ParameterWriterState.Start, "this.State == ParameterWriterState.Start"); @@ -1041,7 +1041,7 @@ await this.InterceptExceptionAsync((thisParam) => thisParam.StartPayloadAsync()) /// The name of the parameter to write. /// The value of the parameter to write (null/ODataEnumValue/primitiveClrValue). /// The expected type reference of the parameter value. - /// A task that represents the asynchronous write operation. + /// A task that represents the asynchronous write operation. private Task WriteValueImplementationAsync(string parameterName, object parameterValue, IEdmTypeReference expectedTypeReference) { Debug.Assert(this.State == ParameterWriterState.CanWriteParameter, "this.State == ParameterWriterState.CanWriteParameter"); @@ -1054,7 +1054,7 @@ private Task WriteValueImplementationAsync(string parameterName, object paramete /// /// The name of the collection parameter to write. /// The type reference of the expected item type or null if no expected item type exists. - /// A task that represents the asynchronous operation. + /// A task that represents the asynchronous operation. /// The value of the TResult parameter contains the newly created . private async Task CreateCollectionWriterImplementationAsync(string parameterName, IEdmTypeReference expectedItemType) { @@ -1071,7 +1071,7 @@ private async Task CreateCollectionWriterImplementationAs /// /// The name of the parameter to write. /// The type reference of the expected item type or null if no expected item type exists. - /// A task that represents the asynchronous operation. + /// A task that represents the asynchronous operation. /// The value of the TResult parameter contains the newly created . private async Task CreateResourceWriterImplementationAsync(string parameterName, IEdmTypeReference expectedItemType) { @@ -1089,7 +1089,7 @@ private async Task CreateResourceWriterImplementationAsync(string p /// /// The name of the collection parameter to write. /// The type reference of the expected item type or null if no expected item type exists. - /// A task that represents the asynchronous operation. + /// A task that represents the asynchronous operation. /// The value of the TResult parameter contains the newly created . private async Task CreateResourceSetWriterImplementationAsync(string parameterName, IEdmTypeReference expectedItemType) { @@ -1105,7 +1105,7 @@ private async Task CreateResourceSetWriterImplementationAsync(strin /// /// Asynchronously finish writing a parameter payload - implementation of the actual functionality. /// - /// A task that represents the asynchronous write operation. + /// A task that represents the asynchronous write operation. private async Task WriteEndImplementationAsync() { await this.InterceptExceptionAsync((thisParam) => thisParam.EndPayloadAsync()) From 9e32638dab38ed2b54b3d557a16a06c3b15eb929 Mon Sep 17 00:00:00 2001 From: Henning Krause Date: Wed, 10 Nov 2021 19:19:35 +0100 Subject: [PATCH 6/9] Fixed issues from code review --- src/Microsoft.OData.Core/Json/JsonValueUtilsAsync.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Microsoft.OData.Core/Json/JsonValueUtilsAsync.cs b/src/Microsoft.OData.Core/Json/JsonValueUtilsAsync.cs index c08001c907..0a6432e04b 100644 --- a/src/Microsoft.OData.Core/Json/JsonValueUtilsAsync.cs +++ b/src/Microsoft.OData.Core/Json/JsonValueUtilsAsync.cs @@ -463,7 +463,7 @@ internal static Task WriteEscapedCharArrayAsync( WriteEscapedCharArrayToBuffer(writer, inputArray, ref inputArrayOffset, inputArrayCount, buffer.Value, ref bufferIndex, stringEscapeOption); } - catch (Exception ex) + catch (ODataException ex) { return TaskUtils.GetFaultedTask(ex); } From 7c2316241ad39010168bf10a5b980cfe2a5aae34 Mon Sep 17 00:00:00 2001 From: Henning Krause Date: Thu, 11 Nov 2021 18:33:46 +0100 Subject: [PATCH 7/9] Resolved issue from code review --- src/Microsoft.OData.Core/ODataAnnotatable.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Microsoft.OData.Core/ODataAnnotatable.cs b/src/Microsoft.OData.Core/ODataAnnotatable.cs index ccab9f2167..1e2f2568bb 100644 --- a/src/Microsoft.OData.Core/ODataAnnotatable.cs +++ b/src/Microsoft.OData.Core/ODataAnnotatable.cs @@ -38,7 +38,6 @@ public abstract class ODataAnnotatable /// The custom instance annotations. internal ICollection GetInstanceAnnotations() { - //Debug.Assert(this.instanceAnnotations != null, "this.instanceAnnotations != null"); if (this.instanceAnnotations == null) { this.instanceAnnotations = new List(); From b3623b60623300ce7969ec62de004743c98aa1e2 Mon Sep 17 00:00:00 2001 From: Henning Krause Date: Thu, 11 Nov 2021 18:43:13 +0100 Subject: [PATCH 8/9] Reverted WriteEscapedCharArrayAsync to be async again. --- .../Json/JsonValueUtilsAsync.cs | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/src/Microsoft.OData.Core/Json/JsonValueUtilsAsync.cs b/src/Microsoft.OData.Core/Json/JsonValueUtilsAsync.cs index 0a6432e04b..d36232ff8e 100644 --- a/src/Microsoft.OData.Core/Json/JsonValueUtilsAsync.cs +++ b/src/Microsoft.OData.Core/Json/JsonValueUtilsAsync.cs @@ -447,7 +447,7 @@ internal static async Task WriteEscapedJsonStringValueAsync( /// The string escape option. /// Char buffer to use for streaming data. /// Character buffer pool. - internal static Task WriteEscapedCharArrayAsync( + internal static async Task WriteEscapedCharArrayAsync( this TextWriter writer, char[] inputArray, int inputArrayOffset, @@ -457,24 +457,15 @@ internal static Task WriteEscapedCharArrayAsync( ICharArrayPool bufferPool) { int bufferIndex = 0; - try - { - buffer.Value = BufferUtils.InitializeBufferIfRequired(bufferPool, buffer.Value); - - WriteEscapedCharArrayToBuffer(writer, inputArray, ref inputArrayOffset, inputArrayCount, buffer.Value, ref bufferIndex, stringEscapeOption); - } - catch (ODataException ex) - { - return TaskUtils.GetFaultedTask(ex); - } + buffer.Value = BufferUtils.InitializeBufferIfRequired(bufferPool, buffer.Value); + + WriteEscapedCharArrayToBuffer(writer, inputArray, ref inputArrayOffset, inputArrayCount, buffer.Value, ref bufferIndex, stringEscapeOption); // write remaining bytes in buffer if (bufferIndex > 0) { - return writer.WriteAsync(buffer.Value, 0, bufferIndex); + await writer.WriteAsync(buffer.Value, 0, bufferIndex).ConfigureAwait(false); } - - return TaskUtils.CompletedTask; } /// From 46aad16e438c0f928fcbb9e9b3ee46f75e6a2335 Mon Sep 17 00:00:00 2001 From: Henning Krause Date: Tue, 4 Jan 2022 12:10:34 +0100 Subject: [PATCH 9/9] Changed usage of "var" to explicit type declaration --- src/Microsoft.OData.Core/Json/JsonValueUtilsAsync.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Microsoft.OData.Core/Json/JsonValueUtilsAsync.cs b/src/Microsoft.OData.Core/Json/JsonValueUtilsAsync.cs index d36232ff8e..e85995f35a 100644 --- a/src/Microsoft.OData.Core/Json/JsonValueUtilsAsync.cs +++ b/src/Microsoft.OData.Core/Json/JsonValueUtilsAsync.cs @@ -133,7 +133,7 @@ internal static Task WriteValueAsync(this TextWriter writer, double value) // double.ToString() supports a max scale of 14, // whereas double.MinValue and double.MaxValue have 16 digits scale. Hence we need // to use XmlConvert in all other cases, except infinity - var valueToWrite = XmlConvert.ToString(value); + string valueToWrite = XmlConvert.ToString(value); if (valueToWrite.IndexOfAny(DoubleIndicatingCharacters) < 0) {