Skip to content

Commit 3754340

Browse files
committed
Merge branch 'main' into mediator
2 parents e945f9e + 7891118 commit 3754340

26 files changed

+261
-155
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,21 @@
33
## v1.0.0
44

55
- Added `SuspendInstanceAsync` and `ResumeInstanceAsync` to `DurableTaskClient`.
6+
- Rename `DurableTaskClient` methods
7+
- `TerminateAsync` -> `TerminateInstanceAsync`
8+
- `PurgeInstanceMetadataAsync` -> `PurgeInstanceAsync`
9+
- `PurgeInstances` -> `PurgeAllInstancesAsync`
10+
- `GetInstanceMetadataAsync` -> `GetInstanceAsync`
11+
- `GetInstances` -> `GetAllInstancesAsync`
612
- `TaskOrchestrationContext.CreateReplaySafeLogger` now creates `ILogger` directly (as opposed to wrapping an existing `ILogger`).
713
- Durable Functions class-based syntax now resolves `ITaskActivity` instances from `IServiceProvider`, if available there.
814
- `DurableTaskClient` methods have been touched up to ensure `CancellationToken` is included, as well as is the last parameter.
915
- Removed obsolete/unimplemented local lambda activity calls from `TaskOrchestrationContext`
1016
- Input is now an optional parameter on `TaskOrchestrationContext.ContinueAsNew`
1117
- Multi-target gRPC projects to now use `Grpc.Net.Client` when appropriate (.NET6.0 and up)
1218

19+
*Note: `Microsoft.DurableTask.Generators` is remaining as `preview.1`.*
20+
1321
## v1.0.0-rc.1
1422

1523
### Included Packages

