diff --git a/.github/workflows/codeQL.yml b/.github/workflows/codeQL.yml
index 7245dd97e..624ea3d32 100644
--- a/.github/workflows/codeQL.yml
+++ b/.github/workflows/codeQL.yml
@@ -39,7 +39,7 @@ jobs:
steps:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
- uses: github/codeql-action/init@v3
+ uses: github/codeql-action/init@v4
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
@@ -49,25 +49,12 @@ jobs:
# Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
# queries: security-extended,security-and-quality
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v7
with:
submodules: true
- - name: Setup .NET
- uses: actions/setup-dotnet@v3
-
- - name: Set up .NET Core 2.1
- uses: actions/setup-dotnet@v3
- with:
- dotnet-version: '2.1.x'
-
- - name: Set up .NET Core 3.1
- uses: actions/setup-dotnet@v3
- with:
- dotnet-version: '3.1.x'
-
- name: Set up .NET 8
- uses: actions/setup-dotnet@v3
+ uses: actions/setup-dotnet@v6
with:
dotnet-version: '8.0.x'
@@ -79,6 +66,6 @@ jobs:
# Run CodeQL analysis
- name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@v3
+ uses: github/codeql-action/analyze@v4
with:
category: "/language:${{matrix.language}}"
\ No newline at end of file
diff --git a/Directory.Packages.props b/Directory.Packages.props
index f088054dc..e5de5b962 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -9,60 +9,62 @@
-
+
+
-
-
-
+
+
+
-
-
+
-
+
-
+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
-
+
-
+
+
-
+
+
-
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
+
@@ -70,49 +72,42 @@
-
+
-
+
-
+
-
-
+
-
-
-
-
-
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
+
+
+
+
diff --git a/Test/DurableTask.AzureStorage.Tests/Storage/DurableTaskStorageExceptionTests.cs b/Test/DurableTask.AzureStorage.Tests/Storage/DurableTaskStorageExceptionTests.cs
index f57918638..399ccd2d8 100644
--- a/Test/DurableTask.AzureStorage.Tests/Storage/DurableTaskStorageExceptionTests.cs
+++ b/Test/DurableTask.AzureStorage.Tests/Storage/DurableTaskStorageExceptionTests.cs
@@ -30,7 +30,7 @@ public void MissingRequestFailedException()
Assert.IsFalse(exception.LeaseLost);
}
- [DataTestMethod]
+ [TestMethod]
[DataRow(true, HttpStatusCode.Conflict, nameof(BlobErrorCode.LeaseLost))]
[DataRow(false, HttpStatusCode.Conflict, nameof(BlobErrorCode.LeaseNotPresentWithBlobOperation))]
[DataRow(false, HttpStatusCode.NotFound, nameof(BlobErrorCode.BlobNotFound))]
diff --git a/docs/getting-started/installation.md b/docs/getting-started/installation.md
index d1e511090..89f6dfac9 100644
--- a/docs/getting-started/installation.md
+++ b/docs/getting-started/installation.md
@@ -4,7 +4,7 @@ This guide covers installing the Durable Task Framework (DTFx) packages for your
## Prerequisites
-- .NET 6.0 or later (.NET 10.0 is currently recommended)
+- .NET 8.0 or later (.NET 10.0 is currently recommended)
- .NET Framework 4.7.2 or later (for .NET Framework projects)
## NuGet Packages
diff --git a/docs/providers/service-bus.md b/docs/providers/service-bus.md
index 5e17bb934..d3461390e 100644
--- a/docs/providers/service-bus.md
+++ b/docs/providers/service-bus.md
@@ -59,7 +59,7 @@ var client = new TaskHubClient(service, loggerFactory: loggerFactory);
### Using Managed Identity
-The Service Bus provider supports managed identity authentication (.NET Standard 2.0+):
+The Service Bus provider supports managed identity authentication on .NET 8.0 or later and on the .NET Framework 4.7.2 (`net472`) target:
```csharp
using Azure.Identity;
diff --git a/eng/templates/build.yml b/eng/templates/build.yml
index a6b0993fd..14864aea3 100644
--- a/eng/templates/build.yml
+++ b/eng/templates/build.yml
@@ -10,6 +10,12 @@ jobs:
sbomPackageName: 'DurableTask SBOM'
steps:
+ - task: UseDotNet@2
+ displayName: 'Use the .NET 8 SDK'
+ inputs:
+ packageType: 'sdk'
+ version: '8.0.x'
+
# Start by restoring all the dependencies. This needs to be its own task
# from what I can tell. We specifically only target DurableTask.AzureStorage
# and its direct dependencies.
@@ -34,7 +40,7 @@ jobs:
displayName: 'Build (AzureStorage)'
inputs:
solution: 'src/DurableTask.AzureStorage/DurableTask.AzureStorage.sln'
- vsVersion: '16.0'
+ vsVersion: '17.0'
logFileVerbosity: minimal
configuration: Release
msbuildArgs: /p:FileVersionRevision=$(Build.BuildId) /p:ContinuousIntegrationBuild=true
@@ -43,7 +49,7 @@ jobs:
displayName: 'Build (ApplicationInsights)'
inputs:
solution: 'src/DurableTask.ApplicationInsights/DurableTask.ApplicationInsights.csproj'
- vsVersion: '16.0'
+ vsVersion: '17.0'
logFileVerbosity: minimal
configuration: Release
msbuildArgs: /p:FileVersionRevision=$(Build.BuildId) /p:ContinuousIntegrationBuild=true
@@ -52,7 +58,7 @@ jobs:
displayName: 'Build (Emulator)'
inputs:
solution: 'src/DurableTask.Emulator/DurableTask.Emulator.csproj'
- vsVersion: '16.0'
+ vsVersion: '17.0'
logFileVerbosity: minimal
configuration: Release
msbuildArgs: /p:FileVersionRevision=$(Build.BuildId) /p:ContinuousIntegrationBuild=true
@@ -61,7 +67,7 @@ jobs:
displayName: 'Build (ServiceBus)'
inputs:
solution: 'src/DurableTask.ServiceBus/DurableTask.ServiceBus.csproj'
- vsVersion: '16.0'
+ vsVersion: '17.0'
logFileVerbosity: minimal
configuration: Release
platform: x64
@@ -71,18 +77,12 @@ jobs:
displayName: 'Build (AzureServiceFabric)'
inputs:
solution: 'src/DurableTask.AzureServiceFabric/DurableTask.AzureServiceFabric.csproj'
- vsVersion: '16.0'
+ vsVersion: '17.0'
logFileVerbosity: minimal
configuration: Release
platform: x64
msbuildArgs: /p:FileVersionRevision=$(Build.BuildId) /p:ContinuousIntegrationBuild=true
- #- task: UseDotNet@2
- # displayName: 'Use the .NET Core 6 SDK (required for build signing)'
- # inputs:
- # packageType: 'sdk'
- # version: '6.x.x'
-
- template: ci/sign-files.yml@eng
parameters:
displayName: Sign assemblies
diff --git a/samples/Correlation.Samples/docs/getting-started.md b/samples/Correlation.Samples/docs/getting-started.md
index 8f5256a83..a4518f287 100644
--- a/samples/Correlation.Samples/docs/getting-started.md
+++ b/samples/Correlation.Samples/docs/getting-started.md
@@ -6,7 +6,7 @@ In this tutorial, you can configure and execute a distributed tracing sample app
The sample application requires these tools. If you don't have it, please click the following link and install it or create it on your Azure subscription.
-- [Visual Studio 2019+](https://visualstudio.microsoft.com/vs/)
+- [Visual Studio 2022 17.8+](https://visualstudio.microsoft.com/vs/)
- [Storage Emulator 5.9+](https://docs.microsoft.com/en-us/azure/storage/common/storage-use-emulator)
- [Application Insights](https://docs.microsoft.com/en-us/azure/azure-monitor/app/create-new-resource)
- [Azure Subscription](https://azure.microsoft.com/en-us/)
diff --git a/samples/DistributedTraceSample/ApplicationInsights/README.md b/samples/DistributedTraceSample/ApplicationInsights/README.md
index cf358239a..3dd3ea50e 100644
--- a/samples/DistributedTraceSample/ApplicationInsights/README.md
+++ b/samples/DistributedTraceSample/ApplicationInsights/README.md
@@ -4,7 +4,7 @@ This sample demonstrates direct integration with Azure Application Insights for
## Prerequisites
-- .NET 6.0 SDK or later
+- .NET 8.0 SDK or later
- Azure Storage Emulator (Azurite) or Azure Storage account
- Azure Application Insights resource
diff --git a/samples/DistributedTraceSample/README.md b/samples/DistributedTraceSample/README.md
index f74316198..6d68a5a20 100644
--- a/samples/DistributedTraceSample/README.md
+++ b/samples/DistributedTraceSample/README.md
@@ -44,7 +44,7 @@ services.TryAddEnumerable(
## Prerequisites
-- .NET 6.0 SDK or later
+- .NET 8.0 SDK or later
- Azure Storage Emulator (Azurite) or Azure Storage account
- (Optional) Application Insights resource
- (Optional) Zipkin instance for OpenTelemetry sample
diff --git a/src/DurableTask.ApplicationInsights/DurableTask.ApplicationInsights.csproj b/src/DurableTask.ApplicationInsights/DurableTask.ApplicationInsights.csproj
index 24841bb21..84235f4ef 100644
--- a/src/DurableTask.ApplicationInsights/DurableTask.ApplicationInsights.csproj
+++ b/src/DurableTask.ApplicationInsights/DurableTask.ApplicationInsights.csproj
@@ -2,7 +2,7 @@
- netstandard2.0
+ net8.0;net472Microsoft.Azure.DurableTask.ApplicationInsightsNU5125;CS7035
diff --git a/src/DurableTask.AzureServiceFabric/DurableTask.AzureServiceFabric.csproj b/src/DurableTask.AzureServiceFabric/DurableTask.AzureServiceFabric.csproj
index 2c2c35ba5..6cb82ff5d 100644
--- a/src/DurableTask.AzureServiceFabric/DurableTask.AzureServiceFabric.csproj
+++ b/src/DurableTask.AzureServiceFabric/DurableTask.AzureServiceFabric.csproj
@@ -24,12 +24,6 @@
-
-
-
-
-
-
diff --git a/src/DurableTask.AzureServiceFabric/README.md b/src/DurableTask.AzureServiceFabric/README.md
index 229674327..3fa1da87a 100644
--- a/src/DurableTask.AzureServiceFabric/README.md
+++ b/src/DurableTask.AzureServiceFabric/README.md
@@ -41,7 +41,7 @@ There are pre-requisites for setting up this project for developing or making co
## .Net Targets
-Currently this project supports .Net 4.6.1, which is framework version of 'netstandard2.0'. Some of the dependency packages do not have support for 'netstandard2.0', once those packages support 'netstandard2.0' this project will also support the same.
+This project supports .NET Framework 4.7.2 or later.
## Main Contributors
diff --git a/src/DurableTask.AzureStorage/AzureStorageOrchestrationService.cs b/src/DurableTask.AzureStorage/AzureStorageOrchestrationService.cs
index 74798cb45..555c138fb 100644
--- a/src/DurableTask.AzureStorage/AzureStorageOrchestrationService.cs
+++ b/src/DurableTask.AzureStorage/AzureStorageOrchestrationService.cs
@@ -456,7 +456,9 @@ public async Task StartAsync()
// Disable nagling to improve storage access latency:
// https://blogs.msdn.microsoft.com/windowsazurestorage/2010/06/25/nagles-algorithm-is-not-friendly-towards-small-requests/
// Ad-hoc testing has shown very nice improvements (20%-50% drop in queue message age for simple scenarios).
+#if NETFRAMEWORK
ServicePointManager.FindServicePoint(this.workItemQueue.Uri).UseNagleAlgorithm = false;
+#endif
this.shutdownSource?.Dispose();
this.shutdownSource = new CancellationTokenSource();
@@ -1158,8 +1160,8 @@ public async Task CompleteTaskOrchestrationWorkItemAsync(
Utils.ConvertDateTimeInHistoryEventsToUTC(timerMessage.Event);
}
- OrchestrationSession session;
- if (!this.orchestrationSessionManager.TryGetExistingSession(workItem.InstanceId, out session))
+ OrchestrationSession session = null;
+ if (!this.orchestrationSessionManager.TryGetExistingSession(workItem.InstanceId, out session) || session == null)
{
this.settings.Logger.AssertFailure(
this.azureStorageClient.QueueAccountName,
@@ -1453,8 +1455,8 @@ await messagesToDelete.ParallelForEachAsync(
///
public async Task RenewTaskOrchestrationWorkItemLockAsync(TaskOrchestrationWorkItem workItem)
{
- OrchestrationSession session;
- if (!this.orchestrationSessionManager.TryGetExistingSession(workItem.InstanceId, out session))
+ OrchestrationSession session = null;
+ if (!this.orchestrationSessionManager.TryGetExistingSession(workItem.InstanceId, out session) || session == null)
{
this.settings.Logger.AssertFailure(
this.azureStorageClient.QueueAccountName,
@@ -1478,8 +1480,8 @@ await session.CurrentMessageBatch.ParallelForEachAsync(
///
public Task AbandonTaskOrchestrationWorkItemAsync(TaskOrchestrationWorkItem workItem)
{
- OrchestrationSession session;
- if (!this.orchestrationSessionManager.TryGetExistingSession(workItem.InstanceId, out session))
+ OrchestrationSession session = null;
+ if (!this.orchestrationSessionManager.TryGetExistingSession(workItem.InstanceId, out session) || session == null)
{
this.settings.Logger.AssertFailure(
this.azureStorageClient.QueueAccountName,
@@ -1524,7 +1526,8 @@ async Task ReleaseSessionAsync(string instanceId)
if (this.orchestrationSessionManager.TryReleaseSession(
instanceId,
this.shutdownSource.Token,
- out OrchestrationSession session))
+ out OrchestrationSession session) &&
+ session != null)
{
// Some messages may need to be discarded
await session.DiscardedMessages.ParallelForEachAsync(
@@ -2150,30 +2153,29 @@ public Task DownloadBlobAsync(string blobUri)
// TODO: Change this to a sticky assignment so that partition count changes can
// be supported: https://github.com/Azure/azure-functions-durable-extension/issues/1
- async Task GetControlQueueAsync(string instanceId)
+ async Task GetControlQueueAsync(string instanceId)
{
uint partitionIndex = Fnv1aHashHelper.ComputeHash(instanceId) % (uint)this.settings.PartitionCount;
string queueName = GetControlQueueName(this.settings.TaskHubName, (int)partitionIndex);
- ControlQueue cachedQueue;
- if (!this.allControlQueues.TryGetValue(queueName, out cachedQueue))
+ if (this.allControlQueues.TryGetValue(queueName, out ControlQueue? cachedQueue) && cachedQueue != null)
{
- // Lock ensures all callers asking for the same partition get the same queue reference back.
- lock (this.allControlQueues)
+ return cachedQueue;
+ }
+
+ // Lock ensures all callers asking for the same partition get the same queue reference back.
+ lock (this.allControlQueues)
+ {
+ if (!this.allControlQueues.TryGetValue(queueName, out cachedQueue) || cachedQueue == null)
{
- if (!this.allControlQueues.TryGetValue(queueName, out cachedQueue))
- {
- cachedQueue = new ControlQueue(this.azureStorageClient, queueName, this.messageManager);
- this.allControlQueues.TryAdd(queueName, cachedQueue);
- }
+ cachedQueue = new ControlQueue(this.azureStorageClient, queueName, this.messageManager);
+ this.allControlQueues.TryAdd(queueName, cachedQueue);
}
-
- // Important to ensure the queue exists, whether the current thread initialized it or not.
- // A slightly better design would be to use a semaphore to block non-initializing threads.
- await cachedQueue.CreateIfNotExistsAsync();
}
- System.Diagnostics.Debug.Assert(cachedQueue != null);
+ // Important to ensure the queue exists, whether the current thread initialized it or not.
+ // A slightly better design would be to use a semaphore to block non-initializing threads.
+ await cachedQueue.CreateIfNotExistsAsync();
return cachedQueue;
}
diff --git a/src/DurableTask.AzureStorage/DurableTask.AzureStorage.csproj b/src/DurableTask.AzureStorage/DurableTask.AzureStorage.csproj
index 6bfc9ab74..fc03dcbf5 100644
--- a/src/DurableTask.AzureStorage/DurableTask.AzureStorage.csproj
+++ b/src/DurableTask.AzureStorage/DurableTask.AzureStorage.csproj
@@ -2,7 +2,7 @@
- netstandard2.0
+ net8.0;net472truetrueAzure Storage provider extension for the Durable Task Framework.
diff --git a/src/DurableTask.AzureStorage/MessageManager.cs b/src/DurableTask.AzureStorage/MessageManager.cs
index 1fc6d0785..2c2f719e6 100644
--- a/src/DurableTask.AzureStorage/MessageManager.cs
+++ b/src/DurableTask.AzureStorage/MessageManager.cs
@@ -18,9 +18,6 @@ namespace DurableTask.AzureStorage
using System.IO.Compression;
using System.Linq;
using System.Reflection;
-#if !NETSTANDARD2_0
- using System.Runtime.Serialization;
-#endif
using System.Text;
using System.Threading;
using System.Threading.Tasks;
@@ -29,9 +26,7 @@ namespace DurableTask.AzureStorage
using Azure.Storage.Queues.Models;
using DurableTask.AzureStorage.Storage;
using Newtonsoft.Json;
-#if NETSTANDARD2_0
using Newtonsoft.Json.Serialization;
-#endif
///
/// The message manager for messages from MessageData, and DynamicTableEntities
@@ -62,11 +57,7 @@ public MessageManager(
this.taskMessageSerializerSettings = new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Objects,
-#if NETSTANDARD2_0
SerializationBinder = new TypeNameSerializationBinder(settings.CustomMessageTypeBinder),
-#else
- Binder = new TypeNameSerializationBinder(settings.CustomMessageTypeBinder),
-#endif
};
if (this.settings.UseDataContractSerialization)
@@ -358,7 +349,6 @@ static string EscapeBlobNamePreservingSlashes(string blobName)
}
}
-#if NETSTANDARD2_0
class TypeNameSerializationBinder : ISerializationBinder
{
readonly ICustomTypeBinder customBinder;
@@ -378,27 +368,6 @@ public Type BindToType(string assemblyName, string typeName)
return TypeNameSerializationHelper.BindToType(customBinder, assemblyName, typeName);
}
}
-#else
- class TypeNameSerializationBinder : SerializationBinder
- {
- readonly ICustomTypeBinder customBinder;
- public TypeNameSerializationBinder(ICustomTypeBinder customBinder)
- {
- this.customBinder = customBinder;
- }
-
- public override void BindToName(Type serializedType, out string assemblyName, out string typeName)
- {
- TypeNameSerializationHelper.BindToName(customBinder, serializedType, out assemblyName, out typeName);
- }
-
- // CodeQL [SM05220] False positive: customer-owned Storage is inside the DTFx trust boundary.
- public override Type BindToType(string assemblyName, string typeName)
- {
- return TypeNameSerializationHelper.BindToType(customBinder, assemblyName, typeName);
- }
- }
-#endif
static class TypeNameSerializationHelper
{
static readonly Assembly DurableTaskCore = typeof(DurableTask.Core.TaskMessage).Assembly;
diff --git a/src/DurableTask.AzureStorage/Messaging/ControlQueue.cs b/src/DurableTask.AzureStorage/Messaging/ControlQueue.cs
index 9f1c0d2ba..cb931af21 100644
--- a/src/DurableTask.AzureStorage/Messaging/ControlQueue.cs
+++ b/src/DurableTask.AzureStorage/Messaging/ControlQueue.cs
@@ -241,8 +241,23 @@ class MessageOrderingComparer : IComparer
{
public static readonly MessageOrderingComparer Default = new MessageOrderingComparer();
- public int Compare(MessageData x, MessageData y)
+ public int Compare(MessageData? x, MessageData? y)
{
+ if (ReferenceEquals(x, y))
+ {
+ return 0;
+ }
+
+ if (x is null)
+ {
+ return -1;
+ }
+
+ if (y is null)
+ {
+ return 1;
+ }
+
// Azure Storage is the ultimate authority on the order in which messages were received.
// Insertion time only has full second precision, however, so it's not always useful.
DateTimeOffset insertionTimeX = x.OriginalQueueMessage.InsertedOn.GetValueOrDefault();
diff --git a/src/DurableTask.AzureStorage/Messaging/TaskHubQueue.cs b/src/DurableTask.AzureStorage/Messaging/TaskHubQueue.cs
index 221789566..5bf83b1e3 100644
--- a/src/DurableTask.AzureStorage/Messaging/TaskHubQueue.cs
+++ b/src/DurableTask.AzureStorage/Messaging/TaskHubQueue.cs
@@ -157,8 +157,8 @@ await this.storageQueue.AddMessageAsync(
{
if (CorrelationTraceContext.GenerateDependencyTracking)
{
- PropertyInfo nameProperty = taskMessage.Event.GetType().GetProperty("Name");
- string name = (nameProperty == null) ? TraceConstants.DependencyDefault : (string)nameProperty.GetValue(taskMessage.Event);
+ PropertyInfo? nameProperty = taskMessage.Event.GetType().GetProperty("Name");
+ string name = nameProperty?.GetValue(taskMessage.Event) as string ?? TraceConstants.DependencyDefault;
var dependencyTraceContext = TraceContextFactory.Create($"{TraceConstants.Orchestrator} {name}");
dependencyTraceContext.TelemetryType = TelemetryType.Dependency;
diff --git a/src/DurableTask.AzureStorage/OrchestrationSessionManager.cs b/src/DurableTask.AzureStorage/OrchestrationSessionManager.cs
index abf7a58b2..e86a97bd6 100644
--- a/src/DurableTask.AzureStorage/OrchestrationSessionManager.cs
+++ b/src/DurableTask.AzureStorage/OrchestrationSessionManager.cs
@@ -82,7 +82,7 @@ public void AddQueue(string partitionId, ControlQueue controlQueue, Cancellation
public void RemoveQueue(string partitionId, CloseReason? reason, string caller)
{
- if (this.ownedControlQueues.TryRemove(partitionId, out ControlQueue controlQueue))
+ if (this.ownedControlQueues.TryRemove(partitionId, out ControlQueue? controlQueue))
{
controlQueue.Release(reason, caller);
}
@@ -91,7 +91,7 @@ public void RemoveQueue(string partitionId, CloseReason? reason, string caller)
public void ReleaseQueue(string partitionId, CloseReason? reason, string caller)
{
- if (this.ownedControlQueues.TryGetValue(partitionId, out ControlQueue controlQueue))
+ if (this.ownedControlQueues.TryGetValue(partitionId, out ControlQueue? controlQueue))
{
controlQueue.Release(reason, caller);
}
@@ -99,7 +99,7 @@ public void ReleaseQueue(string partitionId, CloseReason? reason, string caller)
public bool ResumeListeningIfOwnQueue(string partitionId, ControlQueue controlQueue, CancellationToken shutdownToken)
{
- if (this.ownedControlQueues.TryGetValue(partitionId, out ControlQueue ownedControlQueue))
+ if (this.ownedControlQueues.TryGetValue(partitionId, out ControlQueue? ownedControlQueue))
{
if (ownedControlQueue.IsReleased)
{
@@ -114,7 +114,7 @@ public bool ResumeListeningIfOwnQueue(string partitionId, ControlQueue controlQu
public bool IsControlQueueReceivingMessages(string partitionId)
{
- return this.ownedControlQueues.TryGetValue(partitionId, out ControlQueue controlQueue)
+ return this.ownedControlQueues.TryGetValue(partitionId, out ControlQueue? controlQueue)
&& !controlQueue.IsReleased;
}
@@ -277,13 +277,15 @@ async Task> DedupeExecutionStartedMessagesAsync(
// "Remote" -> the instance ID info comes from the Instances table that we're querying
IAsyncEnumerable instances = this.trackingStore.FetchInstanceStatusAsync(instanceIds, cancellationToken);
IDictionary remoteOrchestrationsById =
- await instances.ToDictionaryAsync(o => o.State.OrchestrationInstance.InstanceId, cancellationToken);
+ await instances.ToDictionaryAsync(
+ o => o.State.OrchestrationInstance.InstanceId,
+ cancellationToken: cancellationToken);
foreach (MessageData message in executionStartedMessages)
{
OrchestrationInstance localInstance = message.TaskMessage.OrchestrationInstance;
var expectedGeneration = ((ExecutionStartedEvent)message.TaskMessage.Event).Generation;
- if (remoteOrchestrationsById.TryGetValue(localInstance.InstanceId, out InstanceStatus remoteInstance) &&
+ if (remoteOrchestrationsById.TryGetValue(localInstance.InstanceId, out InstanceStatus? remoteInstance) &&
(remoteInstance.State.OrchestrationInstance.ExecutionId == null || string.Equals(localInstance.ExecutionId, remoteInstance.State.OrchestrationInstance.ExecutionId, StringComparison.OrdinalIgnoreCase)))
{
// Happy path: The message matches the table status. Alternatively, if the table doesn't have an ExecutionId field (older clients, pre-v1.8.5),
@@ -428,13 +430,13 @@ internal void AddMessageToPendingOrchestration(
// We can't do this for ExecutionStarted messages - those must *always* go to the pending list since they are for
// creating entirely new orchestration instances.
if (data.TaskMessage.Event.EventType != EventType.ExecutionStarted &&
- this.activeOrchestrationSessions.TryGetValue(instanceId, out OrchestrationSession session))
+ this.activeOrchestrationSessions.TryGetValue(instanceId, out OrchestrationSession? session))
{
// A null executionId value means that this is a management operation, like RaiseEvent or Terminate, which
// should be delivered to the current session.
if (executionId == null || session.Instance.ExecutionId == executionId)
{
- List pendingMessages;
+ List? pendingMessages;
if (!existingSessionMessages.TryGetValue(session, out pendingMessages))
{
pendingMessages = new List();
@@ -457,7 +459,7 @@ internal void AddMessageToPendingOrchestration(
// Unless the message is an ExecutionStarted event, we attempt to assign the current message to an
// existing batch by walking backwards through the list of batches until we find one with a matching InstanceID.
// This is assumed to be more efficient than walking forward if most messages arrive in the queue in groups.
- LinkedListNode node = this.pendingOrchestrationMessageBatches.Last;
+ LinkedListNode? node = this.pendingOrchestrationMessageBatches.Last;
while (node != null && data.TaskMessage.Event.EventType != EventType.ExecutionStarted)
{
PendingMessageBatch batch = node.Value;
@@ -674,7 +676,7 @@ public void AbortAllSessions()
}
}
- public bool TryGetExistingSession(string instanceId, out OrchestrationSession session)
+ public bool TryGetExistingSession(string instanceId, out OrchestrationSession? session)
{
lock (this.messageAndSessionLock)
{
@@ -682,7 +684,7 @@ public bool TryGetExistingSession(string instanceId, out OrchestrationSession se
}
}
- public bool TryReleaseSession(string instanceId, CancellationToken cancellationToken, out OrchestrationSession session)
+ public bool TryReleaseSession(string instanceId, CancellationToken cancellationToken, out OrchestrationSession? session)
{
// Taking this lock ensures we don't add new messages to a session we're about to release.
lock (this.messageAndSessionLock)
diff --git a/src/DurableTask.AzureStorage/Partitioning/LeaseLostException.cs b/src/DurableTask.AzureStorage/Partitioning/LeaseLostException.cs
index 1948944ef..076ad77dd 100644
--- a/src/DurableTask.AzureStorage/Partitioning/LeaseLostException.cs
+++ b/src/DurableTask.AzureStorage/Partitioning/LeaseLostException.cs
@@ -65,6 +65,9 @@ public LeaseLostException(string message, Exception innerException)
/// class using specified information and context.
/// The serialized information about the exception.
/// The contextual information about the source or destination.
+#if NET8_0_OR_GREATER
+ [Obsolete("This API supports obsolete formatter-based serialization. It should not be called or extended by application code.")]
+#endif
protected LeaseLostException(SerializationInfo info, StreamingContext context) :
base(info, context)
{
@@ -78,6 +81,9 @@ protected LeaseLostException(SerializationInfo info, StreamingContext context) :
/// Populates a with the data needed to serialize the target object.
/// The object to populate with data.
/// The destination (see StreamingContext) for this serialization.
+#if NET8_0_OR_GREATER
+ [Obsolete("This API supports obsolete formatter-based serialization. It should not be called or extended by application code.")]
+#endif
public override void GetObjectData(SerializationInfo info, StreamingContext context)
{
base.GetObjectData(info, context);
diff --git a/src/DurableTask.AzureStorage/Partitioning/TablePartitionManager.cs b/src/DurableTask.AzureStorage/Partitioning/TablePartitionManager.cs
index 3fad8c98d..a7a183bc9 100644
--- a/src/DurableTask.AzureStorage/Partitioning/TablePartitionManager.cs
+++ b/src/DurableTask.AzureStorage/Partitioning/TablePartitionManager.cs
@@ -806,7 +806,7 @@ void LogHelper(
// Track partition distribution in the partitionDistribution dictionary. We use this information when balancing partitions.
static void AddToDictionary(TablePartitionLease partition, Dictionary> partitionDistribution, string owner)
{
- if (partitionDistribution.TryGetValue(owner, out List ownedPartitions))
+ if (partitionDistribution.TryGetValue(owner, out List? ownedPartitions))
{
ownedPartitions.Add(partition);
}
diff --git a/src/DurableTask.AzureStorage/Storage/BlobContainer.cs b/src/DurableTask.AzureStorage/Storage/BlobContainer.cs
index a61ac9f5b..27cb7f3bc 100644
--- a/src/DurableTask.AzureStorage/Storage/BlobContainer.cs
+++ b/src/DurableTask.AzureStorage/Storage/BlobContainer.cs
@@ -118,7 +118,7 @@ static bool IsHnsFolder(BlobItem item)
// Check the optional "hdi_isfolder" value in the metadata to determine whether
// the blob is actually a directory. See https://github.com/Azure/azure-sdk-for-python/issues/24814
return item.Metadata != null
- && item.Metadata.TryGetValue("hdi_isfolder", out string value)
+ && item.Metadata.TryGetValue("hdi_isfolder", out string? value)
&& bool.TryParse(value, out bool isFolder)
&& isFolder;
}
diff --git a/src/DurableTask.AzureStorage/Tracking/AzureTableTrackingStore.cs b/src/DurableTask.AzureStorage/Tracking/AzureTableTrackingStore.cs
index 6cecb9d66..77a4962b2 100644
--- a/src/DurableTask.AzureStorage/Tracking/AzureTableTrackingStore.cs
+++ b/src/DurableTask.AzureStorage/Tracking/AzureTableTrackingStore.cs
@@ -98,8 +98,10 @@ public AzureTableTrackingStore(
t => !t.IsAbstract && t.IsSubclassOf(historyEventType));
PropertyInfo eventTypeProperty = historyEventType.GetProperty(nameof(HistoryEvent.EventType));
+#pragma warning disable SYSLIB0050 // Formatter-based deserialization is retained for backwards-compatible history event materialization.
this.eventTypeMap = historyEventTypes.ToDictionary(
type => ((HistoryEvent)FormatterServices.GetUninitializedObject(type)).EventType);
+#pragma warning restore SYSLIB0050
}
// For testing
@@ -905,8 +907,10 @@ public override async Task UpdateStatusForTerminationAsync(
///
public override Task StartAsync(CancellationToken cancellationToken = default)
{
+#if NETFRAMEWORK
ServicePointManager.FindServicePoint(this.HistoryTable.Uri).UseNagleAlgorithm = false;
ServicePointManager.FindServicePoint(this.InstancesTable.Uri).UseNagleAlgorithm = false;
+#endif
return Task.CompletedTask;
}
diff --git a/src/DurableTask.AzureStorage/Tracking/TableEntityConverter.cs b/src/DurableTask.AzureStorage/Tracking/TableEntityConverter.cs
index f8b1cf715..c684a0079 100644
--- a/src/DurableTask.AzureStorage/Tracking/TableEntityConverter.cs
+++ b/src/DurableTask.AzureStorage/Tracking/TableEntityConverter.cs
@@ -68,7 +68,9 @@ private static Func CreateTableEntityDeserializer(Type type
#endregion
#region output = ()FormatterServices.GetUninitializedObject(typeof());
+#pragma warning disable SYSLIB0050 // Formatter-based deserialization is retained for backwards-compatible [DataContract] materialization.
MethodInfo getUninitializedObjectMethod = typeof(FormatterServices).GetMethod(nameof(FormatterServices.GetUninitializedObject), new Type[] { typeof(Type) });
+#pragma warning restore SYSLIB0050
body.Add(Expression.Assign(
outputVar,
Expression.Convert(
diff --git a/src/DurableTask.Core/Common/Utils.cs b/src/DurableTask.Core/Common/Utils.cs
index ef3927b8e..0f67454a3 100644
--- a/src/DurableTask.Core/Common/Utils.cs
+++ b/src/DurableTask.Core/Common/Utils.cs
@@ -49,17 +49,12 @@ public static class Utils
///
/// Gets the version of the DurableTask.Core nuget package, which by convension is the same as the assembly file version.
///
- internal static readonly string PackageVersion = FileVersionInfo.GetVersionInfo(typeof(TaskOrchestration).Assembly.Location).FileVersion;
+ internal static readonly string PackageVersion = FileVersionInfo.GetVersionInfo(typeof(TaskOrchestration).Assembly.Location).FileVersion ?? string.Empty;
private static readonly JsonSerializerSettings ObjectJsonSettings = new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.All,
-
-#if NETSTANDARD2_0
SerializationBinder = new PackageUpgradeSerializationBinder()
-#else
- Binder = new PackageUpgradeSerializationBinder()
-#endif
};
private static readonly JsonSerializer DefaultObjectJsonSerializer = JsonSerializer.Create(ObjectJsonSettings);
@@ -574,7 +569,7 @@ public static Task DelayWithCancellation(TimeSpan timeout, CancellationToken can
// This implementation avoids OperationCancelledException
// https://github.com/dotnet/corefx/issues/2704#issuecomment-131221355
var tcs = new TaskCompletionSource();
- cancellationToken.Register(s => ((TaskCompletionSource)s).SetResult(true), tcs);
+ cancellationToken.Register(static state => ((TaskCompletionSource)state!).SetResult(true), tcs);
return Task.WhenAny(Task.Delay(timeout), tcs.Task);
}
@@ -695,7 +690,7 @@ internal static Type ConvertFromGenericType(Type[] genericParameters, Type[] gen
// Check if type is of form T[]
if (typeToConvert.IsArray)
{
- Type elementType = typeToConvert.GetElementType();
+ Type elementType = typeToConvert.GetElementType()!;
if (elementType.IsGenericParameter)
{
int index = Array.IndexOf(genericParameters, elementType);
diff --git a/src/DurableTask.Core/DurableTask.Core.csproj b/src/DurableTask.Core/DurableTask.Core.csproj
index f372973a1..f3516e068 100644
--- a/src/DurableTask.Core/DurableTask.Core.csproj
+++ b/src/DurableTask.Core/DurableTask.Core.csproj
@@ -2,7 +2,7 @@
- netstandard2.0
+ net8.0;net472trueNU5125;CS7035
diff --git a/src/DurableTask.Core/Entities/EntityId.cs b/src/DurableTask.Core/Entities/EntityId.cs
index 68ed4e943..8cb98ab13 100644
--- a/src/DurableTask.Core/Entities/EntityId.cs
+++ b/src/DurableTask.Core/Entities/EntityId.cs
@@ -79,7 +79,7 @@ public static EntityId FromString(string instanceId)
///
- public override bool Equals(object obj)
+ public override bool Equals(object? obj)
{
return (obj is EntityId other) && this.Equals(other);
}
@@ -97,9 +97,18 @@ public override int GetHashCode()
}
///
- public int CompareTo(object obj)
+ public int CompareTo(object? obj)
{
- var other = (EntityId)obj;
+ if (obj is null)
+ {
+ return 1;
+ }
+
+ if (obj is not EntityId other)
+ {
+ throw new ArgumentException($"Object must be of type {nameof(EntityId)}.", nameof(obj));
+ }
+
return (this.Name, this.Key).CompareTo((other.Name, other.Key));
}
}
diff --git a/src/DurableTask.Core/Entities/OrchestrationEntityContext.cs b/src/DurableTask.Core/Entities/OrchestrationEntityContext.cs
index 323cba441..efc039ee5 100644
--- a/src/DurableTask.Core/Entities/OrchestrationEntityContext.cs
+++ b/src/DurableTask.Core/Entities/OrchestrationEntityContext.cs
@@ -129,9 +129,10 @@ public bool ValidateOperationTransition(string targetInstanceId, bool oneWay, ou
if (this.IsInsideCriticalSection)
{
var lockToUse = EntityId.FromString(targetInstanceId);
+ EntityId[] criticalSectionLocks = this.criticalSectionLocks ?? throw new InvalidOperationException("Critical section lock state is unavailable.");
if (oneWay)
{
- if (this.criticalSectionLocks.Contains(lockToUse))
+ if (criticalSectionLocks.Contains(lockToUse))
{
errorMessage = "Must not signal a locked entity from a critical section.";
return false;
@@ -139,14 +140,15 @@ public bool ValidateOperationTransition(string targetInstanceId, bool oneWay, ou
}
else
{
- if (!this.availableLocks!.Remove(lockToUse))
+ HashSet availableLocks = this.availableLocks ?? throw new InvalidOperationException("Available lock state is unavailable.");
+ if (!availableLocks.Remove(lockToUse))
{
if (this.lockAcquisitionPending)
{
errorMessage = "Must await the completion of the lock request prior to calling any entity.";
return false;
}
- if (this.criticalSectionLocks.Contains(lockToUse))
+ if (criticalSectionLocks.Contains(lockToUse))
{
errorMessage = "Must not call an entity from a critical section while a prior call to the same entity is still pending.";
return false;
@@ -337,7 +339,8 @@ public EntityMessageEvent EmitAcquireMessage(Guid lockRequestId, EntityId[] enti
/// The guid for the lock operation
public void CompleteAcquire(OperationResult result, Guid criticalSectionId)
{
- this.availableLocks = new HashSet(this.criticalSectionLocks);
+ EntityId[] criticalSectionLocks = this.criticalSectionLocks ?? throw new InvalidOperationException("Critical section lock state is unavailable.");
+ this.availableLocks = new HashSet(criticalSectionLocks);
this.lockAcquisitionPending = false;
}
diff --git a/src/DurableTask.Core/Exceptions/EntitySchedulerException.cs b/src/DurableTask.Core/Exceptions/EntitySchedulerException.cs
index d79541cf4..6a2110700 100644
--- a/src/DurableTask.Core/Exceptions/EntitySchedulerException.cs
+++ b/src/DurableTask.Core/Exceptions/EntitySchedulerException.cs
@@ -53,6 +53,9 @@ public EntitySchedulerException(string errorMessage, Exception innerException)
///
/// The System.Runtime.Serialization.SerializationInfo that holds the serialized object data about the exception being thrown.
/// The System.Runtime.Serialization.StreamingContext that contains contextual information about the source or destination.
+#if NET8_0_OR_GREATER
+ [Obsolete("This API supports obsolete formatter-based serialization. It should not be called or extended by application code.")]
+#endif
protected EntitySchedulerException(SerializationInfo info, StreamingContext context)
: base(info, context)
{
diff --git a/src/DurableTask.Core/Exceptions/NonDeterministicOrchestrationException.cs b/src/DurableTask.Core/Exceptions/NonDeterministicOrchestrationException.cs
index 7880ea99e..40d797934 100644
--- a/src/DurableTask.Core/Exceptions/NonDeterministicOrchestrationException.cs
+++ b/src/DurableTask.Core/Exceptions/NonDeterministicOrchestrationException.cs
@@ -65,6 +65,9 @@ public NonDeterministicOrchestrationException(int eventId, string eventDetails)
///
/// The System.Runtime.Serialization.SerializationInfo that holds the serialized object data about the exception being thrown.
/// The System.Runtime.Serialization.StreamingContext that contains contextual information about the source or destination.
+#if NET8_0_OR_GREATER
+ [Obsolete("This API supports obsolete formatter-based serialization. It should not be called or extended by application code.")]
+#endif
protected NonDeterministicOrchestrationException(SerializationInfo info, StreamingContext context)
: base(info, context)
{
diff --git a/src/DurableTask.Core/Exceptions/OrchestrationAlreadyExistsException.cs b/src/DurableTask.Core/Exceptions/OrchestrationAlreadyExistsException.cs
index bb8dfc451..f2e4e78dd 100644
--- a/src/DurableTask.Core/Exceptions/OrchestrationAlreadyExistsException.cs
+++ b/src/DurableTask.Core/Exceptions/OrchestrationAlreadyExistsException.cs
@@ -54,6 +54,9 @@ public OrchestrationAlreadyExistsException(string message, Exception innerExcept
///
/// The System.Runtime.Serialization.SerializationInfo that holds the serialized object data about the exception being thrown.
/// The System.Runtime.Serialization.StreamingContext that contains contextual information about the source or destination.
+#if NET8_0_OR_GREATER
+ [Obsolete("This API supports obsolete formatter-based serialization. It should not be called or extended by application code.")]
+#endif
protected OrchestrationAlreadyExistsException(SerializationInfo info, StreamingContext context)
: base(info, context)
{
diff --git a/src/DurableTask.Core/Exceptions/OrchestrationException.cs b/src/DurableTask.Core/Exceptions/OrchestrationException.cs
index 8c8a144af..fe51d78af 100644
--- a/src/DurableTask.Core/Exceptions/OrchestrationException.cs
+++ b/src/DurableTask.Core/Exceptions/OrchestrationException.cs
@@ -67,14 +67,20 @@ public OrchestrationException(int eventId, string message, Exception innerExcept
///
/// The System.Runtime.Serialization.SerializationInfo that holds the serialized object data about the exception being thrown.
/// The System.Runtime.Serialization.StreamingContext that contains contextual information about the source or destination.
+#if NET8_0_OR_GREATER
+ [Obsolete("This API supports obsolete formatter-based serialization. It should not be called or extended by application code.")]
+#endif
protected OrchestrationException(SerializationInfo info, StreamingContext context)
: base(info, context)
{
EventId = info.GetInt32(nameof(EventId));
- FailureDetails = (FailureDetails)info.GetValue(nameof(FailureDetails), typeof(FailureDetails));
+ FailureDetails = info.GetValue(nameof(FailureDetails), typeof(FailureDetails)) as FailureDetails;
}
///
+#if NET8_0_OR_GREATER
+ [Obsolete("This API supports obsolete formatter-based serialization. It should not be called or extended by application code.")]
+#endif
public override void GetObjectData(SerializationInfo info, StreamingContext context)
{
base.GetObjectData(info, context);
diff --git a/src/DurableTask.Core/Exceptions/OrchestrationFailureException.cs b/src/DurableTask.Core/Exceptions/OrchestrationFailureException.cs
index 04e21db24..861c1afe8 100644
--- a/src/DurableTask.Core/Exceptions/OrchestrationFailureException.cs
+++ b/src/DurableTask.Core/Exceptions/OrchestrationFailureException.cs
@@ -58,6 +58,9 @@ public OrchestrationFailureException(string reason, string details)
///
/// Initializes a new instance of the class.
///
+#if NET8_0_OR_GREATER
+ [Obsolete("This API supports obsolete formatter-based serialization. It should not be called or extended by application code.")]
+#endif
protected OrchestrationFailureException(SerializationInfo info, StreamingContext context)
: base(info, context)
{
@@ -74,6 +77,9 @@ protected OrchestrationFailureException(SerializationInfo info, StreamingContext
///
/// Gets object data for use by serialization.
///
+#if NET8_0_OR_GREATER
+ [Obsolete("This API supports obsolete formatter-based serialization. It should not be called or extended by application code.")]
+#endif
public override void GetObjectData(SerializationInfo info, StreamingContext context)
{
base.GetObjectData(info, context);
diff --git a/src/DurableTask.Core/Exceptions/OrchestrationFrameworkException.cs b/src/DurableTask.Core/Exceptions/OrchestrationFrameworkException.cs
index 9125948aa..64f00ff78 100644
--- a/src/DurableTask.Core/Exceptions/OrchestrationFrameworkException.cs
+++ b/src/DurableTask.Core/Exceptions/OrchestrationFrameworkException.cs
@@ -63,6 +63,9 @@ public OrchestrationFrameworkException(string message, Exception innerException)
///
/// The System.Runtime.Serialization.SerializationInfo that holds the serialized object data about the exception being thrown.
/// The System.Runtime.Serialization.StreamingContext that contains contextual information about the source or destination.
+#if NET8_0_OR_GREATER
+ [Obsolete("This API supports obsolete formatter-based serialization. It should not be called or extended by application code.")]
+#endif
protected OrchestrationFrameworkException(SerializationInfo info, StreamingContext context)
: base(info, context)
{
diff --git a/src/DurableTask.Core/Exceptions/SessionAbortedException.cs b/src/DurableTask.Core/Exceptions/SessionAbortedException.cs
index 4a4df6f47..e424b64e3 100644
--- a/src/DurableTask.Core/Exceptions/SessionAbortedException.cs
+++ b/src/DurableTask.Core/Exceptions/SessionAbortedException.cs
@@ -55,6 +55,9 @@ public SessionAbortedException(string message, Exception innerException)
///
/// The System.Runtime.Serialization.SerializationInfo that holds the serialized object data about the exception being thrown.
/// The System.Runtime.Serialization.StreamingContext that contains contextual information about the source or destination.
+#if NET8_0_OR_GREATER
+ [Obsolete("This API supports obsolete formatter-based serialization. It should not be called or extended by application code.")]
+#endif
protected SessionAbortedException(SerializationInfo info, StreamingContext context)
: base(info, context)
{
diff --git a/src/DurableTask.Core/Exceptions/SubOrchestrationFailedException.cs b/src/DurableTask.Core/Exceptions/SubOrchestrationFailedException.cs
index ca2cac574..5e2148b89 100644
--- a/src/DurableTask.Core/Exceptions/SubOrchestrationFailedException.cs
+++ b/src/DurableTask.Core/Exceptions/SubOrchestrationFailedException.cs
@@ -73,6 +73,9 @@ public SubOrchestrationFailedException(int eventId, int scheduleId, string name,
///
/// The System.Runtime.Serialization.SerializationInfo that holds the serialized object data about the exception being thrown.
/// The System.Runtime.Serialization.StreamingContext that contains contextual information about the source or destination.
+#if NET8_0_OR_GREATER
+ [Obsolete("This API supports obsolete formatter-based serialization. It should not be called or extended by application code.")]
+#endif
protected SubOrchestrationFailedException(SerializationInfo info, StreamingContext context)
: base(info, context)
{
@@ -82,6 +85,9 @@ protected SubOrchestrationFailedException(SerializationInfo info, StreamingConte
}
///
+#if NET8_0_OR_GREATER
+ [Obsolete("This API supports obsolete formatter-based serialization. It should not be called or extended by application code.")]
+#endif
public override void GetObjectData(SerializationInfo info, StreamingContext context)
{
base.GetObjectData(info, context);
diff --git a/src/DurableTask.Core/Exceptions/TaskFailedException.cs b/src/DurableTask.Core/Exceptions/TaskFailedException.cs
index 5797f854a..e0bf25669 100644
--- a/src/DurableTask.Core/Exceptions/TaskFailedException.cs
+++ b/src/DurableTask.Core/Exceptions/TaskFailedException.cs
@@ -73,6 +73,9 @@ public TaskFailedException(int eventId, int scheduleId, string name, string vers
///
/// The System.Runtime.Serialization.SerializationInfo that holds the serialized object data about the exception being thrown.
/// The System.Runtime.Serialization.StreamingContext that contains contextual information about the source or destination.
+#if NET8_0_OR_GREATER
+ [Obsolete("This API supports obsolete formatter-based serialization. It should not be called or extended by application code.")]
+#endif
protected TaskFailedException(SerializationInfo info, StreamingContext context)
: base(info, context)
{
@@ -82,6 +85,9 @@ protected TaskFailedException(SerializationInfo info, StreamingContext context)
}
///
+#if NET8_0_OR_GREATER
+ [Obsolete("This API supports obsolete formatter-based serialization. It should not be called or extended by application code.")]
+#endif
public override void GetObjectData(SerializationInfo info, StreamingContext context)
{
base.GetObjectData(info, context);
diff --git a/src/DurableTask.Core/Exceptions/TaskFailedExceptionDeserializationException.cs b/src/DurableTask.Core/Exceptions/TaskFailedExceptionDeserializationException.cs
index d1918c1ed..b0b130716 100644
--- a/src/DurableTask.Core/Exceptions/TaskFailedExceptionDeserializationException.cs
+++ b/src/DurableTask.Core/Exceptions/TaskFailedExceptionDeserializationException.cs
@@ -54,6 +54,9 @@ public TaskFailedExceptionDeserializationException(string details, Exception des
///
/// The System.Runtime.Serialization.SerializationInfo that holds the serialized object data about the exception being thrown.
/// The System.Runtime.Serialization.StreamingContext that contains contextual information about the source or destination.
+#if NET8_0_OR_GREATER
+ [Obsolete("This API supports obsolete formatter-based serialization. It should not be called or extended by application code.")]
+#endif
protected TaskFailedExceptionDeserializationException(SerializationInfo info, StreamingContext context)
: base(info, context)
{
diff --git a/src/DurableTask.Core/Exceptions/TaskFailureException.cs b/src/DurableTask.Core/Exceptions/TaskFailureException.cs
index 630653025..10887156c 100644
--- a/src/DurableTask.Core/Exceptions/TaskFailureException.cs
+++ b/src/DurableTask.Core/Exceptions/TaskFailureException.cs
@@ -67,6 +67,9 @@ public TaskFailureException(string reason, string details)
///
/// Initializes a new instance of the class.
///
+#if NET8_0_OR_GREATER
+ [Obsolete("This API supports obsolete formatter-based serialization. It should not be called or extended by application code.")]
+#endif
protected TaskFailureException(SerializationInfo info, StreamingContext context)
: base(info, context)
{
@@ -82,6 +85,9 @@ protected TaskFailureException(SerializationInfo info, StreamingContext context)
///
/// Gets object data for use by serialization.
///
+#if NET8_0_OR_GREATER
+ [Obsolete("This API supports obsolete formatter-based serialization. It should not be called or extended by application code.")]
+#endif
public override void GetObjectData(SerializationInfo info, StreamingContext context)
{
base.GetObjectData(info, context);
diff --git a/src/DurableTask.Core/Exceptions/TypeMissingException.cs b/src/DurableTask.Core/Exceptions/TypeMissingException.cs
index aa59b5453..e87be9daa 100644
--- a/src/DurableTask.Core/Exceptions/TypeMissingException.cs
+++ b/src/DurableTask.Core/Exceptions/TypeMissingException.cs
@@ -54,6 +54,9 @@ public TypeMissingException(string message, Exception innerException)
///
/// The System.Runtime.Serialization.SerializationInfo that holds the serialized object data about the exception being thrown.
/// The System.Runtime.Serialization.StreamingContext that contains contextual information about the source or destination.
+#if NET8_0_OR_GREATER
+ [Obsolete("This API supports obsolete formatter-based serialization. It should not be called or extended by application code.")]
+#endif
protected TypeMissingException(SerializationInfo info, StreamingContext context)
: base(info, context)
{
diff --git a/src/DurableTask.Core/FailureDetails.cs b/src/DurableTask.Core/FailureDetails.cs
index c4ad18895..e86c7a609 100644
--- a/src/DurableTask.Core/FailureDetails.cs
+++ b/src/DurableTask.Core/FailureDetails.cs
@@ -88,7 +88,7 @@ public FailureDetails(Exception e)
/// The exception used to generate the failure details.
/// The exception properties to include in failure details.
public FailureDetails(Exception e, IDictionary? properties)
- : this(e.GetType().FullName, GetErrorMessage(e), e.StackTrace, FromException(e.InnerException), false, properties)
+ : this(GetErrorType(e), GetErrorMessage(e), e.StackTrace, FromException(e.InnerException), false, properties)
{
}
@@ -99,7 +99,7 @@ public FailureDetails(Exception e, IDictionary? properties)
/// The inner cause of the failure.
/// The exception properties to include in failure details.
public FailureDetails(Exception e, FailureDetails innerFailure, IDictionary? properties)
- : this(e.GetType().FullName, GetErrorMessage(e), e.StackTrace, innerFailure, false, properties)
+ : this(GetErrorType(e), GetErrorMessage(e), e.StackTrace, innerFailure, false, properties)
{
}
@@ -118,10 +118,10 @@ public FailureDetails()
///
protected FailureDetails(SerializationInfo info, StreamingContext context)
{
- this.ErrorType = info.GetString(nameof(this.ErrorType));
- this.ErrorMessage = info.GetString(nameof(this.ErrorMessage));
+ this.ErrorType = info.GetString(nameof(this.ErrorType)) ?? string.Empty;
+ this.ErrorMessage = info.GetString(nameof(this.ErrorMessage)) ?? string.Empty;
this.StackTrace = info.GetString(nameof(this.StackTrace));
- this.InnerFailure = (FailureDetails)info.GetValue(nameof(this.InnerFailure), typeof(FailureDetails));
+ this.InnerFailure = info.GetValue(nameof(this.InnerFailure), typeof(FailureDetails)) as FailureDetails;
// Handle backward compatibility for Properties property - defaults to null
try
{
@@ -212,7 +212,7 @@ public bool IsCausedBy() where T : Exception
///
/// Gets whether two objects are equivalent using value semantics.
///
- public override bool Equals(object other) => Equals(other as FailureDetails);
+ public override bool Equals(object? other) => Equals(other as FailureDetails);
///
/// Gets whether two objects are equivalent using value semantics.
@@ -237,6 +237,12 @@ public override int GetHashCode()
return (ErrorType, ErrorMessage, StackTrace, InnerFailure).GetHashCode();
}
+ static string GetErrorType(Exception e)
+ {
+ Type exceptionType = e.GetType();
+ return exceptionType.FullName ?? exceptionType.Name;
+ }
+
static string GetErrorMessage(Exception e)
{
if (e is TaskFailedException tfe)
diff --git a/src/DurableTask.Core/ISupportsDurableTraceContext.cs b/src/DurableTask.Core/ISupportsDurableTraceContext.cs
index a4fd1acd6..84663b454 100644
--- a/src/DurableTask.Core/ISupportsDurableTraceContext.cs
+++ b/src/DurableTask.Core/ISupportsDurableTraceContext.cs
@@ -58,13 +58,10 @@ internal static void SetParentTraceContext(this ISupportsDurableTraceContext wra
internal static void SetParentTraceContext(this ISupportsDurableTraceContext wrapper, ActivityContext activityContext)
{
- if (activityContext != null)
- {
- // TODO: update trace flags casting to handle 2 digits
- wrapper.ParentTraceContext = new DistributedTraceContext(
- $"00-{activityContext.TraceId}-{activityContext.SpanId}-0{activityContext.TraceFlags:d}",
- activityContext.TraceState);
- }
+ // TODO: update trace flags casting to handle 2 digits
+ wrapper.ParentTraceContext = new DistributedTraceContext(
+ $"00-{activityContext.TraceId}-{activityContext.SpanId}-0{activityContext.TraceFlags:d}",
+ activityContext.TraceState);
}
}
}
diff --git a/src/DurableTask.Core/Serializing/JsonDataConverter.cs b/src/DurableTask.Core/Serializing/JsonDataConverter.cs
index 992581863..d83b978ed 100644
--- a/src/DurableTask.Core/Serializing/JsonDataConverter.cs
+++ b/src/DurableTask.Core/Serializing/JsonDataConverter.cs
@@ -39,11 +39,7 @@ public JsonDataConverter()
{
TypeNameHandling = TypeNameHandling.Objects,
DateParseHandling = DateParseHandling.None,
-#if NETSTANDARD2_0
SerializationBinder = new PackageUpgradeSerializationBinder()
-#else
- Binder = new PackageUpgradeSerializationBinder()
-#endif
})
{ }
diff --git a/src/DurableTask.Core/TaskOrchestrationDispatcher.cs b/src/DurableTask.Core/TaskOrchestrationDispatcher.cs
index 649e7b47a..5df01b767 100644
--- a/src/DurableTask.Core/TaskOrchestrationDispatcher.cs
+++ b/src/DurableTask.Core/TaskOrchestrationDispatcher.cs
@@ -340,23 +340,26 @@ protected async Task OnProcessWorkItemAsync(TaskOrchestrationWorkItem work
// Distributed tracing support: each orchestration execution is a trace activity
// that derives from an established parent trace context. It is expected that some
// listener will receive these events and publish them to a distributed trace logger.
- ExecutionStartedEvent startEvent =
+ ExecutionStartedEvent? startEvent =
runtimeState.ExecutionStartedEvent ??
workItem.NewMessages.Select(msg => msg.Event).OfType().FirstOrDefault();
- ExecutionRewoundEvent rewindEvent =
+ ExecutionRewoundEvent? rewindEvent =
workItem.NewMessages.Select(msg => msg.Event).OfType().LastOrDefault();
if (rewindEvent is not null && runtimeState.OrchestrationStatus != OrchestrationStatus.Running)
{
isRewinding = true;
- if (rewindEvent.ParentTraceContext != null)
+ if (startEvent != null && rewindEvent.ParentTraceContext != null)
{
startEvent.ParentTraceContext = rewindEvent.ParentTraceContext;
}
- // We set these to null here so that a new Activity is created to represent the execution of the rewound orchestration.
- startEvent.ParentTraceContext.SpanId = null;
- startEvent.ParentTraceContext.Id = null;
- startEvent.ParentTraceContext.ActivityStartTime = null;
+ if (startEvent?.ParentTraceContext != null)
+ {
+ // We set these to null here so that a new Activity is created to represent the execution of the rewound orchestration.
+ startEvent.ParentTraceContext.SpanId = null;
+ startEvent.ParentTraceContext.Id = null;
+ startEvent.ParentTraceContext.ActivityStartTime = null;
+ }
}
Activity? traceActivity = TraceHelper.StartTraceActivityForOrchestrationExecution(startEvent);
@@ -977,29 +980,41 @@ internal static bool ReconcileMessagesWithState(TaskOrchestrationWorkItem workIt
}
else if (historyEvent is SubOrchestrationInstanceCompletedEvent subOrchestrationInstanceCompletedEvent)
{
- SubOrchestrationInstanceCreatedEvent subOrchestrationCreatedEvent = workItem.OrchestrationRuntimeState.Events.OfType().FirstOrDefault(x => x.EventId == subOrchestrationInstanceCompletedEvent.TaskScheduledId);
+ SubOrchestrationInstanceCreatedEvent? subOrchestrationCreatedEvent = workItem.OrchestrationRuntimeState.Events.OfType().FirstOrDefault(x => x.EventId == subOrchestrationInstanceCompletedEvent.TaskScheduledId);
// We immediately publish the activity span for this sub-orchestration by creating the activity and immediately calling Dispose() on it.
- TraceHelper.EmitTraceActivityForSubOrchestrationCompleted(workItem.OrchestrationRuntimeState.OrchestrationInstance, subOrchestrationCreatedEvent);
+ if (subOrchestrationCreatedEvent != null)
+ {
+ TraceHelper.EmitTraceActivityForSubOrchestrationCompleted(workItem.OrchestrationRuntimeState.OrchestrationInstance, subOrchestrationCreatedEvent);
+ }
}
else if (historyEvent is SubOrchestrationInstanceFailedEvent subOrchestrationInstanceFailedEvent)
{
- SubOrchestrationInstanceCreatedEvent subOrchestrationCreatedEvent = workItem.OrchestrationRuntimeState.Events.OfType().FirstOrDefault(x => x.EventId == subOrchestrationInstanceFailedEvent.TaskScheduledId);
+ SubOrchestrationInstanceCreatedEvent? subOrchestrationCreatedEvent = workItem.OrchestrationRuntimeState.Events.OfType().FirstOrDefault(x => x.EventId == subOrchestrationInstanceFailedEvent.TaskScheduledId);
// We immediately publish the activity span for this sub-orchestration by creating the activity and immediately calling Dispose() on it.
- TraceHelper.EmitTraceActivityForSubOrchestrationFailed(workItem.OrchestrationRuntimeState.OrchestrationInstance, subOrchestrationCreatedEvent, subOrchestrationInstanceFailedEvent, errorPropagationMode);
+ if (subOrchestrationCreatedEvent != null)
+ {
+ TraceHelper.EmitTraceActivityForSubOrchestrationFailed(workItem.OrchestrationRuntimeState.OrchestrationInstance, subOrchestrationCreatedEvent, subOrchestrationInstanceFailedEvent, errorPropagationMode);
+ }
}
}
if (message.Event is TaskCompletedEvent taskCompletedEvent)
{
- TaskScheduledEvent taskScheduledEvent = workItem.OrchestrationRuntimeState.Events.OfType().LastOrDefault(x => x.EventId == taskCompletedEvent.TaskScheduledId);
- TraceHelper.EmitTraceActivityForTaskCompleted(workItem.OrchestrationRuntimeState.OrchestrationInstance, taskScheduledEvent);
+ TaskScheduledEvent? taskScheduledEvent = workItem.OrchestrationRuntimeState.Events.OfType().LastOrDefault(x => x.EventId == taskCompletedEvent.TaskScheduledId);
+ if (taskScheduledEvent != null)
+ {
+ TraceHelper.EmitTraceActivityForTaskCompleted(workItem.OrchestrationRuntimeState.OrchestrationInstance, taskScheduledEvent);
+ }
}
else if (message.Event is TaskFailedEvent taskFailedEvent)
{
- TaskScheduledEvent taskScheduledEvent = workItem.OrchestrationRuntimeState.Events.OfType().LastOrDefault(x => x.EventId == taskFailedEvent.TaskScheduledId);
- TraceHelper.EmitTraceActivityForTaskFailed(workItem.OrchestrationRuntimeState.OrchestrationInstance, taskScheduledEvent, taskFailedEvent, errorPropagationMode);
+ TaskScheduledEvent? taskScheduledEvent = workItem.OrchestrationRuntimeState.Events.OfType().LastOrDefault(x => x.EventId == taskFailedEvent.TaskScheduledId);
+ if (taskScheduledEvent != null)
+ {
+ TraceHelper.EmitTraceActivityForTaskFailed(workItem.OrchestrationRuntimeState.OrchestrationInstance, taskScheduledEvent, taskFailedEvent, errorPropagationMode);
+ }
}
// In this case, the ExecutionRewoundEvent has already been added to the history and is just sent as a way to trigger the failed deepest suborchestrations to rerun.
@@ -1036,7 +1051,9 @@ internal static bool ReconcileMessagesWithState(TaskOrchestrationWorkItem workIt
runtimeState.AddEvent(executionCompletedEvent);
- if (completeOrchestratorAction.Tags.TryGetValue(OrchestrationTags.CompleteOrchestrationLogWarning, out string warningMessage))
+ if (completeOrchestratorAction.Tags != null &&
+ completeOrchestratorAction.Tags.TryGetValue(OrchestrationTags.CompleteOrchestrationLogWarning, out string? warningMessage) &&
+ warningMessage != null)
{
this.logHelper.OrchestrationCompletedWithWarning(runtimeState.OrchestrationInstance!, completeOrchestratorAction.OrchestrationStatus, warningMessage);
}
@@ -1315,11 +1332,12 @@ TaskMessage ProcessCreateSubOrchestrationInstanceDecision(
// If a parent trace context was provided via the CreateSubOrchestrationAction.Tags, we will use this as the parent trace context of the suborchestration execution Activity rather than Activity.Current.Context.
if (createSubOrchestrationAction.Tags != null
- && createSubOrchestrationAction.Tags.TryGetValue(OrchestrationTags.TraceParent, out string traceParent))
+ && createSubOrchestrationAction.Tags.TryGetValue(OrchestrationTags.TraceParent, out string? traceParent)
+ && traceParent != null)
{
// If a parent trace context was provided but we fail to parse it, we don't want to attach any parent trace context to the start event since that will incorrectly link the trace corresponding to the orchestration execution
// as a child of Activity.Current, which is not truly the parent of the request
- if (createSubOrchestrationAction.Tags.TryGetValue(OrchestrationTags.TraceState, out string traceState)
+ if (createSubOrchestrationAction.Tags.TryGetValue(OrchestrationTags.TraceState, out string? traceState)
&& ActivityContext.TryParse(traceParent, traceState, out ActivityContext parentTraceContext))
{
startedEvent.SetParentTraceContext(parentTraceContext);
diff --git a/src/DurableTask.Core/TaskOrchestrationExecutor.cs b/src/DurableTask.Core/TaskOrchestrationExecutor.cs
index 540851e50..9e0db7c9c 100644
--- a/src/DurableTask.Core/TaskOrchestrationExecutor.cs
+++ b/src/DurableTask.Core/TaskOrchestrationExecutor.cs
@@ -137,7 +137,7 @@ public OrchestratorExecutionResult ExecuteNewEvents()
OrchestratorExecutionResult ExecuteCore(IEnumerable pastEvents, IEnumerable newEvents)
{
- SynchronizationContext prevCtx = SynchronizationContext.Current;
+ SynchronizationContext? prevCtx = SynchronizationContext.Current;
try
{
@@ -290,7 +290,7 @@ public TaskOrchestrationSynchronizationContext(TaskScheduler scheduler)
this.scheduler = scheduler;
}
- public override void Post(SendOrPostCallback sendOrPostCallback, object state)
+ public override void Post(SendOrPostCallback sendOrPostCallback, object? state)
{
Task.Factory.StartNew(() => sendOrPostCallback(state),
CancellationToken.None,
@@ -298,7 +298,7 @@ public override void Post(SendOrPostCallback sendOrPostCallback, object state)
this.scheduler);
}
- public override void Send(SendOrPostCallback sendOrPostCallback, object state)
+ public override void Send(SendOrPostCallback sendOrPostCallback, object? state)
{
var t = new Task(() => sendOrPostCallback(state));
t.RunSynchronously(this.scheduler);
diff --git a/src/DurableTask.Core/Tracing/TraceHelper.cs b/src/DurableTask.Core/Tracing/TraceHelper.cs
index 13a7a0cb9..1ed20792c 100644
--- a/src/DurableTask.Core/Tracing/TraceHelper.cs
+++ b/src/DurableTask.Core/Tracing/TraceHelper.cs
@@ -59,14 +59,17 @@ public class TraceHelper
startTime = requestTime;
}
- Activity? newActivity = ActivityTraceSource.StartActivity(
+ Activity? newActivity = ActivityTraceSource.CreateActivity(
CreateSpanName(TraceActivityConstants.CreateOrchestration, startEvent.Name, startEvent.Version),
kind: ActivityKind.Producer,
parentContext: parentTraceContext,
- startTime: startTime);
+ idFormat: ActivityIdFormat.W3C);
if (newActivity != null)
{
+ // This ID is persisted as a W3C traceparent, independent of process-wide Activity defaults.
+ newActivity.SetStartTime(startTime.UtcDateTime);
+ newActivity.Start();
newActivity.SetTag(Schema.Task.Type, TraceActivityConstants.Orchestration);
newActivity.SetTag(Schema.Task.Name, startEvent.Name);
newActivity.SetTag(Schema.Task.InstanceId, startEvent.OrchestrationInstance.InstanceId);
diff --git a/src/DurableTask.Emulator/DurableTask.Emulator.csproj b/src/DurableTask.Emulator/DurableTask.Emulator.csproj
index cdac0b73b..39ec97b85 100644
--- a/src/DurableTask.Emulator/DurableTask.Emulator.csproj
+++ b/src/DurableTask.Emulator/DurableTask.Emulator.csproj
@@ -2,7 +2,7 @@
- netstandard2.0;net48
+ net8.0;net472;net48Microsoft.Azure.DurableTask.EmulatorNU5125
diff --git a/src/DurableTask.ServiceBus/Common/Abstraction/ServiceBusAbstraction.cs b/src/DurableTask.ServiceBus/Common/Abstraction/ServiceBusAbstraction.cs
index c8d97b205..c111f2b4a 100644
--- a/src/DurableTask.ServiceBus/Common/Abstraction/ServiceBusAbstraction.cs
+++ b/src/DurableTask.ServiceBus/Common/Abstraction/ServiceBusAbstraction.cs
@@ -9,7 +9,7 @@ namespace DurableTask.ServiceBus.Common.Abstraction
using System.Threading.Tasks;
using System.Xml;
using DurableTask.ServiceBus.Tracking;
-#if !NETSTANDARD2_0
+#if !USE_AZURE_MESSAGING_SERVICEBUS
using Microsoft.ServiceBus.Messaging;
public class DataContractBinarySerializer
@@ -116,7 +116,7 @@ public override void WriteStartObject(XmlDictionaryWriter writer, object graph)
}
#endif
-#if NETSTANDARD2_0
+#if USE_AZURE_MESSAGING_SERVICEBUS
///
public class IMessageSession
{
@@ -249,7 +249,7 @@ public async Task CloseAsync()
}
-#if NETSTANDARD2_0
+#if USE_AZURE_MESSAGING_SERVICEBUS
public abstract class Union
{
public abstract T Match(Func f, Func g);
@@ -553,7 +553,7 @@ public SystemPropertiesCollection(BrokeredMessage brokered)
#endif
}
-#if NETSTANDARD2_0
+#if USE_AZURE_MESSAGING_SERVICEBUS
///
public abstract class RetryPolicy : Azure.Messaging.ServiceBus.ServiceBusRetryPolicy
{
@@ -583,7 +583,7 @@ public static implicit operator Microsoft.ServiceBus.RetryPolicy(RetryPolicy rp)
#endif
}
-#if NETSTANDARD2_0
+#if USE_AZURE_MESSAGING_SERVICEBUS
///
public class ServiceBusConnection
{
@@ -643,7 +643,7 @@ public ServiceBusConnection(ServiceBusConnectionStringBuilder connectionStringBu
#endif
}
-#if NETSTANDARD2_0
+#if USE_AZURE_MESSAGING_SERVICEBUS
///
public class ServiceBusConnectionStringBuilder
{
@@ -675,7 +675,7 @@ public ServiceBusConnectionStringBuilder(string connectionString)
#endif
}
-#if NETSTANDARD2_0
+#if USE_AZURE_MESSAGING_SERVICEBUS
#else
public class TokenProvider
{
@@ -703,7 +703,7 @@ public static TokenProvider CreateSharedAccessSignatureTokenProvider(string keyN
}
#endif
-#if NETSTANDARD2_0
+#if USE_AZURE_MESSAGING_SERVICEBUS
///
public class MessageSender
{
@@ -802,7 +802,7 @@ public async Task CloseAsync()
#endif
}
-#if NETSTANDARD2_0
+#if USE_AZURE_MESSAGING_SERVICEBUS
///
public class MessageReceiver
{
@@ -909,7 +909,7 @@ public async Task RenewLockAsync(Message message)
#endif
}
-#if NETSTANDARD2_0
+#if USE_AZURE_MESSAGING_SERVICEBUS
///
public class QueueClient
{
@@ -993,7 +993,7 @@ public async Task AcceptMessageSessionAsync(TimeSpan operationT
#endif
}
-#if NETSTANDARD2_0
+#if USE_AZURE_MESSAGING_SERVICEBUS
public class QueueDescription
{
private readonly Union propertiesUnion;
@@ -1106,7 +1106,7 @@ public async Task> GetQueuesAsync()
#endif
}
-#if NETSTANDARD2_0
+#if USE_AZURE_MESSAGING_SERVICEBUS
public class SessionClient
{
private readonly Azure.Messaging.ServiceBus.ServiceBusClient serviceBusClient;
diff --git a/src/DurableTask.ServiceBus/Common/ServiceBusUtils.cs b/src/DurableTask.ServiceBus/Common/ServiceBusUtils.cs
index cc9472a1a..dff023398 100644
--- a/src/DurableTask.ServiceBus/Common/ServiceBusUtils.cs
+++ b/src/DurableTask.ServiceBus/Common/ServiceBusUtils.cs
@@ -54,7 +54,7 @@ public static async Task GetBrokeredMessageFromObjectAsync(
if (compressionSettings.Style == CompressionStyle.Legacy)
{
-#if NETSTANDARD2_0
+#if USE_AZURE_MESSAGING_SERVICEBUS
using (var ms = new MemoryStream())
{
var serialiser = (XmlObjectSerializer)typeof(DataContractSerializer)
@@ -137,7 +137,7 @@ public static async Task GetBrokeredMessageFromObjectAsync(
static Message GenerateBrokeredMessageWithCompressionTypeProperty(Stream stream, string compressionType)
{
-#if NETSTANDARD2_0
+#if USE_AZURE_MESSAGING_SERVICEBUS
Message brokeredMessage;
using (var ms = new MemoryStream())
{
@@ -212,7 +212,7 @@ public static async Task GetObjectFromBrokeredMessageAsync(Message message
if (string.IsNullOrWhiteSpace(compressionType))
{
// no compression, legacy style
-#if NETSTANDARD2_0
+#if USE_AZURE_MESSAGING_SERVICEBUS
var dataContractSerializer = new DataContractSerializer(typeof(T));
using (var ms = new MemoryStream(message.Body))
deserializedObject = (T)dataContractSerializer.ReadObject(ms);
@@ -300,7 +300,7 @@ static Task LoadMessageStreamAsync(Message message, IOrchestrationServic
{
// load the stream from the message directly if the blob key property is not set,
// i.e., it is not stored externally
-#if NETSTANDARD2_0
+#if USE_AZURE_MESSAGING_SERVICEBUS
return Task.Run(() => new System.IO.MemoryStream(message.Body) as Stream);
#else
return Task.Run(() => message.GetBody());
diff --git a/src/DurableTask.ServiceBus/DurableTask.ServiceBus.csproj b/src/DurableTask.ServiceBus/DurableTask.ServiceBus.csproj
index 0152c0860..d539732d9 100644
--- a/src/DurableTask.ServiceBus/DurableTask.ServiceBus.csproj
+++ b/src/DurableTask.ServiceBus/DurableTask.ServiceBus.csproj
@@ -2,9 +2,10 @@
- netstandard2.0;net48
+ net8.0;net472;net48Microsoft.Azure.DurableTask.ServiceBusAnyCPU;x64
+ $(DefineConstants);USE_AZURE_MESSAGING_SERVICEBUS
@@ -25,18 +26,25 @@
+
+
+
-
+
+
+
+
+
diff --git a/src/DurableTask.ServiceBus/ServiceBusOrchestrationService.cs b/src/DurableTask.ServiceBus/ServiceBusOrchestrationService.cs
index ae451e033..2a0e41e2b 100644
--- a/src/DurableTask.ServiceBus/ServiceBusOrchestrationService.cs
+++ b/src/DurableTask.ServiceBus/ServiceBusOrchestrationService.cs
@@ -42,7 +42,7 @@ namespace DurableTask.ServiceBus
using ServiceBusConnection = DurableTask.ServiceBus.Common.Abstraction.ServiceBusConnection;
using ManagementClient = DurableTask.ServiceBus.Common.Abstraction.ManagementClient;
using ServiceBusConnectionStringBuilder = DurableTask.ServiceBus.Common.Abstraction.ServiceBusConnectionStringBuilder;
-#if NETSTANDARD2_0
+#if USE_AZURE_MESSAGING_SERVICEBUS
using Azure.Core;
using ReceiveMode = Azure.Messaging.ServiceBus.ServiceBusReceiveMode;
#else
@@ -109,7 +109,7 @@ public class ServiceBusOrchestrationService : IOrchestrationService, IOrchestrat
ServiceBusConnection serviceBusConnection;
-#if NETSTANDARD2_0
+#if USE_AZURE_MESSAGING_SERVICEBUS
///
/// Create a new ServiceBusOrchestrationService to the given service bus namespace and hub name
@@ -187,7 +187,7 @@ public ServiceBusOrchestrationService(
if (!string.IsNullOrEmpty(connectionSettings.ConnectionString))
{
var sbConnectionStringBuilder = new ServiceBusConnectionStringBuilder(connectionSettings.ConnectionString);
-#if NETSTANDARD2_0
+#if USE_AZURE_MESSAGING_SERVICEBUS
this.serviceBusConnection = new ServiceBusConnection(sbConnectionStringBuilder);
#else
this.serviceBusConnection = new ServiceBusConnection(sbConnectionStringBuilder)
@@ -197,7 +197,7 @@ public ServiceBusOrchestrationService(
};
#endif
}
-#if NETSTANDARD2_0
+#if USE_AZURE_MESSAGING_SERVICEBUS
else if (connectionSettings.Endpoint != null && connectionSettings.TokenCredential != null)
{
@@ -248,7 +248,7 @@ public async Task StartAsync()
this.orchestratorSender = new MessageSender(this.serviceBusConnection, this.orchestratorEntityName, this.workerEntityName);
this.workerSender = new MessageSender(this.serviceBusConnection, this.workerEntityName, this.orchestratorEntityName);
this.trackingSender = new MessageSender(this.serviceBusConnection, this.trackingEntityName, this.orchestratorEntityName);
-#if !NETSTANDARD2_0
+#if !USE_AZURE_MESSAGING_SERVICEBUS
this.orchestratorQueueClient = new QueueClient(this.serviceBusConnection, this.orchestratorEntityName, ReceiveMode.PeekLock, RetryPolicy.Default);
#else
this.orchestratorQueueClient = new QueueClient(this.serviceBusConnection, this.orchestratorEntityName);
@@ -1333,7 +1333,7 @@ public async Task PurgeOrchestrationHistoryAsync(
static bool IsTransientException(Exception exception)
{
// TODO : Once we change the exception model, check for inner exception
-#if NETSTANDARD2_0
+#if USE_AZURE_MESSAGING_SERVICEBUS
return (exception as Azure.Messaging.ServiceBus.ServiceBusException)?.IsTransient ?? false;
#else
return (exception as MessagingException)?.IsTransient ?? false;
@@ -1729,7 +1729,7 @@ await Utils.ExecuteWithRetries(async () =>
{
await managementClient.DeleteQueueAsync(path);
}
-#if !NETSTANDARD2_0
+#if !USE_AZURE_MESSAGING_SERVICEBUS
catch (MessagingEntityAlreadyExistsException)
#else
catch (Azure.Messaging.ServiceBus.ServiceBusException e) when (e.Reason.Equals(Azure.Messaging.ServiceBus.ServiceBusFailureReason.MessagingEntityAlreadyExists))
@@ -1737,7 +1737,7 @@ await Utils.ExecuteWithRetries(async () =>
{
await Task.FromResult(0);
}
-#if !NETSTANDARD2_0
+#if !USE_AZURE_MESSAGING_SERVICEBUS
catch (MessagingEntityNotFoundException)
#else
catch (Azure.Messaging.ServiceBus.ServiceBusException e) when (e.Reason.Equals(Azure.Messaging.ServiceBus.ServiceBusFailureReason.MessagingEntityNotFound))
@@ -1762,7 +1762,7 @@ await Utils.ExecuteWithRetries(async () =>
{
await CreateQueueAsync(managementClient, path, requiresSessions, requiresDuplicateDetection, maxDeliveryCount, maxSizeInMegabytes);
}
-#if !NETSTANDARD2_0
+#if !USE_AZURE_MESSAGING_SERVICEBUS
catch (MessagingEntityAlreadyExistsException)
#else
catch (Azure.Messaging.ServiceBus.ServiceBusException e) when (e.Reason.Equals(Azure.Messaging.ServiceBus.ServiceBusFailureReason.MessagingEntityAlreadyExists))
@@ -1800,7 +1800,7 @@ async Task CreateQueueAsync(
throw new ArgumentException($"The specified value {maxSizeInMegabytes} is invalid for the maximum queue size in megabytes.\r\nIt must be one of the following values:\r\n{string.Join(";", ValidQueueSizes)}", nameof(maxSizeInMegabytes));
}
-#if NETSTANDARD2_0
+#if USE_AZURE_MESSAGING_SERVICEBUS
var description = new Azure.Messaging.ServiceBus.Administration.CreateQueueOptions(path)
#else
var description = new QueueDescription(path)
@@ -1849,7 +1849,7 @@ ManagementClient CreateManagementClient()
{
return new ManagementClient(this.connectionSettings.ConnectionString);
}
-#if NETSTANDARD2_0
+#if USE_AZURE_MESSAGING_SERVICEBUS
else if (connectionSettings.Endpoint != null && connectionSettings.TokenCredential != null)
{
return new ManagementClient(connectionSettings.Endpoint.Host, connectionSettings.TokenCredential);
diff --git a/src/DurableTask.ServiceBus/Settings/ServiceBusConnectionSettings.cs b/src/DurableTask.ServiceBus/Settings/ServiceBusConnectionSettings.cs
index 075207532..952cbc816 100644
--- a/src/DurableTask.ServiceBus/Settings/ServiceBusConnectionSettings.cs
+++ b/src/DurableTask.ServiceBus/Settings/ServiceBusConnectionSettings.cs
@@ -13,7 +13,7 @@
namespace DurableTask.ServiceBus.Settings
{
-#if NETSTANDARD2_0
+#if USE_AZURE_MESSAGING_SERVICEBUS
using Azure.Core;
#endif
using System;
@@ -36,7 +36,7 @@ public static ServiceBusConnectionSettings Create(string connectionString)
};
}
-#if NETSTANDARD2_0
+#if USE_AZURE_MESSAGING_SERVICEBUS
///
/// Creates an instance of
@@ -83,7 +83,7 @@ private ServiceBusConnectionSettings()
///
public string ConnectionString { get; private set; }
-#if NETSTANDARD2_0
+#if USE_AZURE_MESSAGING_SERVICEBUS
///
/// Service Bus endpoint
diff --git a/test/DurableTask.AzureServiceFabric.Integration.Tests/DurableTask.AzureServiceFabric.Integration.Tests.csproj b/test/DurableTask.AzureServiceFabric.Integration.Tests/DurableTask.AzureServiceFabric.Integration.Tests.csproj
index 09d30327f..3864bd43e 100644
--- a/test/DurableTask.AzureServiceFabric.Integration.Tests/DurableTask.AzureServiceFabric.Integration.Tests.csproj
+++ b/test/DurableTask.AzureServiceFabric.Integration.Tests/DurableTask.AzureServiceFabric.Integration.Tests.csproj
@@ -12,7 +12,7 @@
-
+
diff --git a/test/DurableTask.AzureStorage.Tests/AsyncAutoResetEventTests.cs b/test/DurableTask.AzureStorage.Tests/AsyncAutoResetEventTests.cs
index 29d0b0ef9..4ca66cc55 100644
--- a/test/DurableTask.AzureStorage.Tests/AsyncAutoResetEventTests.cs
+++ b/test/DurableTask.AzureStorage.Tests/AsyncAutoResetEventTests.cs
@@ -21,14 +21,14 @@ namespace DurableTask.AzureStorage.Tests
[TestClass]
public class AsyncAutoResetEventTests
{
- [DataTestMethod]
+ [TestMethod]
[DataRow(false, false)]
[DataRow(true, true)]
public async Task InitialState(bool initiallySignaled, bool expectedResult)
{
var resetEvent = new AsyncAutoResetEvent(initiallySignaled);
bool result = await resetEvent.WaitAsync(TimeSpan.Zero);
- Assert.AreEqual(result, expectedResult);
+ Assert.AreEqual(expectedResult, result);
}
[TestMethod]
diff --git a/test/DurableTask.AzureStorage.Tests/AzureStorageScaleTests.cs b/test/DurableTask.AzureStorage.Tests/AzureStorageScaleTests.cs
index 7d7ac8a78..660adb9fc 100644
--- a/test/DurableTask.AzureStorage.Tests/AzureStorageScaleTests.cs
+++ b/test/DurableTask.AzureStorage.Tests/AzureStorageScaleTests.cs
@@ -210,7 +210,7 @@ private async Task EnsureLeasesMatchControlQueue(string directoryReference, Blob
/// REQUIREMENT: Workers can be added or removed at any time and control-queue partitions are load-balanced automatically.
/// REQUIREMENT: No two workers will ever process the same control queue.
///
- [DataTestMethod]
+ [TestMethod]
[DataRow(PartitionManagerType.V1Legacy, 30)]
[DataRow(PartitionManagerType.V2Safe, 180)]
public async Task MultiWorkerLeaseMovement(PartitionManagerType partitionManagerType, int timeoutInSeconds)
@@ -589,7 +589,7 @@ await TestHelpers.WaitFor(
await service2.CompleteTaskOrchestrationWorkItemAsync(workItem2, runtimeState, new List(), new List(), new List(), null, null);
// Now worker 1 will attempt to complete the same work item. Since this is the first attempt to complete a work item and add a history for the orchestration (by worker 1),
// there is no etag stored for the OrchestrationSession, and so the a "conflict" exception will be thrown as worker 2 already created a history for the orchestration.
- SessionAbortedException exception = await Assert.ThrowsExceptionAsync(async () =>
+ SessionAbortedException exception = await Assert.ThrowsExactlyAsync(async () =>
await service1.CompleteTaskOrchestrationWorkItemAsync(workItem1, runtimeState, new List(), new List(), new List(), null, null)
);
Assert.IsInstanceOfType(exception.InnerException, typeof(DurableTaskStorageException));
@@ -632,7 +632,7 @@ await TestHelpers.WaitFor(
await service1.CompleteTaskOrchestrationWorkItemAsync(workItem1, runtimeState, new List(), new List(), new List(), null, null);
// Now worker 2 attempts to complete the same work item. Since this is not the first work item for the orchestration, now an etag exists for the OrchestrationSession, and the exception
// that is thrown will be "precondition failed" as the Etag is stale after worker 1 completed the work item.
- exception = await Assert.ThrowsExceptionAsync(async () =>
+ exception = await Assert.ThrowsExactlyAsync(async () =>
await service2.CompleteTaskOrchestrationWorkItemAsync(workItem2, runtimeState, new List(), new List(), new List(), null, null)
);
Assert.IsInstanceOfType(exception.InnerException, typeof(DurableTaskStorageException));
diff --git a/test/DurableTask.AzureStorage.Tests/AzureStorageScenarioTests.cs b/test/DurableTask.AzureStorage.Tests/AzureStorageScenarioTests.cs
index 07b064aca..98f358adc 100644
--- a/test/DurableTask.AzureStorage.Tests/AzureStorageScenarioTests.cs
+++ b/test/DurableTask.AzureStorage.Tests/AzureStorageScenarioTests.cs
@@ -47,10 +47,26 @@ public class AzureStorageScenarioTests
{
public static readonly TimeSpan StandardTimeout = Debugger.IsAttached ? TimeSpan.FromMinutes(5) : TimeSpan.FromSeconds(30);
+ static void AssertJsonTokenEquals(JToken expected, string actual)
+ {
+ JToken actualToken = JToken.Parse(actual);
+ Assert.IsTrue(
+ JToken.DeepEquals(expected, actualToken),
+ $"Expected JSON token {expected} ({expected.Type}), but found {actualToken} ({actualToken.Type}).");
+ }
+
+ static void AssertJsonTokenNotEquals(JToken expected, string actual)
+ {
+ JToken actualToken = JToken.Parse(actual);
+ Assert.IsFalse(
+ JToken.DeepEquals(expected, actualToken),
+ $"Expected JSON token to differ from {expected} ({expected.Type}).");
+ }
+
///
/// End-to-end test which validates a simple orchestrator function which doesn't call any activity functions.
///
- [DataTestMethod]
+ [TestMethod]
[DataRow(true)]
[DataRow(false)]
public async Task HelloWorldOrchestration_Inline(bool enableExtendedSessions)
@@ -63,8 +79,8 @@ public async Task HelloWorldOrchestration_Inline(bool enableExtendedSessions)
var status = await client.WaitForCompletionAsync(StandardTimeout);
Assert.AreEqual(OrchestrationStatus.Completed, status?.OrchestrationStatus);
- Assert.AreEqual("World", JToken.Parse(status?.Input));
- Assert.AreEqual("Hello, World!", JToken.Parse(status?.Output));
+ AssertJsonTokenEquals("World", status?.Input);
+ AssertJsonTokenEquals("Hello, World!", status?.Output);
await host.StopAsync();
}
@@ -73,7 +89,7 @@ public async Task HelloWorldOrchestration_Inline(bool enableExtendedSessions)
///
/// End-to-end test which runs a simple orchestrator function that calls a single activity function.
///
- [DataTestMethod]
+ [TestMethod]
[DataRow(true)]
[DataRow(false)]
public async Task HelloWorldOrchestration_Activity(bool enableExtendedSessions)
@@ -86,8 +102,8 @@ public async Task HelloWorldOrchestration_Activity(bool enableExtendedSessions)
var status = await client.WaitForCompletionAsync(StandardTimeout);
Assert.AreEqual(OrchestrationStatus.Completed, status?.OrchestrationStatus);
- Assert.AreEqual("World", JToken.Parse(status?.Input));
- Assert.AreEqual("Hello, World!", JToken.Parse(status?.Output));
+ AssertJsonTokenEquals("World", status?.Input);
+ AssertJsonTokenEquals("Hello, World!", status?.Output);
await host.StopAsync();
}
@@ -107,8 +123,8 @@ public async Task SequentialOrchestration()
var status = await client.WaitForCompletionAsync(StandardTimeout);
Assert.AreEqual(OrchestrationStatus.Completed, status?.OrchestrationStatus);
- Assert.AreEqual(10, JToken.Parse(status?.Input));
- Assert.AreEqual(3628800, JToken.Parse(status?.Output));
+ AssertJsonTokenEquals(10, status?.Input);
+ AssertJsonTokenEquals(3628800, status?.Output);
await host.StopAsync();
}
@@ -129,8 +145,8 @@ public async Task SequentialOrchestrationNoReplay()
var status = await client.WaitForCompletionAsync(StandardTimeout);
Assert.AreEqual(OrchestrationStatus.Completed, status?.OrchestrationStatus);
- Assert.AreEqual(10, JToken.Parse(status?.Input));
- Assert.AreEqual(3628800, JToken.Parse(status?.Output));
+ AssertJsonTokenEquals(10, status?.Input);
+ AssertJsonTokenEquals(3628800, status?.Output);
await host.StopAsync();
}
@@ -147,8 +163,8 @@ public async Task ParentOfSequentialOrchestration()
var status = await client.WaitForCompletionAsync(TimeSpan.FromSeconds(30));
Assert.AreEqual(OrchestrationStatus.Completed, status?.OrchestrationStatus);
- Assert.AreEqual(10, JToken.Parse(status?.Input));
- Assert.AreEqual(3628800, JToken.Parse(status?.Output));
+ AssertJsonTokenEquals(10, status?.Input);
+ AssertJsonTokenEquals(3628800, status?.Output);
await host.StopAsync();
}
@@ -157,7 +173,7 @@ public async Task ParentOfSequentialOrchestration()
///
/// End-to-end test which runs a slow orchestrator that causes work item renewal
///
- [DataTestMethod]
+ [TestMethod]
[DataRow(true)]
[DataRow(false)]
public async Task LongRunningOrchestrator(bool enableExtendedSessions)
@@ -176,7 +192,7 @@ public async Task LongRunningOrchestrator(bool enableExtendedSessions)
var status = await client.WaitForCompletionAsync(StandardTimeout);
Assert.AreEqual(OrchestrationStatus.Completed, status?.OrchestrationStatus);
- Assert.AreEqual("ok", JToken.Parse(status?.Output));
+ AssertJsonTokenEquals("ok", status?.Output);
await host.StopAsync();
}
@@ -266,7 +282,7 @@ public async Task NoInstancesGetAllOrchestrationStatusesNullContinuationToken()
}
}
- [DataTestMethod]
+ [TestMethod]
[DataRow(false, false)]
[DataRow(true, false)]
[DataRow(false, true)]
@@ -281,13 +297,13 @@ public async Task EventConversation(bool enableExtendedSessions, bool useFireAnd
var status = await client.WaitForCompletionAsync(TimeSpan.FromSeconds(30));
Assert.AreEqual(OrchestrationStatus.Completed, status?.OrchestrationStatus);
- Assert.AreEqual("OK", JToken.Parse(status?.Output));
+ AssertJsonTokenEquals("OK", status?.Output);
await host.StopAsync();
}
}
- [DataTestMethod]
+ [TestMethod]
[DataRow(false)]
[DataRow(true)]
public async Task AutoStart(bool enableExtendedSessions)
@@ -302,13 +318,13 @@ public async Task AutoStart(bool enableExtendedSessions)
var status = await client.WaitForCompletionAsync(TimeSpan.FromSeconds(30));
Assert.AreEqual(OrchestrationStatus.Completed, status?.OrchestrationStatus);
- Assert.AreEqual("OK", JToken.Parse(status?.Output));
+ AssertJsonTokenEquals("OK", status?.Output);
await host.StopAsync();
}
}
- [DataTestMethod]
+ [TestMethod]
[DataRow(false)]
[DataRow(true)]
public async Task ContinueAsNewThenTimer(bool enableExtendedSessions)
@@ -321,7 +337,7 @@ public async Task ContinueAsNewThenTimer(bool enableExtendedSessions)
var status = await client.WaitForCompletionAsync(TimeSpan.FromSeconds(30));
Assert.AreEqual(OrchestrationStatus.Completed, status?.OrchestrationStatus);
- Assert.AreEqual("OK", JToken.Parse(status?.Output));
+ AssertJsonTokenEquals("OK", status?.Output);
await host.StopAsync();
}
@@ -396,7 +412,7 @@ public async Task ValidateCustomStatusPersists()
var state = await client.WaitForCompletionAsync(TimeSpan.FromSeconds(30));
Assert.AreEqual(OrchestrationStatus.Completed, state?.OrchestrationStatus);
- Assert.AreEqual(customStatus, JToken.Parse(state?.Status));
+ AssertJsonTokenEquals(customStatus, state?.Status);
await host.StopAsync();
}
@@ -956,7 +972,7 @@ public async Task PurgeInstanceHistoryWithoutTimeoutReturnsNullIsComplete()
/// End-to-end test which validates parallel function execution by enumerating all files in the current directory
/// in parallel and getting the sum total of all file sizes.
///
- [DataTestMethod]
+ [TestMethod]
[DataRow(true)]
[DataRow(false)]
public async Task ParallelOrchestration(bool enableExtendedSessions)
@@ -969,14 +985,14 @@ public async Task ParallelOrchestration(bool enableExtendedSessions)
var status = await client.WaitForCompletionAsync(TimeSpan.FromSeconds(90));
Assert.AreEqual(OrchestrationStatus.Completed, status?.OrchestrationStatus);
- Assert.AreEqual(Environment.CurrentDirectory, JToken.Parse(status?.Input));
+ AssertJsonTokenEquals(Environment.CurrentDirectory, status?.Input);
Assert.IsTrue(long.Parse(status?.Output) > 0L);
await host.StopAsync();
}
}
- [DataTestMethod]
+ [TestMethod]
[DataRow(true)]
[DataRow(false)]
public async Task LargeFanOutOrchestration(bool enableExtendedSessions)
@@ -1015,7 +1031,7 @@ public async Task FanOutOrchestration_LargeHistoryBatches()
///
/// End-to-end test which validates the ContinueAsNew functionality by implementing a counter actor pattern.
///
- [DataTestMethod]
+ [TestMethod]
[DataRow(true)]
[DataRow(false)]
public async Task ActorOrchestration(bool enableExtendedSessions)
@@ -1051,10 +1067,10 @@ public async Task ActorOrchestration(bool enableExtendedSessions)
status = await client.WaitForCompletionAsync(TimeSpan.FromSeconds(10));
Assert.AreEqual(OrchestrationStatus.Completed, status?.OrchestrationStatus);
- Assert.AreEqual(3, JToken.Parse(status?.Output));
+ AssertJsonTokenEquals(3, status?.Output);
// When using ContinueAsNew, the original input is discarded and replaced with the most recent state.
- Assert.AreNotEqual(initialValue, JToken.Parse(status?.Input));
+ AssertJsonTokenNotEquals(initialValue, status?.Input);
await host.StopAsync();
}
@@ -1063,7 +1079,7 @@ public async Task ActorOrchestration(bool enableExtendedSessions)
///
/// End-to-end test which validates the ContinueAsNew functionality by implementing character counter actor pattern.
///
- [DataTestMethod]
+ [TestMethod]
[DataRow(true)]
[DataRow(false)]
public async Task ActorOrchestrationForLargeInput(bool enableExtendedSessions)
@@ -1074,7 +1090,7 @@ public async Task ActorOrchestrationForLargeInput(bool enableExtendedSessions)
///
/// End-to-end test which validates the deletion of all data generated by the ContinueAsNew functionality in the character counter actor pattern.
///
- [DataTestMethod]
+ [TestMethod]
[DataRow(true)]
[DataRow(false)]
public async Task ActorOrchestrationDeleteAllLargeMessageBlobs(bool enableExtendedSessions)
@@ -1188,7 +1204,7 @@ private async Task> ValidateCharacterCoun
///
/// End-to-end test which validates the Terminate functionality.
///
- [DataTestMethod]
+ [TestMethod]
[DataRow(true)]
[DataRow(false)]
public async Task TerminateOrchestration(bool enableExtendedSessions)
@@ -1218,7 +1234,7 @@ public async Task TerminateOrchestration(bool enableExtendedSessions)
///
/// End-to-end test which validates the Suspend-Resume functionality.
///
- [DataTestMethod]
+ [TestMethod]
[DataRow(true)]
[DataRow(false)]
public async Task SuspendResumeOrchestration(bool enableExtendedSessions)
@@ -1242,13 +1258,13 @@ public async Task SuspendResumeOrchestration(bool enableExtendedSessions)
// Test case 2: external event does not go through
await client.RaiseEventAsync("changeStatusNow", changedStatus);
status = await client.GetStatusAsync();
- Assert.AreEqual(originalStatus, JToken.Parse(status?.Status));
+ AssertJsonTokenEquals(originalStatus, status?.Status);
// Test case 3: external event now goes through
await client.ResumeAsync("wakeUp");
status = await client.WaitForCompletionAsync(TimeSpan.FromSeconds(10));
Assert.AreEqual(OrchestrationStatus.Completed, status?.OrchestrationStatus);
- Assert.AreEqual(changedStatus, JToken.Parse(status?.Status));
+ AssertJsonTokenEquals(changedStatus, status?.Status);
await host.StopAsync();
}
@@ -1257,7 +1273,7 @@ public async Task SuspendResumeOrchestration(bool enableExtendedSessions)
///
/// Test that a suspended orchestration can be terminated.
///
- [DataTestMethod]
+ [TestMethod]
[DataRow(true)]
[DataRow(false)]
public async Task TerminateSuspendedOrchestration(bool enableExtendedSessions)
@@ -1286,7 +1302,7 @@ public async Task TerminateSuspendedOrchestration(bool enableExtendedSessions)
/// Test that a pending orchestration can be terminated (including tests with a large termination reason that will need to be
/// stored in blob storage).
///
- [DataTestMethod]
+ [TestMethod]
[DataRow(true, true)]
[DataRow(false, true)]
[DataRow(true, false)]
@@ -1593,7 +1609,7 @@ public async Task RewindNestedSubOrchestrationTest()
}
}
- [DataTestMethod]
+ [TestMethod]
[DataRow(true)]
[DataRow(false)]
public async Task TimerCancellation(bool enableExtendedSessions)
@@ -1612,7 +1628,7 @@ public async Task TimerCancellation(bool enableExtendedSessions)
var status = await client.WaitForCompletionAsync(TimeSpan.FromSeconds(30));
Assert.AreEqual(OrchestrationStatus.Completed, status?.OrchestrationStatus);
- Assert.AreEqual("Approved", JToken.Parse(status?.Output));
+ AssertJsonTokenEquals("Approved", status?.Output);
await host.StopAsync();
}
@@ -1621,7 +1637,7 @@ public async Task TimerCancellation(bool enableExtendedSessions)
///
/// End-to-end test which validates the handling of durable timer expiration.
///
- [DataTestMethod]
+ [TestMethod]
[DataRow(true)]
[DataRow(false)]
public async Task TimerExpiration(bool enableExtendedSessions)
@@ -1641,13 +1657,13 @@ public async Task TimerExpiration(bool enableExtendedSessions)
var status = await client.WaitForCompletionAsync(TimeSpan.FromSeconds(20));
Assert.AreEqual(OrchestrationStatus.Completed, status?.OrchestrationStatus);
- Assert.AreEqual("Expired", JToken.Parse(status?.Output));
+ AssertJsonTokenEquals("Expired", status?.Output);
await host.StopAsync();
}
}
- [DataTestMethod]
+ [TestMethod]
[DataRow(true)]
[DataRow(false)]
public async Task TimerDelay(bool useUtc)
@@ -1675,7 +1691,7 @@ public async Task TimerDelay(bool useUtc)
}
}
- [DataTestMethod]
+ [TestMethod]
[DataRow(false)]
[DataRow(true)]
public async Task OrchestratorStartAtAcceptsAllDateTimeKinds(bool useUtc)
@@ -1715,7 +1731,7 @@ public async Task OrchestratorStartAtAcceptsAllDateTimeKinds(bool useUtc)
///
/// End-to-end test which validates that orchestrations run concurrently of each other (up to 100 by default).
///
- [DataTestMethod]
+ [TestMethod]
[DataRow(true)]
[DataRow(false)]
public async Task OrchestrationConcurrency(bool enableExtendedSessions)
@@ -1754,7 +1770,7 @@ public async Task OrchestrationConcurrency(bool enableExtendedSessions)
///
/// End-to-end test which validates the orchestrator's exception handling behavior.
///
- [DataTestMethod]
+ [TestMethod]
[DataRow(true)]
[DataRow(false)]
public async Task HandledActivityException(bool enableExtendedSessions)
@@ -1768,7 +1784,7 @@ public async Task HandledActivityException(bool enableExtendedSessions)
var status = await client.WaitForCompletionAsync(TimeSpan.FromSeconds(15));
Assert.AreEqual(OrchestrationStatus.Completed, status?.OrchestrationStatus);
- Assert.AreEqual(5, JToken.Parse(status?.Output));
+ AssertJsonTokenEquals(5, status?.Output);
await host.StopAsync();
}
@@ -1777,7 +1793,7 @@ public async Task HandledActivityException(bool enableExtendedSessions)
///
/// End-to-end test which validates the handling of unhandled exceptions generated from orchestrator code.
///
- [DataTestMethod]
+ [TestMethod]
[DataRow(true)]
[DataRow(false)]
public async Task UnhandledOrchestrationException(bool enableExtendedSessions)
@@ -1800,7 +1816,7 @@ public async Task UnhandledOrchestrationException(bool enableExtendedSessions)
///
/// End-to-end test which validates the handling of unhandled exceptions generated from activity code.
///
- [DataTestMethod]
+ [TestMethod]
[DataRow(true)]
[DataRow(false)]
public async Task UnhandledActivityException(bool enableExtendedSessions)
@@ -1823,7 +1839,7 @@ public async Task UnhandledActivityException(bool enableExtendedSessions)
///
/// Fan-out/fan-in test which ensures each operation is run only once.
///
- [DataTestMethod]
+ [TestMethod]
[DataRow(true)]
[DataRow(false)]
public async Task FanOutToTableStorage(bool enableExtendedSessions)
@@ -1867,7 +1883,7 @@ public void ValidateEventSource()
///
/// End-to-end test which validates that orchestrations with <=60KB text message sizes can run successfully.
///
- [DataTestMethod]
+ [TestMethod]
[DataRow(true)]
[DataRow(false)]
public async Task SmallTextMessagePayloads(bool enableExtendedSessions)
@@ -1894,7 +1910,7 @@ public async Task SmallTextMessagePayloads(bool enableExtendedSessions)
var status = await client.WaitForCompletionAsync(TimeSpan.FromSeconds(60));
Assert.AreEqual(OrchestrationStatus.Completed, status?.OrchestrationStatus);
- Assert.AreEqual(message, JToken.Parse(status?.Output));
+ AssertJsonTokenEquals(message, status?.Output);
await host.StopAsync();
}
@@ -1903,7 +1919,7 @@ public async Task SmallTextMessagePayloads(bool enableExtendedSessions)
///
/// End-to-end test which validates that orchestrations with > 60KB text message sizes can run successfully.
///
- [DataTestMethod]
+ [TestMethod]
[DataRow(true)]
[DataRow(false)]
public async Task LargeQueueTextMessagePayloads_BlobUrl(bool enableExtendedSessions)
@@ -1919,9 +1935,10 @@ public async Task LargeQueueTextMessagePayloads_BlobUrl(bool enableExtendedSessi
var client = await host.StartOrchestrationAsync(typeof(Orchestrations.Echo), message);
var status = await client.WaitForCompletionAsync(TimeSpan.FromMinutes(2));
- Assert.AreEqual(OrchestrationStatus.Completed, status?.OrchestrationStatus);
- Assert.AreEqual(message, JToken.Parse(status?.Output));
- Assert.AreEqual(message, JToken.Parse(status.Input));
+ Assert.IsNotNull(status);
+ Assert.AreEqual(OrchestrationStatus.Completed, status.OrchestrationStatus);
+ AssertJsonTokenEquals(message, status.Output);
+ AssertJsonTokenEquals(message, status.Input);
await host.StopAsync();
}
@@ -1930,7 +1947,7 @@ public async Task LargeQueueTextMessagePayloads_BlobUrl(bool enableExtendedSessi
///
/// End-to-end test which validates that orchestrations with > 60KB text message sizes can run successfully.
///
- [DataTestMethod]
+ [TestMethod]
[DataRow(true)]
[DataRow(false)]
public async Task LargeTableTextMessagePayloads_SizeViolation_BlobUrl(bool enableExtendedSessions)
@@ -2014,7 +2031,7 @@ public async Task TagsAreAvailableInOrchestrationState()
///
/// End-to-end test which validates that orchestrations with > 60KB text message sizes can run successfully.
///
- [DataTestMethod]
+ [TestMethod]
[DataRow(true)]
[DataRow(false)]
public async Task LargeOverallTextMessagePayloads_BlobUrl(bool enableExtendedSessions)
@@ -2051,7 +2068,7 @@ await ValidateLargeMessageBlobUrlAsync(
///
/// End-to-end test which validates that orchestrations with > 60KB text message sizes can run successfully.
///
- [DataTestMethod]
+ [TestMethod]
[DataRow(true)]
[DataRow(false)]
public async Task LargeTextMessagePayloads_FetchLargeMessages(bool enableExtendedSessions)
@@ -2065,8 +2082,8 @@ public async Task LargeTextMessagePayloads_FetchLargeMessages(bool enableExtende
var status = await client.WaitForCompletionAsync(TimeSpan.FromMinutes(2));
Assert.AreEqual(OrchestrationStatus.Completed, status?.OrchestrationStatus);
- Assert.AreEqual(message, JToken.Parse(status?.Input));
- Assert.AreEqual(message, JToken.Parse(status?.Output));
+ AssertJsonTokenEquals(message, status?.Input);
+ AssertJsonTokenEquals(message, status?.Output);
await host.StopAsync();
}
@@ -2075,7 +2092,7 @@ public async Task LargeTextMessagePayloads_FetchLargeMessages(bool enableExtende
///
/// End-to-end test which validates that orchestrations with > 60KB text message sizes can run successfully.
///
- [DataTestMethod]
+ [TestMethod]
[DataRow(true)]
[DataRow(false)]
public async Task LargeTableTextMessagePayloads_FetchLargeMessages(bool enableExtendedSessions)
@@ -2091,8 +2108,8 @@ public async Task LargeTableTextMessagePayloads_FetchLargeMessages(bool enableEx
var status = await client.WaitForCompletionAsync(TimeSpan.FromMinutes(2));
Assert.AreEqual(OrchestrationStatus.Completed, status?.OrchestrationStatus);
- Assert.AreEqual(message, JToken.Parse(status?.Input));
- Assert.AreEqual(message, JToken.Parse(status?.Output));
+ AssertJsonTokenEquals(message, status?.Input);
+ AssertJsonTokenEquals(message, status?.Output);
await host.StopAsync();
}
@@ -2127,7 +2144,7 @@ public async Task LargeOrchestrationTags()
///
/// End-to-end test which validates that orchestrations with > 60KB text message sizes can run successfully.
///
- [DataTestMethod]
+ [TestMethod]
[DataRow(true)]
[DataRow(false)]
public async Task NonBlobUriPayload_FetchLargeMessages_RetainsOriginalPayload(bool enableExtendedSessions)
@@ -2141,8 +2158,8 @@ public async Task NonBlobUriPayload_FetchLargeMessages_RetainsOriginalPayload(bo
var status = await client.WaitForCompletionAsync(TimeSpan.FromMinutes(2));
Assert.AreEqual(OrchestrationStatus.Completed, status?.OrchestrationStatus);
- Assert.AreEqual(message, JToken.Parse(status?.Input));
- Assert.AreEqual(message, JToken.Parse(status?.Output));
+ AssertJsonTokenEquals(message, status?.Input);
+ AssertJsonTokenEquals(message, status?.Output);
await host.StopAsync();
}
@@ -2151,7 +2168,7 @@ public async Task NonBlobUriPayload_FetchLargeMessages_RetainsOriginalPayload(bo
///
/// End-to-end test which validates that orchestrations with > 60KB text message sizes can run successfully.
///
- [DataTestMethod]
+ [TestMethod]
[DataRow(true)]
[DataRow(false)]
public async Task LargeTextMessagePayloads_FetchLargeMessages_QueryState(bool enableExtendedSessions)
@@ -2168,8 +2185,8 @@ public async Task LargeTextMessagePayloads_FetchLargeMessages_QueryState(bool en
status = (await client.GetStateAsync(status.OrchestrationInstance.InstanceId)).First();
Assert.AreEqual(OrchestrationStatus.Completed, status?.OrchestrationStatus);
- Assert.AreEqual(message, JToken.Parse(status?.Input));
- Assert.AreEqual(message, JToken.Parse(status?.Output));
+ AssertJsonTokenEquals(message, status?.Input);
+ AssertJsonTokenEquals(message, status?.Output);
await host.StopAsync();
}
@@ -2179,7 +2196,7 @@ public async Task LargeTextMessagePayloads_FetchLargeMessages_QueryState(bool en
/// End-to-end test which validates that exception messages that are considered valid Urls in the Uri.TryCreate() method
/// are handled with an additional Uri format check
///
- [DataTestMethod]
+ [TestMethod]
[DataRow(true)]
[DataRow(false)]
public async Task LargeTextMessagePayloads_URIFormatCheck(bool enableExtendedSessions)
@@ -2243,7 +2260,7 @@ private StringBuilder GenerateMediumRandomStringPayload(int numChars = 128 * 102
///
/// End-to-end test which validates that orchestrations with > 60KB binary bytes message sizes can run successfully.
///
- [DataTestMethod]
+ [TestMethod]
[DataRow(true)]
[DataRow(false)]
public async Task LargeBinaryByteMessagePayloads(bool enableExtendedSessions)
@@ -2273,7 +2290,7 @@ public async Task LargeBinaryByteMessagePayloads(bool enableExtendedSessions)
///
/// End-to-end test which validates that orchestrations with > 60KB binary string message sizes can run successfully.
///
- [DataTestMethod]
+ [TestMethod]
[DataRow(true)]
[DataRow(false)]
public async Task LargeBinaryStringMessagePayloads(bool enableExtendedSessions)
@@ -2305,7 +2322,7 @@ public async Task LargeBinaryStringMessagePayloads(bool enableExtendedSessions)
///
/// End-to-end test which validates that a completed singleton instance can be recreated.
///
- [DataTestMethod]
+ [TestMethod]
[DataRow(true)]
[DataRow(false)]
public async Task RecreateCompletedInstance(bool enableExtendedSessions)
@@ -2323,8 +2340,8 @@ public async Task RecreateCompletedInstance(bool enableExtendedSessions)
var status = await client.WaitForCompletionAsync(TimeSpan.FromSeconds(30));
Assert.AreEqual(OrchestrationStatus.Completed, status?.OrchestrationStatus);
- Assert.AreEqual("One", JToken.Parse(status?.Input));
- Assert.AreEqual("Hello, One!", JToken.Parse(status?.Output));
+ AssertJsonTokenEquals("One", status?.Input);
+ AssertJsonTokenEquals("Hello, One!", status?.Output);
client = await host.StartOrchestrationAsync(
typeof(Orchestrations.SayHelloWithActivity),
@@ -2333,8 +2350,8 @@ public async Task RecreateCompletedInstance(bool enableExtendedSessions)
status = await client.WaitForCompletionAsync(TimeSpan.FromSeconds(30));
Assert.AreEqual(OrchestrationStatus.Completed, status?.OrchestrationStatus);
- Assert.AreEqual("Two", JToken.Parse(status?.Input));
- Assert.AreEqual("Hello, Two!", JToken.Parse(status?.Output));
+ AssertJsonTokenEquals("Two", status?.Input);
+ AssertJsonTokenEquals("Hello, Two!", status?.Output);
await host.StopAsync();
}
@@ -2343,7 +2360,7 @@ public async Task RecreateCompletedInstance(bool enableExtendedSessions)
///
/// End-to-end test which validates that a failed singleton instance can be recreated.
///
- [DataTestMethod]
+ [TestMethod]
[DataRow(true)]
[DataRow(false)]
public async Task RecreateFailedInstance(bool enableExtendedSessions)
@@ -2369,7 +2386,7 @@ public async Task RecreateFailedInstance(bool enableExtendedSessions)
status = await client.WaitForCompletionAsync(TimeSpan.FromSeconds(30));
Assert.AreEqual(OrchestrationStatus.Completed, status?.OrchestrationStatus);
- Assert.AreEqual("Hello, NotNull!", JToken.Parse(status?.Output));
+ AssertJsonTokenEquals("Hello, NotNull!", status?.Output);
await host.StopAsync();
}
@@ -2378,7 +2395,7 @@ public async Task RecreateFailedInstance(bool enableExtendedSessions)
///
/// End-to-end test which validates that a terminated orchestration can be recreated.
///
- [DataTestMethod]
+ [TestMethod]
[DataRow(true)]
[DataRow(false)]
public async Task RecreateTerminatedInstance(bool enableExtendedSessions)
@@ -2422,7 +2439,7 @@ public async Task RecreateTerminatedInstance(bool enableExtendedSessions)
///
/// End-to-end test which validates that a running orchestration can be recreated.
///
- [DataTestMethod]
+ [TestMethod]
[DataRow(true)]
[DataRow(false)]
public async Task RecreateRunningInstance(bool enableExtendedSessions)
@@ -2509,7 +2526,7 @@ public async Task ExtendedSessions_SessionTimeout()
status = await client.WaitForCompletionAsync(TimeSpan.FromSeconds(10));
Assert.AreEqual(OrchestrationStatus.Completed, status?.OrchestrationStatus);
- Assert.AreEqual(1, JToken.Parse(status?.Output));
+ AssertJsonTokenEquals(1, status?.Output);
await host.StopAsync();
}
@@ -2519,7 +2536,7 @@ public async Task ExtendedSessions_SessionTimeout()
/// Tests an orchestration that does two consecutive fan-out, fan-ins.
/// This is a regression test for https://github.com/Azure/durabletask/issues/241.
///
- [DataTestMethod]
+ [TestMethod]
[DataRow(true)]
[DataRow(false)]
public async Task DoubleFanOut(bool enableExtendedSessions)
@@ -2567,7 +2584,7 @@ private static async Task ValidateLargeMessageBlobUrlAsync(string taskHubName, s
///
/// Tests the behavior of from orchestrations and activities.
///
- [DataTestMethod]
+ [TestMethod]
[DataRow(true)]
[DataRow(false)]
public async Task AbortOrchestrationAndActivity(bool enableExtendedSessions)
@@ -2582,7 +2599,7 @@ public async Task AbortOrchestrationAndActivity(bool enableExtendedSessions)
Assert.AreEqual(OrchestrationStatus.Completed, status?.OrchestrationStatus);
Assert.IsNotNull(status.Output);
- Assert.AreEqual("True", JToken.Parse(status.Output));
+ AssertJsonTokenEquals("True", status.Output);
await host.StopAsync();
}
}
@@ -2592,7 +2609,7 @@ public async Task AbortOrchestrationAndActivity(bool enableExtendedSessions)
///
///
///
- [DataTestMethod]
+ [TestMethod]
[DataRow(true)]
[DataRow(false)]
public async Task ScheduledStart_Inline(bool enableExtendedSessions)
@@ -2611,11 +2628,11 @@ public async Task ScheduledStart_Inline(bool enableExtendedSessions)
await Task.WhenAll(statusStartingNow, statusStartingIn30Seconds);
Assert.AreEqual(OrchestrationStatus.Completed, statusStartingNow.Result?.OrchestrationStatus);
- Assert.AreEqual("Current Time!", JToken.Parse(statusStartingNow.Result?.Input));
+ AssertJsonTokenEquals("Current Time!", statusStartingNow.Result?.Input);
Assert.IsNull(statusStartingNow.Result.ScheduledStartTime);
Assert.AreEqual(OrchestrationStatus.Completed, statusStartingIn30Seconds.Result?.OrchestrationStatus);
- Assert.AreEqual("Current Time!", JToken.Parse(statusStartingIn30Seconds.Result?.Input));
+ AssertJsonTokenEquals("Current Time!", statusStartingIn30Seconds.Result?.Input);
Assert.AreEqual(expectedStartTime, statusStartingIn30Seconds.Result.ScheduledStartTime);
var startNowResult = (DateTime)JToken.Parse(statusStartingNow.Result?.Output);
@@ -2633,7 +2650,7 @@ public async Task ScheduledStart_Inline(bool enableExtendedSessions)
///
///
///
- [DataTestMethod]
+ [TestMethod]
[DataRow(true)]
[DataRow(false)]
public async Task ScheduledStart_Activity(bool enableExtendedSessions)
@@ -2652,11 +2669,11 @@ public async Task ScheduledStart_Activity(bool enableExtendedSessions)
await Task.WhenAll(statusStartingNow, statusStartingIn30Seconds);
Assert.AreEqual(OrchestrationStatus.Completed, statusStartingNow.Result?.OrchestrationStatus);
- Assert.AreEqual("Current Time!", JToken.Parse(statusStartingNow.Result?.Input));
+ AssertJsonTokenEquals("Current Time!", statusStartingNow.Result?.Input);
Assert.IsNull(statusStartingNow.Result.ScheduledStartTime);
Assert.AreEqual(OrchestrationStatus.Completed, statusStartingIn30Seconds.Result?.OrchestrationStatus);
- Assert.AreEqual("Current Time!", JToken.Parse(statusStartingIn30Seconds.Result?.Input));
+ AssertJsonTokenEquals("Current Time!", statusStartingIn30Seconds.Result?.Input);
Assert.AreEqual(expectedStartTime, statusStartingIn30Seconds.Result.ScheduledStartTime);
var startNowResult = (DateTime)JToken.Parse(statusStartingNow.Result?.Output);
@@ -2674,7 +2691,7 @@ public async Task ScheduledStart_Activity(bool enableExtendedSessions)
///
///
///
- [DataTestMethod]
+ [TestMethod]
[DataRow(true)]
[DataRow(false)]
public async Task ScheduledStart_Activity_GetStatus_Returns_ScheduledStart(bool enableExtendedSessions)
@@ -2719,7 +2736,7 @@ await Task.WhenAll(
/// To recover from this, users may set `AllowReplayingTerminalInstances` to true. When this is set, DTFx will not discard
/// events for terminal orchestrators, forcing a replay which eventually updates the instances table to the right state.
///
- [DataTestMethod]
+ [TestMethod]
[DataRow(true, true, true)]
[DataRow(true, true, false)]
[DataRow(true, false, true)]
@@ -2817,7 +2834,7 @@ public async Task TestAllowReplayingTerminalInstances(bool enableExtendedSession
/// the tracking store context object that is part of the orchestration session state which keeps track of the blobs.
///
///
- [DataTestMethod]
+ [TestMethod]
[DataRow(true, true)]
[DataRow(false, true)]
[DataRow(true, false)]
@@ -2909,7 +2926,7 @@ public async Task TestWorkerFailingDuringCompleteWorkItemCallCompletedOrchestrat
/// Same as but for a failed orchestration.
///
///
- [DataTestMethod]
+ [TestMethod]
[DataRow(true, true)]
[DataRow(false, true)]
[DataRow(true, false)]
@@ -3001,7 +3018,7 @@ public async Task TestWorkerFailingDuringCompleteWorkItemCallFailedOrchestration
///
/// Same as but for a terminated orchestration.
///
- [DataTestMethod]
+ [TestMethod]
[DataRow(true, true)]
[DataRow(false, true)]
[DataRow(true, false)]
@@ -3095,7 +3112,7 @@ public async Task TestWorkerFailingDuringCompleteWorkItemCallTerminatedOrchestra
/// Same as but for an orchestration with large input
/// and output, which will need to be stored in blob storage.
///
- [DataTestMethod]
+ [TestMethod]
[DataRow(true, true)]
[DataRow(false, true)]
[DataRow(true, false)]
@@ -3188,7 +3205,7 @@ public async Task TestWorkerFailingDuringCompleteWorkItemCallLargeInputOutput(bo
/// Same as but for a large termination reason that
/// will need to be stored in blob storage.
///
- [DataTestMethod]
+ [TestMethod]
[DataRow(true, true)]
[DataRow(false, true)]
[DataRow(true, false)]
@@ -3285,7 +3302,7 @@ public async Task TestWorkerFailingDuringCompleteWorkItemCallLargeTerminationRea
/// Same as but for a large exception message that will need
/// to be stored in blob storage.
///
- [DataTestMethod]
+ [TestMethod]
[DataRow(true, true)]
[DataRow(false, true)]
[DataRow(true, false)]
@@ -3477,7 +3494,7 @@ public async Task OrchestrationRejectsWithVersionMismatch()
///
/// The value to use for
///
- [DataTestMethod]
+ [TestMethod]
[DataRow(true)]
[DataRow(false)]
public async Task WorkerAttemptingToUpdateInstanceTableAfterStalling(bool useInstanceEtag)
@@ -3541,7 +3558,7 @@ await service.CreateTaskOrchestrationAsync(
if (useInstanceEtag)
{
// Confirm an exception is thrown due to the etag mismatch for the instance table when the worker attempts to complete the work item
- SessionAbortedException exception = await Assert.ThrowsExceptionAsync(async () =>
+ SessionAbortedException exception = await Assert.ThrowsExactlyAsync(async () =>
await service.CompleteTaskOrchestrationWorkItemAsync(workItem, runtimeState, new List(), new List(), new List(), null, null)
);
Assert.IsInstanceOfType(exception.InnerException, typeof(DurableTaskStorageException));
@@ -3592,7 +3609,7 @@ await service.CreateTaskOrchestrationAsync(
///
/// The value to use for
///
- [DataTestMethod]
+ [TestMethod]
[DataRow(true)]
[DataRow(false)]
public async Task WorkerAttemptingToUpdateInstanceTableAfterStallingForSubOrchestration(bool useInstanceEtag)
@@ -3699,7 +3716,7 @@ await service.CreateTaskOrchestrationAsync(
{
// Confirm an exception is thrown because the worker attempts to insert a new entity for the suborchestration into the instance table
// when one already exists
- SessionAbortedException exception = await Assert.ThrowsExceptionAsync(async () =>
+ SessionAbortedException exception = await Assert.ThrowsExactlyAsync(async () =>
await service.CompleteTaskOrchestrationWorkItemAsync(workItem, runtimeState, new List(), new List(), new List(), null, null)
);
Assert.IsInstanceOfType(exception.InnerException, typeof(DurableTaskStorageException));
@@ -3733,7 +3750,7 @@ await service.CreateTaskOrchestrationAsync(
}
}
- [DataTestMethod]
+ [TestMethod]
[DataRow(true)]
[DataRow(false)]
public async Task WorkerAttemptingToDequeueMessageForNonExistentInstance(bool extendedSessionsEnabled)
@@ -3785,7 +3802,7 @@ await service.SendTaskOrchestrationMessageAsync(
}
}
- [DataTestMethod]
+ [TestMethod]
[DataRow(true, true)]
[DataRow(false, true)]
[DataRow(true, false)]
@@ -3878,7 +3895,7 @@ await service.SendTaskOrchestrationMessageAsync(
}
}
- [DataTestMethod]
+ [TestMethod]
[DataRow(true, true)]
[DataRow(false, true)]
[DataRow(true, false)]
@@ -3991,7 +4008,7 @@ await service.SendTaskOrchestrationMessageAsync(
/// End-to-end test which validates a simple orchestrator function that calls an activity function
/// and checks the OpenTelemetry trace information
///
- [DataTestMethod]
+ [TestMethod]
[DataRow(true)]
[DataRow(false)]
public async Task OpenTelemetry_SayHelloWithActivity(bool enableExtendedSessions)
@@ -4061,7 +4078,7 @@ public async Task OpenTelemetry_SayHelloWithActivity(bool enableExtendedSessions
/// End-to-end test which validates a simple orchestrator function that waits for an external event
/// raised through the RaiseEvent API and checks the OpenTelemetry trace information
///
- [DataTestMethod]
+ [TestMethod]
[DataRow(true)]
[DataRow(false)]
public async Task OpenTelemetry_ExternalEvent_RaiseEvent(bool enableExtendedSessions)
@@ -4131,7 +4148,7 @@ public async Task OpenTelemetry_ExternalEvent_RaiseEvent(bool enableExtendedSess
///
/// End-to-end test which validates a simple orchestrator function that fires a timer and checks the OpenTelemetry trace information
///
- [DataTestMethod]
+ [TestMethod]
[DataRow(true)]
[DataRow(false)]
public async Task OpenTelemetry_TimerFired(bool enableExtendedSessions)
@@ -4198,7 +4215,7 @@ public async Task OpenTelemetry_TimerFired(bool enableExtendedSessions)
/// End-to-end test which validates a simple orchestrator function that waits for an external event
/// raised by calling SendEvent and checks the OpenTelemetry trace information
///
- [DataTestMethod]
+ [TestMethod]
[DataRow(true)]
[DataRow(false)]
public async Task OpenTelemetry_ExternalEvent_SendEvent(bool enableExtendedSessions)
diff --git a/test/DurableTask.AzureStorage.Tests/AzureTableQueryFilterTests.cs b/test/DurableTask.AzureStorage.Tests/AzureTableQueryFilterTests.cs
index b9a41b5c5..694d627af 100644
--- a/test/DurableTask.AzureStorage.Tests/AzureTableQueryFilterTests.cs
+++ b/test/DurableTask.AzureStorage.Tests/AzureTableQueryFilterTests.cs
@@ -21,7 +21,7 @@ public class AzureTableQueryFilterTests
{
// PartitionKeyEquals applies KeySanitation.EscapePartitionKey (storage-key characters) and then
// OData quote-escaping (single quotes doubled).
- [DataTestMethod]
+ [TestMethod]
[DataRow("instance1", "PartitionKey eq 'instance1'")]
[DataRow("inst'ance", "PartitionKey eq 'inst''ance'")]
[DataRow("in#st'ance", "PartitionKey eq 'in^2st''ance'")]
@@ -31,7 +31,7 @@ public void PartitionKeyEquals(string instanceId, string expectedFilter)
}
// ColumnEquals OData-escapes the value (single quotes doubled); the column name is literal text.
- [DataTestMethod]
+ [TestMethod]
[DataRow("ExecutionId", "abc", "ExecutionId eq 'abc'")]
[DataRow("ExecutionId", "a'b", "ExecutionId eq 'a''b'")]
[DataRow("RowKey", "", "RowKey eq ''")]
@@ -40,7 +40,7 @@ public void ColumnEquals(string columnName, string value, string expectedFilter)
Assert.AreEqual(expectedFilter, AzureTableQueryFilter.ColumnEquals(columnName, value));
}
- [DataTestMethod]
+ [TestMethod]
[DataRow("prefix", "PartitionKey ge 'prefix'")]
[DataRow("pre'fix", "PartitionKey ge 'pre''fix'")]
public void PartitionKeyGreaterOrEqual(string sanitizedPartitionKey, string expectedFilter)
@@ -48,7 +48,7 @@ public void PartitionKeyGreaterOrEqual(string sanitizedPartitionKey, string expe
Assert.AreEqual(expectedFilter, AzureTableQueryFilter.PartitionKeyGreaterOrEqual(sanitizedPartitionKey));
}
- [DataTestMethod]
+ [TestMethod]
[DataRow("prefix", "PartitionKey lt 'prefix'")]
[DataRow("pre'fix", "PartitionKey lt 'pre''fix'")]
public void PartitionKeyLessThan(string sanitizedPartitionKey, string expectedFilter)
diff --git a/test/DurableTask.AzureStorage.Tests/Correlation/CorrelationScenarioTest.cs b/test/DurableTask.AzureStorage.Tests/Correlation/CorrelationScenarioTest.cs
index 91643511d..758c76745 100644
--- a/test/DurableTask.AzureStorage.Tests/Correlation/CorrelationScenarioTest.cs
+++ b/test/DurableTask.AzureStorage.Tests/Correlation/CorrelationScenarioTest.cs
@@ -31,7 +31,7 @@ namespace DurableTask.AzureStorage.Tests.Correlation
[TestClass]
public class CorrelationScenarioTest
{
- [DataTestMethod]
+ [TestMethod]
[DataRow(Protocol.W3CTraceContext, false)]
[DataRow(Protocol.HttpCorrelationProtocol, false)]
[DataRow(Protocol.W3CTraceContext, true)]
@@ -78,7 +78,7 @@ protected override string Execute(TaskContext context, string input)
}
}
- [DataTestMethod]
+ [TestMethod]
[DataRow(Protocol.W3CTraceContext, false)]
[DataRow(Protocol.HttpCorrelationProtocol, false)]
[DataRow(Protocol.W3CTraceContext, true)]
@@ -113,7 +113,7 @@ public async Task SingleOrchestrationWithThrowingExceptionAsync(Protocol protoco
);
}
- [DataTestMethod]
+ [TestMethod]
[DataRow(Protocol.W3CTraceContext, false)]
[DataRow(Protocol.HttpCorrelationProtocol, false)]
[DataRow(Protocol.W3CTraceContext, true)]
@@ -173,7 +173,7 @@ protected override async Task ExecuteAsync(TaskContext context, string i
}
}
- [DataTestMethod]
+ [TestMethod]
[DataRow(Protocol.W3CTraceContext, false)]
[DataRow(Protocol.HttpCorrelationProtocol, false)]
[DataRow(Protocol.W3CTraceContext, true)]
@@ -217,7 +217,7 @@ public override Task RunTask(OrchestrationContext context, string input)
}
}
- [DataTestMethod]
+ [TestMethod]
[DataRow(Protocol.W3CTraceContext, false)]
[DataRow(Protocol.HttpCorrelationProtocol, false)]
[DataRow(Protocol.W3CTraceContext, true)]
@@ -274,7 +274,7 @@ public override async Task RunTask(OrchestrationContext context, string
}
}
- [DataTestMethod]
+ [TestMethod]
[DataRow(Protocol.W3CTraceContext, false)]
[DataRow(Protocol.HttpCorrelationProtocol, false)]
[DataRow(Protocol.W3CTraceContext, true)]
@@ -338,7 +338,7 @@ protected override string Execute(TaskContext context, string input)
}
}
- [DataTestMethod]
+ [TestMethod]
[DataRow(Protocol.W3CTraceContext, false)]
[DataRow(Protocol.HttpCorrelationProtocol, false)]
[DataRow(Protocol.W3CTraceContext, true)]
@@ -447,7 +447,7 @@ protected override string Execute(TaskContext context, string input)
//[TestMethod] ContinueAsNew
- [DataTestMethod]
+ [TestMethod]
[DataRow(Protocol.W3CTraceContext, false)]
[DataRow(Protocol.HttpCorrelationProtocol, false)]
[DataRow(Protocol.W3CTraceContext, true)]
@@ -502,7 +502,7 @@ internal static void Reset()
}
}
- [DataTestMethod]
+ [TestMethod]
[DataRow(Protocol.W3CTraceContext, false)]
[DataRow(Protocol.HttpCorrelationProtocol, false)]
[DataRow(Protocol.W3CTraceContext, true)]
@@ -576,7 +576,7 @@ internal static void Reset()
}
}
- [DataTestMethod]
+ [TestMethod]
[DataRow(Protocol.W3CTraceContext, false)]
[DataRow(Protocol.HttpCorrelationProtocol, false)]
[DataRow(Protocol.W3CTraceContext, true)]
@@ -718,7 +718,7 @@ internal static void Reset()
}
}
- [DataTestMethod]
+ [TestMethod]
[DataRow(Protocol.W3CTraceContext, false)]
[DataRow(Protocol.HttpCorrelationProtocol, false)]
[DataRow(Protocol.W3CTraceContext, true)]
diff --git a/test/DurableTask.AzureStorage.Tests/Correlation/StringExtensionsTest.cs b/test/DurableTask.AzureStorage.Tests/Correlation/StringExtensionsTest.cs
index 164e7ebc0..2ba468c19 100644
--- a/test/DurableTask.AzureStorage.Tests/Correlation/StringExtensionsTest.cs
+++ b/test/DurableTask.AzureStorage.Tests/Correlation/StringExtensionsTest.cs
@@ -35,7 +35,7 @@ public void TestParseTraceParent()
public void TestParseTraceParentThrowsException()
{
string wrongTraceparentString = "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7";
- Assert.ThrowsException(
+ Assert.ThrowsExactly(
() => { wrongTraceparentString.ToTraceParent(); });
}
diff --git a/test/DurableTask.AzureStorage.Tests/DurableTask.AzureStorage.Tests.csproj b/test/DurableTask.AzureStorage.Tests/DurableTask.AzureStorage.Tests.csproj
index c1b403fe6..7fd49db21 100644
--- a/test/DurableTask.AzureStorage.Tests/DurableTask.AzureStorage.Tests.csproj
+++ b/test/DurableTask.AzureStorage.Tests/DurableTask.AzureStorage.Tests.csproj
@@ -10,13 +10,10 @@
-
+
+
diff --git a/test/DurableTask.AzureStorage.Tests/KeySanitationTests.cs b/test/DurableTask.AzureStorage.Tests/KeySanitationTests.cs
index 326ffc1e3..d631241ac 100644
--- a/test/DurableTask.AzureStorage.Tests/KeySanitationTests.cs
+++ b/test/DurableTask.AzureStorage.Tests/KeySanitationTests.cs
@@ -24,7 +24,7 @@ namespace DurableTask.AzureStorage.Tests
[TestClass]
public class KeySanitationTests
{
- [DataTestMethod]
+ [TestMethod]
[DataRow("\r")]
[DataRow("")]
[DataRow("hello")]
diff --git a/test/DurableTask.AzureStorage.Tests/MessageManagerTests.cs b/test/DurableTask.AzureStorage.Tests/MessageManagerTests.cs
index 008a9eac7..e16d13526 100644
--- a/test/DurableTask.AzureStorage.Tests/MessageManagerTests.cs
+++ b/test/DurableTask.AzureStorage.Tests/MessageManagerTests.cs
@@ -23,7 +23,7 @@ namespace DurableTask.AzureStorage.Tests
[TestClass]
public class MessageManagerTests
{
- [DataTestMethod]
+ [TestMethod]
[DataRow("System.Collections.Generic.Dictionary`2[[System.String, System.Private.CoreLib],[System.String, System.Private.CoreLib]]")]
[DataRow("System.Collections.Generic.Dictionary`2[[System.String, mscorlib],[System.String, mscorlib]]")]
public void DeserializesStandardTypes(string dictionaryType)
@@ -49,7 +49,7 @@ public void FailsDeserializingUnknownTypes()
var messageManager = SetupMessageManager(new KnownTypeBinder());
// When/Then
- Assert.ThrowsException(() => messageManager.DeserializeMessageData(message));
+ Assert.ThrowsExactly(() => messageManager.DeserializeMessageData(message));
}
@@ -69,7 +69,7 @@ public void DeserializesCustomTypes()
Assert.AreEqual("tagValue", startedEvent.Tags["tag1"]);
}
- [DataTestMethod]
+ [TestMethod]
[DataRow("blob.bin", "blob.bin")]
[DataRow("@#$%!", "%40%23%24%25%21")]
[DataRow("foo/bar/b@z.tar.gz", "foo/bar/b%40z.tar.gz")]
diff --git a/test/DurableTask.AzureStorage.Tests/Net/UriPathTests.cs b/test/DurableTask.AzureStorage.Tests/Net/UriPathTests.cs
index 78703d5fd..4f528070a 100644
--- a/test/DurableTask.AzureStorage.Tests/Net/UriPathTests.cs
+++ b/test/DurableTask.AzureStorage.Tests/Net/UriPathTests.cs
@@ -18,7 +18,7 @@ namespace DurableTask.AzureStorage.Net
[TestClass]
public class UriPathTests
{
- [DataTestMethod]
+ [TestMethod]
[DataRow("", "", "")]
[DataRow("", "bar/baz", "bar/baz")]
[DataRow("foo", "", "foo")]
diff --git a/test/DurableTask.AzureStorage.Tests/Storage/TableDeleteBatchParallelTests.cs b/test/DurableTask.AzureStorage.Tests/Storage/TableDeleteBatchParallelTests.cs
index 87a73edfa..b7fd9bc0b 100644
--- a/test/DurableTask.AzureStorage.Tests/Storage/TableDeleteBatchParallelTests.cs
+++ b/test/DurableTask.AzureStorage.Tests/Storage/TableDeleteBatchParallelTests.cs
@@ -250,7 +250,7 @@ public async Task DeleteBatchParallelAsync_CancellationToken_IsPropagated()
return Task.FromResult(CreateMockBatchResponse(batch.Count()));
});
- await Assert.ThrowsExceptionAsync(
+ await Assert.ThrowsExactlyAsync(
() => table.DeleteBatchParallelAsync(entities, cts.Token));
}
diff --git a/test/DurableTask.AzureStorage.Tests/StressTests.cs b/test/DurableTask.AzureStorage.Tests/StressTests.cs
index 101dc6b51..9bdbf5542 100644
--- a/test/DurableTask.AzureStorage.Tests/StressTests.cs
+++ b/test/DurableTask.AzureStorage.Tests/StressTests.cs
@@ -49,7 +49,7 @@ public void Cleanup()
/// both in the case where they all share the same instance ID and when they have unique
/// instance IDs.
///
- [DataTestMethod]
+ [TestMethod]
[DataRow(true)]
[DataRow(false)]
public async Task ConcurrentOrchestrationStarts(bool useSameInstanceId)
diff --git a/test/DurableTask.AzureStorage.Tests/TestTablePartitionManager.cs b/test/DurableTask.AzureStorage.Tests/TestTablePartitionManager.cs
index 7fdb42e04..e581c7acc 100644
--- a/test/DurableTask.AzureStorage.Tests/TestTablePartitionManager.cs
+++ b/test/DurableTask.AzureStorage.Tests/TestTablePartitionManager.cs
@@ -702,7 +702,7 @@ await WaitForConditionAsync(
// read the partition table
var results = partitionTable.ExecuteQueryAsync();
var numResults = await results.CountAsync();
- Assert.AreEqual(numResults, 1); // there should only be 1 partition
+ Assert.AreEqual(1, numResults); // there should only be 1 partition
// We want to test that worker 0 starts listening to the control queue without claiming the lease.
// Therefore, we force the table to be in a state where worker 0 is still the current owner of the partition.
@@ -716,8 +716,8 @@ await WaitForConditionAsync(
// guarantee table is corrrectly updated
results = partitionTable.ExecuteQueryAsync();
numResults = await results.CountAsync();
- Assert.AreEqual(numResults, 1); // there should only be 1 partition
- Assert.AreEqual((await results.FirstAsync()).CurrentOwner, "0"); // ensure current owner is partition "0"
+ Assert.AreEqual(1, numResults); // there should only be 1 partition
+ Assert.AreEqual("0", (await results.FirstAsync()).CurrentOwner); // ensure current owner is partition "0"
// create and start new worker with the same settings, ensure it is actively listening to the queue
worker = new TaskHubWorker(service);
diff --git a/test/DurableTask.Core.Tests/ContinueAsNewTraceBehaviorTests.cs b/test/DurableTask.Core.Tests/ContinueAsNewTraceBehaviorTests.cs
index cfa4e3966..ef76e66e0 100644
--- a/test/DurableTask.Core.Tests/ContinueAsNewTraceBehaviorTests.cs
+++ b/test/DurableTask.Core.Tests/ContinueAsNewTraceBehaviorTests.cs
@@ -61,6 +61,7 @@ public void Setup()
{
ShouldListenTo = source => source.Name == "DurableTask.Core",
Sample = (ref ActivityCreationOptions options) => ActivitySamplingResult.AllDataAndRecorded,
+ SampleUsingParentId = (ref ActivityCreationOptions options) => ActivitySamplingResult.AllDataAndRecorded,
};
ActivitySource.AddActivityListener(listener);
}
@@ -473,6 +474,7 @@ public void TraceHelper_DoesNotConsumeFreshTraceSignals_WhenProducerActivityIsSu
{
ShouldListenTo = source => source.Name == "DurableTask.Core",
Sample = (ref ActivityCreationOptions options) => ActivitySamplingResult.None,
+ SampleUsingParentId = (ref ActivityCreationOptions options) => ActivitySamplingResult.None,
};
ActivitySource.AddActivityListener(listener);
@@ -622,12 +624,12 @@ public void Context_ContinueAsNew_LastCallWins()
}
[TestMethod]
- [ExpectedException(typeof(ArgumentNullException))]
public void Context_ContinueAsNew_NullOptions_Throws()
{
var instance = new OrchestrationInstance { InstanceId = "test", ExecutionId = Guid.NewGuid().ToString() };
var context = new TestableTaskOrchestrationContext(instance, TaskScheduler.Default);
- context.ContinueAsNew(null, "input", (ContinueAsNewOptions)null!);
+ Assert.ThrowsExactly(
+ () => context.ContinueAsNew(null, "input", (ContinueAsNewOptions)null!));
}
#endregion
@@ -635,11 +637,11 @@ public void Context_ContinueAsNew_NullOptions_Throws()
#region Base class — NotSupportedException for unsupported implementations
[TestMethod]
- [ExpectedException(typeof(NotSupportedException))]
public void BaseClass_ContinueAsNewWithOptions_ThrowsNotSupported()
{
var ctx = new MinimalOrchestrationContext();
- ctx.ContinueAsNew("1.0", "input", new ContinueAsNewOptions());
+ Assert.ThrowsExactly(
+ () => ctx.ContinueAsNew("1.0", "input", new ContinueAsNewOptions()));
}
#endregion
diff --git a/test/DurableTask.Core.Tests/DispatcherMiddlewareTests.cs b/test/DurableTask.Core.Tests/DispatcherMiddlewareTests.cs
index e91b7ef84..b1ff65076 100644
--- a/test/DurableTask.Core.Tests/DispatcherMiddlewareTests.cs
+++ b/test/DurableTask.Core.Tests/DispatcherMiddlewareTests.cs
@@ -339,7 +339,7 @@ public async Task EnsureActivityDispatcherMiddlewareHasAccessToRuntimeState()
Assert.AreEqual("Value", executionContext?.OrchestrationTags?["Test"]);
}
- [DataTestMethod]
+ [TestMethod]
[DataRow(OrchestrationStatus.Completed)]
[DataRow(OrchestrationStatus.Failed)]
[DataRow(OrchestrationStatus.Terminated)]
diff --git a/test/DurableTask.Core.Tests/ExceptionHandlingIntegrationTests.cs b/test/DurableTask.Core.Tests/ExceptionHandlingIntegrationTests.cs
index d4ab393f9..675ae951a 100644
--- a/test/DurableTask.Core.Tests/ExceptionHandlingIntegrationTests.cs
+++ b/test/DurableTask.Core.Tests/ExceptionHandlingIntegrationTests.cs
@@ -49,7 +49,7 @@ public ExceptionHandlingIntegrationTests()
this.client = new TaskHubClient(service, loggerFactory: loggerFactory);
}
- [DataTestMethod]
+ [TestMethod]
[DataRow(ErrorPropagationMode.SerializeExceptions)]
[DataRow(ErrorPropagationMode.UseFailureDetails)]
public async Task CatchInvalidOperationException(ErrorPropagationMode mode)
@@ -123,7 +123,7 @@ await this.worker
Assert.AreEqual(1, retryPolicyInvokedCount);
}
- [DataTestMethod]
+ [TestMethod]
[DataRow(ErrorPropagationMode.SerializeExceptions)]
[DataRow(ErrorPropagationMode.UseFailureDetails)]
public async Task FailureDetailsOnUnhandled(ErrorPropagationMode mode)
diff --git a/test/DurableTask.Core.Tests/RetryInterceptorTests.cs b/test/DurableTask.Core.Tests/RetryInterceptorTests.cs
index eddd9b12c..d82ad53cc 100644
--- a/test/DurableTask.Core.Tests/RetryInterceptorTests.cs
+++ b/test/DurableTask.Core.Tests/RetryInterceptorTests.cs
@@ -28,10 +28,10 @@ public async Task Invoke_WithFailingRetryCall_ShouldThrowCorrectException()
var interceptor = new RetryInterceptor