diff --git a/Microsoft.Azure.Cosmos/src/Resource/CosmosExceptions/CosmosOperationCanceledException.cs b/Microsoft.Azure.Cosmos/src/Resource/CosmosExceptions/CosmosOperationCanceledException.cs index 131c6c8203..310616198c 100644 --- a/Microsoft.Azure.Cosmos/src/Resource/CosmosExceptions/CosmosOperationCanceledException.cs +++ b/Microsoft.Azure.Cosmos/src/Resource/CosmosExceptions/CosmosOperationCanceledException.cs @@ -6,18 +6,18 @@ namespace Microsoft.Azure.Cosmos { using System; using System.Collections; - using System.Threading; + using System.Runtime.Serialization; using global::Azure.Core.Pipeline; using Microsoft.Azure.Cosmos.Diagnostics; using Microsoft.Azure.Cosmos.Telemetry; - using Microsoft.Azure.Cosmos.Telemetry.Diagnostics; using Microsoft.Azure.Cosmos.Tracing; /// /// The exception that is thrown in a thread upon cancellation of an operation that /// the thread was executing. This extends the OperationCanceledException to include the /// diagnostics of the operation that was canceled. - /// + /// + [Serializable] public class CosmosOperationCanceledException : OperationCanceledException { private readonly OperationCanceledException originalException; @@ -63,6 +63,22 @@ internal CosmosOperationCanceledException( this.lazyMessage = this.CreateLazyMessage(); } + /// + /// Initializes a new instance of the class. + /// + /// The SerializationInfo object that holds serialized object data for the exception being thrown. + /// The StreamingContext that contains contextual information about the source or destination. + protected CosmosOperationCanceledException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + this.originalException = (OperationCanceledException)info.GetValue("originalException", typeof(OperationCanceledException)); + this.tokenCancellationRequested = (bool)info.GetValue("tokenCancellationRequested", typeof(bool)); + this.lazyMessage = new Lazy(() => (string)info.GetValue("lazyMessage", typeof(string))); + this.toStringMessage = new Lazy(() => (string)info.GetValue("toStringMessage", typeof(string))); + //Diagnostics cannot be serialized + this.Diagnostics = new CosmosTraceDiagnostics(NoOpTrace.Singleton); + } + /// public override string Source { @@ -111,7 +127,7 @@ private Lazy CreateToStringMessage() { return new Lazy(() => $"{this.originalException}{Environment.NewLine}Cancellation Token has expired: {this.tokenCancellationRequested}. Learn more at: https://aka.ms/cosmosdb-tsg-request-timeout{Environment.NewLine}CosmosDiagnostics: {this.Diagnostics}"); } - + /// /// RecordOtelAttributes /// @@ -123,5 +139,19 @@ internal static void RecordOtelAttributes(CosmosOperationCanceledException excep scope.AddAttribute(OpenTelemetryAttributeKeys.RequestDiagnostics, exception.Diagnostics); scope.AddAttribute(OpenTelemetryAttributeKeys.ExceptionMessage, exception.GetBaseException().Message); } + + /// + /// Sets the System.Runtime.Serialization.SerializationInfo with information about the exception. + /// + /// The SerializationInfo object that holds serialized object data for the exception being thrown. + /// The StreamingContext that contains contextual information about the source or destination. + public override void GetObjectData(SerializationInfo info, StreamingContext context) + { + base.GetObjectData(info, context); + info.AddValue("originalException", this.originalException); + info.AddValue("tokenCancellationRequested", this.tokenCancellationRequested); + info.AddValue("lazyMessage", this.lazyMessage.Value); + info.AddValue("toStringMessage", this.toStringMessage.Value); + } } } diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Contracts/DotNetSDKAPI.json b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Contracts/DotNetSDKAPI.json index bc28d53536..1a69454b04 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Contracts/DotNetSDKAPI.json +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Contracts/DotNetSDKAPI.json @@ -3259,7 +3259,7 @@ }, "NestedTypes": {} }, - "Microsoft.Azure.Cosmos.CosmosOperationCanceledException;System.OperationCanceledException;IsAbstract:False;IsSealed:False;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": { + "Microsoft.Azure.Cosmos.CosmosOperationCanceledException;System.OperationCanceledException;IsAbstract:False;IsSealed:False;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:True": { "Subclasses": {}, "Members": { "Microsoft.Azure.Cosmos.CosmosDiagnostics Diagnostics": { @@ -3339,6 +3339,11 @@ "Attributes": [], "MethodInfo": "[Void .ctor(System.OperationCanceledException, Microsoft.Azure.Cosmos.CosmosDiagnostics), Void .ctor(System.OperationCanceledException, Microsoft.Azure.Cosmos.CosmosDiagnostics)]" }, + "Void GetObjectData(System.Runtime.Serialization.SerializationInfo, System.Runtime.Serialization.StreamingContext)": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "Void GetObjectData(System.Runtime.Serialization.SerializationInfo, System.Runtime.Serialization.StreamingContext);IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, "Void set_HelpLink(System.String)": { "Type": "Method", "Attributes": [], diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CosmosOperationCanceledTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CosmosOperationCanceledTests.cs new file mode 100644 index 0000000000..5262c2b343 --- /dev/null +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CosmosOperationCanceledTests.cs @@ -0,0 +1,39 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +namespace Microsoft.Azure.Cosmos.Tests +{ + using System; + using Microsoft.Azure.Cosmos.Diagnostics; + using Microsoft.Azure.Cosmos.Tracing; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Newtonsoft.Json; + + [TestClass] + public class CosmosOperationCanceledTests + { + + [TestMethod] + public void SerializationValidation() + { + //create test exception + CosmosOperationCanceledException originalException = new CosmosOperationCanceledException( + new OperationCanceledException("error message"), + new CosmosTraceDiagnostics(NoOpTrace.Singleton)); + + //serialize exception + string serialized = JsonConvert.SerializeObject(originalException); + + CosmosOperationCanceledException deserializedExceptoin = + JsonConvert.DeserializeObject(serialized); + + //Asserts + Assert.AreEqual(originalException.ToString(), deserializedExceptoin.ToString()); + Assert.AreEqual(originalException.Message, deserializedExceptoin.Message); + Assert.AreEqual(originalException.GetBaseException().Message, deserializedExceptoin.GetBaseException().Message); + Assert.AreEqual(originalException.GetBaseException().ToString(), deserializedExceptoin.GetBaseException().ToString()); + Assert.AreEqual(originalException.GetBaseException().HResult, deserializedExceptoin.GetBaseException().HResult); + } + } +}