README.md

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,11 @@
33
[![Build status](https://github.com/microsoft/durabletask-dotnet/workflows/Validate%20Build/badge.svg)](https://github.com/microsoft/durabletask-dotnet/actions?workflow=Validate+Build)
44
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
55

6-
⚠ This project is not yet ready for production use ⚠
7-
86
The Durable Task .NET Client SDK is a .NET Standard 2.0 library for implementing Durable Task orchestrations and activities. It's specifically designed to connect to a "sidecar" process, such as the [Azure Functions .NET Isolated host](https://docs.microsoft.com/azure/azure-functions/dotnet-isolated-process-guide), a special purpose sidecar container, or potentially even [Dapr](https://github.com/dapr/dapr/issues/4576).
97

108
If you're looking to run fully self-hosted Durable Task Framework apps, see https://github.com/azure/durabletask.
119

12-
*Current Release*: [v1.0.0-rc.1](https://github.com/microsoft/durabletask-dotnet/releases/tag/v1.0.0-rc.1)
10+
*Current Release*: [v1.0.0](https://github.com/microsoft/durabletask-dotnet/releases/tag/v1.0.0)
1311

1412
## NuGet packages
1513

@@ -34,7 +32,7 @@ To get started, add the [Microsoft.Azure.Functions.Worker.Extensions.DurableTask
3432
```xml
3533
<ItemGroup>
3634
<PackageReference Include="Microsoft.Azure.Functions.Worker" Version="1.10.0" />
37-
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.DurableTask" Version="1.0.0-rc.1" />
35+
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.DurableTask" Version="1.0.0" />
3836
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Http" Version="3.0.13" />
3937
<PackageReference Include="Microsoft.Azure.Functions.Worker.Sdk" Version="1.7.0" OutputItemType="Analyzer" />
4038
<PackageReference Include="Microsoft.DurableTask.Generators" Version="1.0.0-preview.1" OutputItemType="Analyzer" />
@@ -47,6 +45,7 @@ You can then use the following code to define a simple "Hello, cities" durable o
4745
using Microsoft.Azure.Functions.Worker;
4846
using Microsoft.Azure.Functions.Worker.Http;
4947
using Microsoft.DurableTask;
48+
using Microsoft.DurableTask.Client;
5049
using Microsoft.Extensions.Logging;
5150

5251
namespace IsolatedFunctionApp1.Untyped;
@@ -56,15 +55,15 @@ static class HelloSequenceUntyped
5655
[Function(nameof(StartHelloCitiesUntyped))]
5756
public static async Task<HttpResponseData> StartHelloCitiesUntyped(
5857
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")] HttpRequestData req,
59-
[DurableClient] DurableClientContext durableContext,
58+
[DurableClient] DurableTaskClient client,
6059
FunctionContext executionContext)
6160
{
6261
ILogger logger = executionContext.GetLogger(nameof(StartHelloCitiesUntyped));
6362

64-
string instanceId = await durableContext.Client.ScheduleNewOrchestrationInstanceAsync(nameof(HelloCitiesUntyped));
63+
string instanceId = await client.ScheduleNewOrchestrationInstanceAsync(nameof(HelloCitiesUntyped));
6564
logger.LogInformation("Created new orchestration with instance ID = {instanceId}", instanceId);
6665

67-
return durableContext.CreateCheckStatusResponse(req, instanceId);
66+
return client.CreateCheckStatusResponse(req, instanceId);
6867
}
6968

7069
[Function(nameof(HelloCitiesUntyped))]
@@ -101,6 +100,7 @@ The source generators also generate type-safe extension methods on the `client`
101100
using Microsoft.Azure.Functions.Worker;
102101
using Microsoft.Azure.Functions.Worker.Http;
103102
using Microsoft.DurableTask;
103+
using Microsoft.DurableTask.Client;
104104
using Microsoft.Extensions.Logging;
105105

106106
namespace IsolatedFunctionApp1.Typed;
@@ -110,15 +110,15 @@ public static class HelloCitiesTypedStarter
110110
[Function(nameof(StartHelloCitiesTyped))]
111111
public static async Task<HttpResponseData> StartHelloCitiesTyped(
112112
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")] HttpRequestData req,
113-
[DurableClient] DurableClientContext durableContext,
113+
[DurableClient] DurableTaskClient client,
114114
FunctionContext executionContext)
115115
{
116116
ILogger logger = executionContext.GetLogger(nameof(StartHelloCitiesTyped));
117117

118-
string instanceId = await durableContext.Client.ScheduleNewHelloCitiesTypedInstanceAsync();
118+
string instanceId = await client.ScheduleNewHelloCitiesTypedInstanceAsync();
119119
logger.LogInformation("Created new orchestration with instance ID = {instanceId}", instanceId);
120120

121-
return durableContext.CreateCheckStatusResponse(req, instanceId);
121+
return client.CreateCheckStatusResponse(req, instanceId);
122122
}
123123
}
124124

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
using Microsoft.Azure.Functions.Worker;
5+
using Microsoft.Azure.Functions.Worker.Http;
6+
using Microsoft.DurableTask;
7+
using Microsoft.DurableTask.Client;
8+
using Microsoft.Extensions.Logging;
9+
10+
namespace Company.Function
11+
{
12+
public static class AzureFunctionsApp
13+
{
14+
[Function(nameof(AzureFunctionsApp))]
15+
public static async Task<List<string>> RunOrchestrator(
16+
[OrchestrationTrigger] TaskOrchestrationContext context)
17+
{
18+
ILogger logger = context.CreateReplaySafeLogger(nameof(AzureFunctionsApp));
19+
logger.LogInformation("Saying hello.");
20+
var outputs = new List<string>();
21+
22+
// Replace name and input with values relevant for your Durable Functions Activity
23+
outputs.Add(await context.CallActivityAsync<string>(nameof(SayHello), "Tokyo"));
24+
outputs.Add(await context.CallActivityAsync<string>(nameof(SayHello), "Seattle"));
25+
outputs.Add(await context.CallActivityAsync<string>(nameof(SayHello), "London"));
26+
27+
// returns ["Hello Tokyo!", "Hello Seattle!", "Hello London!"]
28+
return outputs;
29+
}
30+
31+
[Function(nameof(SayHello))]
32+
public static string SayHello([ActivityTrigger] string name, FunctionContext executionContext)
33+
{
34+
ILogger logger = executionContext.GetLogger("SayHello");
35+
logger.LogInformation($"Saying hello to {name}.");
36+
return $"Hello {name}!";
37+
}
38+
39+
[Function("AzureFunctionsApp_HttpStart")]
40+
public static async Task<HttpResponseData> HttpStart(
41+
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")] HttpRequestData req,
42+
[DurableClient] DurableTaskClient client,
43+
FunctionContext executionContext)
44+
{
45+
ILogger logger = executionContext.GetLogger("AzureFunctionsApp_HttpStart");
46+
47+
// Function input comes from the request content.
48+
string instanceId = await client
49+
.ScheduleNewOrchestrationInstanceAsync(nameof(AzureFunctionsApp));
50+
51+
logger.LogInformation($"Started orchestration with ID = '{instanceId}'.");
52+
53+
// Returns an HTTP 202 response with an instance management payload.
54+
// See https://learn.microsoft.com/azure/azure-functions/durable/durable-functions-http-api#start-orchestration
55+
return client.CreateCheckStatusResponse(req, instanceId);
56+
}
57+
}
58+
}

samples/AzureFunctionsApp/AzureFunctionsApp.csproj

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
<Project Sdk="Microsoft.NET.Sdk">
2+
23
<PropertyGroup>
34
<TargetFramework>net6.0</TargetFramework>
45
<AzureFunctionsVersion>v4</AzureFunctionsVersion>
@@ -8,7 +9,7 @@
89

910
<ItemGroup>
1011
<PackageReference Include="Microsoft.Azure.Functions.Worker" Version="1.10.0" />
11-
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.DurableTask" Version="1.0.0-rc.1" />
12+
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.DurableTask" Version="1.0.0" />
1213
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Http" Version="3.0.13" />
1314
<PackageReference Include="Microsoft.Azure.Functions.Worker.Sdk" Version="1.7.0" OutputItemType="Analyzer" />
1415
<PackageReference Include="Microsoft.DurableTask.Generators" Version="1.0.0-preview.1" OutputItemType="Analyzer" />

samples/AzureFunctionsApp/Fib.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using Microsoft.Azure.Functions.Worker;
55
using Microsoft.Azure.Functions.Worker.Http;
66
using Microsoft.DurableTask;
7+
using Microsoft.DurableTask.Client;
78
using Microsoft.Extensions.Logging;
89

910
namespace AzureFunctionsApp;
@@ -23,17 +24,17 @@ static class Fib
2324
[Function(nameof(Fib))]
2425
public static async Task<HttpResponseData> Start(
2526
[HttpTrigger(AuthorizationLevel.Anonymous, "post")] HttpRequestData req,
26-
[DurableClient] DurableClientContext durableContext,
27+
[DurableClient] DurableTaskClient client,
2728
FunctionContext executionContext)
2829
{
2930
ILogger logger = executionContext.GetLogger(nameof(Fib));
3031

3132
int? payload = await req.ReadFromJsonAsync<int>();
32-
string instanceId = await durableContext.Client
33+
string instanceId = await client
3334
.ScheduleNewOrchestrationInstanceAsync(nameof(FibOrchestration), payload);
3435
logger.LogInformation("Created new orchestration with instance ID = {instanceId}", instanceId);
3536

36-
return durableContext.CreateCheckStatusResponse(req, instanceId);
37+
return client.CreateCheckStatusResponse(req, instanceId);
3738
}
3839

3940
[Function(nameof(FibOrchestration))]

samples/AzureFunctionsApp/HelloCitiesTyped.cs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using Microsoft.Azure.Functions.Worker;
66
using Microsoft.Azure.Functions.Worker.Http;
77
using Microsoft.Extensions.Logging;
8+
using Microsoft.DurableTask.Client;
89

910
namespace AzureFunctionsApp.Typed;
1011

@@ -14,13 +15,13 @@ public static class HelloCitiesTypedStarter
1415
/// HTTP-triggered function that starts the <see cref="HelloCitiesTyped"/> orchestration.
1516
/// </summary>
1617
/// <param name="req">The HTTP request that was used to trigger this function.</param>
17-
/// <param name="durableContext">The Durable Functions client binding context object that is used to start and manage orchestration instances.</param>
18+
/// <param name="client">The Durable Functions client that is used to start and manage orchestration instances.</param>
1819
/// <param name="executionContext">The Azure Functions execution context, which is available to all function types.</param>
1920
/// <returns>Returns an HTTP response with more information about the started orchestration instance.</returns>
2021
[Function(nameof(StartHelloCitiesTyped))]
2122
public static async Task<HttpResponseData> StartHelloCitiesTyped(
2223
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")] HttpRequestData req,
23-
[DurableClient] DurableClientContext durableContext,
24+
[DurableClient] DurableTaskClient client,
2425
FunctionContext executionContext)
2526
{
2627
ILogger logger = executionContext.GetLogger(nameof(StartHelloCitiesTyped));
@@ -30,9 +31,9 @@ public static async Task<HttpResponseData> StartHelloCitiesTyped(
3031
// are based on the names of the orchestrator classes. Note that the source generator will *not*
3132
// generate type-safe extension methods for non-class-based orchestrator functions.
3233
// NOTE: This feature is in PREVIEW and requires a package reference to Microsoft.DurableTask.Generators.
33-
string instanceId = await durableContext.Client.ScheduleNewHelloCitiesTypedInstanceAsync();
34+
string instanceId = await client.ScheduleNewHelloCitiesTypedInstanceAsync();
3435
logger.LogInformation("Created new orchestration with instance ID = {instanceId}", instanceId);
35-
return durableContext.CreateCheckStatusResponse(req, instanceId);
36+
return client.CreateCheckStatusResponse(req, instanceId);
3637
}
3738
}
3839

samples/AzureFunctionsApp/HelloCitiesUntyped.cs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using Microsoft.Azure.Functions.Worker;
55
using Microsoft.Azure.Functions.Worker.Http;
66
using Microsoft.DurableTask;
7+
using Microsoft.DurableTask.Client;
78
using Microsoft.Extensions.Logging;
89

910
namespace AzureFunctionsApp.Untyped;
@@ -19,21 +20,21 @@ static class HelloSequenceUntyped
1920
/// HTTP-triggered function that starts the <see cref="HelloCitiesUntyped"/> orchestration.
2021
/// </summary>
2122
/// <param name="req">The HTTP request that was used to trigger this function.</param>
22-
/// <param name="durableContext">The Durable Functions client binding context object that is used to start and manage orchestration instances.</param>
23+
/// <param name="client">The Durable Functions client that is used to start and manage orchestration instances.</param>
2324
/// <param name="executionContext">The Azure Functions execution context, which is available to all function types.</param>
2425
/// <returns>Returns an HTTP response with more information about the started orchestration instance.</returns>
2526
[Function(nameof(StartHelloCitiesUntyped))]
2627
public static async Task<HttpResponseData> StartHelloCitiesUntyped(
2728
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")] HttpRequestData req,
28-
[DurableClient] DurableClientContext durableContext,
29+
[DurableClient] DurableTaskClient client,
2930
FunctionContext executionContext)
3031
{
3132
ILogger logger = executionContext.GetLogger(nameof(StartHelloCitiesUntyped));
3233

33-
string instanceId = await durableContext.Client.ScheduleNewOrchestrationInstanceAsync(nameof(HelloCitiesUntyped));
34+
string instanceId = await client.ScheduleNewOrchestrationInstanceAsync(nameof(HelloCitiesUntyped));
3435
logger.LogInformation("Created new orchestration with instance ID = {instanceId}", instanceId);
3536

36-
return durableContext.CreateCheckStatusResponse(req, instanceId);
37+
return client.CreateCheckStatusResponse(req, instanceId);
3738
}
3839

3940
/// <summary>

src/Abstractions/Abstractions.csproj

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
<!-- Version info -->
1010
<PropertyGroup>
1111
<VersionPrefix>1.0.0</VersionPrefix>
12-
<VersionSuffix>rc.1</VersionSuffix>
1312
</PropertyGroup>
1413

1514
<ItemGroup>

src/Abstractions/TaskActivity.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,8 @@ public interface ITaskActivity
5151
/// Because activities only guarantee at least once execution, it's recommended that activity logic be implemented as
5252
/// idempotent whenever possible.
5353
/// </para><para>
54-
/// Activities are invoked by orchestrators using one of the <see cref="TaskOrchestrationContext.CallActivityAsync"/>
54+
/// Activities are invoked by orchestrators using one of the
55+
/// <see cref="TaskOrchestrationContext.CallActivityAsync(TaskName, object?, TaskOptions?)"/>
5556
/// method overloads. Activities that derive from <see cref="TaskActivity{TInput, TOutput}"/> can also be invoked
5657
/// using generated extension methods. To participate in code generation, an activity class must be decorated with the
5758
/// <see cref="DurableTaskAttribute"/> attribute. The source generator will then generate a <c>CallMyActivityAsync()</c>

src/Abstractions/TaskOrchestrationContext.cs

Lines changed: 38 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -108,15 +108,30 @@ public abstract class TaskOrchestrationContext
108108
/// <see cref="TaskFailedException.FailureDetails"/> property.
109109
/// </exception>
110110
public virtual Task CallActivityAsync(TaskName name, object? input = null, TaskOptions? options = null)
111-
{
112-
return this.CallActivityAsync<object>(name, input, options);
113-
}
111+
=> this.CallActivityAsync<object>(name, input, options);
112+
113+
/// <returns>
114+
/// A task that completes when the activity completes or fails. The result of the task is the activity's return value.
115+
/// </returns>
116+
/// <inheritdoc cref="CallActivityAsync(TaskName, object?, TaskOptions?)"/>
117+
public virtual Task CallActivityAsync(TaskName name, TaskOptions options)
118+
=> this.CallActivityAsync(name, null, options);
114119

115120
/// <returns>
116121
/// A task that completes when the activity completes or fails. The result of the task is the activity's return value.
117122
/// </returns>
118-
/// <inheritdoc cref="CallActivityAsync"/>
119-
public abstract Task<T> CallActivityAsync<T>(TaskName name, object? input = null, TaskOptions? options = null);
123+
/// <typeparam name="TResult">The type into which to deserialize the activity's output.</typeparam>
124+
/// <inheritdoc cref="CallActivityAsync(TaskName, object?, TaskOptions?)"/>
125+
public virtual Task<TResult> CallActivityAsync<TResult>(TaskName name, TaskOptions options)
126+
=> this.CallActivityAsync<TResult>(name, null, options);
127+
128+
/// <returns>
129+
/// A task that completes when the activity completes or fails. The result of the task is the activity's return value.
130+
/// </returns>
131+
/// <typeparam name="TResult">The type into which to deserialize the activity's output.</typeparam>
132+
/// <inheritdoc cref="CallActivityAsync(TaskName, object?, TaskOptions?)"/>
133+
public abstract Task<TResult> CallActivityAsync<TResult>(
134+
TaskName name, object? input = null, TaskOptions? options = null);
120135

121136
/// <summary>
122137
/// Creates a durable timer that expires after the specified delay.
@@ -247,13 +262,26 @@ public async Task<T> WaitForExternalEvent<T>(string eventName, TimeSpan timeout)
247262
/// <summary>
248263
/// Executes a named sub-orchestrator and returns the result.
249264
/// </summary>
250-
/// <typeparam name="TResult">
251-
/// The type into which to deserialize the sub-orchestrator's output.
252-
/// </typeparam>
265+
/// <typeparam name="TResult">The type into which to deserialize the sub-orchestrator's output.</typeparam>
253266
/// <inheritdoc cref="CallSubOrchestratorAsync(TaskName, object?, TaskOptions?)"/>
254267
public abstract Task<TResult> CallSubOrchestratorAsync<TResult>(
255268
TaskName orchestratorName, object? input = null, TaskOptions? options = null);
256269

270+
/// <summary>
271+
/// Executes a named sub-orchestrator and returns the result.
272+
/// </summary>
273+
/// <typeparam name="TResult">The type into which to deserialize the sub-orchestrator's output.</typeparam>
274+
/// <inheritdoc cref="CallSubOrchestratorAsync(TaskName, object?, TaskOptions?)"/>
275+
public virtual Task<TResult> CallSubOrchestratorAsync<TResult>(TaskName orchestratorName, TaskOptions options)
276+
=> this.CallSubOrchestratorAsync<TResult>(orchestratorName, null, options);
277+
278+
/// <summary>
279+
/// Executes a named sub-orchestrator and returns the result.
280+
/// </summary>
281+
/// <inheritdoc cref="CallSubOrchestratorAsync(TaskName, object?, TaskOptions?)"/>
282+
public virtual Task CallSubOrchestratorAsync(TaskName orchestratorName, TaskOptions options)
283+
=> this.CallSubOrchestratorAsync(orchestratorName, null, options);
284+
257285
/// <summary>
258286
/// Executes a named sub-orchestrator.
259287
/// </summary>
@@ -296,11 +324,9 @@ public abstract Task<TResult> CallSubOrchestratorAsync<TResult>(
296324
/// The sub-orchestration failed with an unhandled exception. The details of the failure can be found in the
297325
/// <see cref="TaskFailedException.FailureDetails"/> property.
298326
/// </exception>
299-
public Task CallSubOrchestratorAsync(
327+
public virtual Task CallSubOrchestratorAsync(
300328
TaskName orchestratorName, object? input = null, TaskOptions? options = null)
301-
{
302-
return this.CallSubOrchestratorAsync<object>(orchestratorName, input, options);
303-
}
329+
=> this.CallSubOrchestratorAsync<object>(orchestratorName, input, options);
304330

305331
/// <summary>
306332
/// Restarts the orchestration with a new input and clears its history.

0 commit comments

Comments
 (0)