Skip to content

Commit 2ff37c5

Browse files
authored
Add new create builder overloads without callbacks (#366)
* Add new builder overload without callback * Add unit tests * Fix TaskNameTests namsespace * Update changelog * Fix tests passing null
1 parent fd86ae1 commit 2ff37c5

File tree

14 files changed

+379
-89
lines changed

14 files changed

+379
-89
lines changed

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,15 @@
11
# Changelog
22

3+
## vNext
4+
5+
### Microsoft.DurableTask.Client
6+
7+
- Add new `IDurableTaskClientBuilder AddDurableTaskClient(IServiceCollection, string?)` API
8+
9+
### Microsoft.DurableTask.Worker
10+
11+
- Add new `IDurableTaskWorkerBuilder AddDurableTaskWorker(IServiceCollection, string?)` API
12+
313
## v1.5.0
414

515
- Implement work item completion tokens for standalone worker scenarios ([#359](https://github.com/microsoft/durabletask-dotnet/pull/359))

Microsoft.DurableTask.sln

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shared", "Shared", "{CECADD
8383
EndProject
8484
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shared.AzureManaged.Tests", "test\Shared\AzureManaged.Tests\Shared.AzureManaged.Tests.csproj", "{3272C041-F81D-4C85-A4FB-2A700B5A7A9D}"
8585
EndProject
86+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConsoleAppMinimal", "samples\ConsoleAppMinimal\ConsoleAppMinimal.csproj", "{B48FACA9-A328-452A-BFAE-C4F60F9C7024}"
87+
EndProject
8688
Global
8789
GlobalSection(SolutionConfigurationPlatforms) = preSolution
8890
Debug|Any CPU = Debug|Any CPU
@@ -217,6 +219,10 @@ Global
217219
{3272C041-F81D-4C85-A4FB-2A700B5A7A9D}.Debug|Any CPU.Build.0 = Debug|Any CPU
218220
{3272C041-F81D-4C85-A4FB-2A700B5A7A9D}.Release|Any CPU.ActiveCfg = Release|Any CPU
219221
{3272C041-F81D-4C85-A4FB-2A700B5A7A9D}.Release|Any CPU.Build.0 = Release|Any CPU
222+
{B48FACA9-A328-452A-BFAE-C4F60F9C7024}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
223+
{B48FACA9-A328-452A-BFAE-C4F60F9C7024}.Debug|Any CPU.Build.0 = Debug|Any CPU
224+
{B48FACA9-A328-452A-BFAE-C4F60F9C7024}.Release|Any CPU.ActiveCfg = Release|Any CPU
225+
{B48FACA9-A328-452A-BFAE-C4F60F9C7024}.Release|Any CPU.Build.0 = Release|Any CPU
220226
EndGlobalSection
221227
GlobalSection(SolutionProperties) = preSolution
222228
HideSolutionNode = FALSE
@@ -258,6 +264,7 @@ Global
258264
{1E5C2E83-7B6B-425A-9C9B-0B887D273B12} = {51DC98A3-0193-4C66-964B-C26C748E25B6}
259265
{CECADDB5-E30A-4CE2-8604-9AC596D4A2DC} = {E5637F81-2FB9-4CD7-900D-455363B142A7}
260266
{3272C041-F81D-4C85-A4FB-2A700B5A7A9D} = {CECADDB5-E30A-4CE2-8604-9AC596D4A2DC}
267+
{B48FACA9-A328-452A-BFAE-C4F60F9C7024} = {EFF7632B-821E-4CFC-B4A0-ED4B24296B17}
261268
EndGlobalSection
262269
GlobalSection(ExtensibilityGlobals) = postSolution
263270
SolutionGuid = {AB41CB55-35EA-4986-A522-387AB3402E71}

samples/ConsoleApp/ConsoleApp.csproj

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,24 @@
22

33
<PropertyGroup>
44
<OutputType>Exe</OutputType>
5-
<TargetFramework>net6.0</TargetFramework>
5+
<TargetFramework>net8.0</TargetFramework>
66
<Nullable>enable</Nullable>
77
</PropertyGroup>
88

99
<ItemGroup>
10-
<PackageReference Include="Microsoft.Extensions.Hosting" Version="6.0.0" />
11-
<PackageReference Include="Microsoft.DurableTask.Client.Grpc" Version="1.1.0" />
12-
<PackageReference Include="Microsoft.DurableTask.Worker.Grpc" Version="1.1.0" />
10+
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.1" />
11+
12+
<!-- Real projects would use package references -->
13+
<!--
14+
<PackageReference Include="Microsoft.DurableTask.Client.Grpc" Version="1.5.0" />
15+
<PackageReference Include="Microsoft.DurableTask.Worker.Grpc" Version="1.5.0" />
16+
-->
17+
</ItemGroup>
18+
19+
<ItemGroup>
20+
<!-- Using p2p references so we can show latest changes in samples. -->
21+
<ProjectReference Include="$(SrcRoot)Client/Grpc/Client.Grpc.csproj" />
22+
<ProjectReference Include="$(SrcRoot)Worker/Grpc/Worker.Grpc.csproj" />
1323
</ItemGroup>
1424

1525
</Project>

samples/ConsoleApp/Program.cs

Lines changed: 40 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -8,68 +8,55 @@
88
using Microsoft.Extensions.DependencyInjection;
99
using Microsoft.Extensions.Hosting;
1010

11-
IHost host = Host.CreateDefaultBuilder(args)
12-
.ConfigureServices(services =>
13-
{
14-
services.AddDurableTaskClient(builder =>
15-
{
16-
// Configure options for this builder. Can be omitted if no options customization is needed.
17-
builder.Configure(opt => { });
18-
builder.UseGrpc(); // multiple overloads available for providing gRPC information
11+
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
1912

20-
// AddDurableTaskClient allows for multiple named clients by passing in a name as the first argument.
21-
// When using a non-default named client, you will need to make this call below to have the
22-
// DurableTaskClient added directly to the DI container. Otherwise IDurableTaskClientProvider must be used
23-
// to retrieve DurableTaskClients by name from the DI container. In this case, we are using the default
24-
// name, so the line below is NOT required as it was already called for us.
25-
builder.RegisterDirectly();
26-
});
13+
IDurableTaskClientBuilder clientBuilder = builder.Services.AddDurableTaskClient()
14+
.Configure(opt => { }) // configure options for this builder, if desired.
15+
.UseGrpc(); // multiple overloads available for providing gRPC information
2716

28-
services.AddDurableTaskWorker(builder =>
29-
{
30-
// Configure options for this builder. Can be omitted if no options customization is needed.
31-
builder.Configure(opt => { });
17+
// OPTIONAL STEP
18+
// AddDurableTaskClient allows for multiple named clients by passing in a name as the first argument.
19+
// When using a non-default named client, you will need to make this call below to have the
20+
// DurableTaskClient added directly to the DI container. Otherwise IDurableTaskClientProvider must be used
21+
// to retrieve DurableTaskClients by name from the DI container. In this case, we are using the default
22+
// name, so the line below is NOT required as it was already called for us.
23+
clientBuilder.RegisterDirectly();
3224

33-
// Register orchestrators and activities.
34-
builder.AddTasks(tasks =>
25+
builder.Services.AddDurableTaskWorker()
26+
.Configure(opt => { }) // configure options for this builder.
27+
.AddTasks(tasks =>
28+
{
29+
// Add tasks to the worker.
30+
tasks.AddOrchestratorFunc("HelloSequence", async context =>
31+
{
32+
var greetings = new List<string>
3533
{
36-
tasks.AddOrchestratorFunc("HelloSequence", async context =>
37-
{
38-
var greetings = new List<string>
39-
{
40-
await context.CallActivityAsync<string>("SayHello", "Tokyo"),
41-
await context.CallActivityAsync<string>("SayHello", "London"),
42-
await context.CallActivityAsync<string>("SayHello", "Seattle"),
43-
};
44-
45-
return greetings;
46-
});
47-
48-
tasks.AddActivityFunc<string, string>("SayHello", (context, city) => $"Hello {city}!");
49-
});
34+
await context.CallActivityAsync<string>("SayHello", "Tokyo"),
35+
await context.CallActivityAsync<string>("SayHello", "London"),
36+
await context.CallActivityAsync<string>("SayHello", "Seattle"),
37+
};
5038

51-
builder.UseGrpc(); // multiple overloads available for providing gRPC information
39+
return greetings;
5240
});
5341

54-
// Can also configure worker and client options through all the existing options config methods.
55-
// These are equivalent to the 'builder.Configure' calls above.
56-
services.Configure<DurableTaskWorkerOptions>(opt => { });
57-
services.Configure<DurableTaskClientOptions>(opt => { });
42+
tasks.AddActivityFunc<string, string>("SayHello", (context, city) => $"Hello {city}!");
43+
})
44+
.UseGrpc(); // multiple overloads available for providing gRPC information
5845

59-
// Registry can also be done via options pattern. This is equivalent to the 'builder.AddTasks' call above.
60-
// You can use all the tools options pattern has available. For example, if you have multiple workers you could
61-
// use ConfigureAll<DurableTaskRegistry> to add tasks to ALL workers in one go. Otherwise, you need to use
62-
// named option configuration to register to specific workers (where the name matches the name passed to
63-
// 'AddDurableTaskWorker(name?, builder)').
64-
services.Configure<DurableTaskRegistry>(registry => { });
46+
// OPTIONAL STEP
47+
// Client and Worker options can also be configured through the options pattern.
48+
// When using the options pattern, configure with the same name as the builder.
49+
builder.Services.Configure<DurableTaskClientOptions>(opt => { });
50+
builder.Services.Configure<DurableTaskWorkerOptions>(opt => { });
51+
builder.Services.Configure<DurableTaskRegistry>(registry => { });
6552

66-
// You can configure custom data converter multiple ways. One is through worker/client options configuration.
67-
// Alternatively, data converter will be used from the service provider if available (as a singleton) AND no
68-
// converter was explicitly set on the options.
69-
services.AddSingleton<DataConverter>(JsonDataConverter.Default);
70-
})
71-
.Build();
53+
// OPTIONAL STEP
54+
// You can configure custom data converter multiple ways. One is through worker/client options configuration.
55+
// Alternatively, data converter will be used from the service provider if available (as a singleton) AND no
56+
// converter was explicitly set on the options.
57+
builder.Services.AddSingleton<DataConverter>(JsonDataConverter.Default);
7258

59+
IHost host = builder.Build();
7360
await host.StartAsync();
7461

7562
await using DurableTaskClient client = host.Services.GetRequiredService<DurableTaskClient>();
@@ -82,4 +69,4 @@ await context.CallActivityAsync<string>("SayHello", "Seattle"),
8269
getInputsAndOutputs: true,
8370
cts.Token);
8471

85-
Console.WriteLine($"Instance completed: {instance}");
72+
Console.WriteLine($"Instance completed: {instance}");
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFramework>net8.0</TargetFramework>
6+
<Nullable>enable</Nullable>
7+
</PropertyGroup>
8+
9+
<ItemGroup>
10+
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.1" />
11+
12+
<!-- Real projects would use package references -->
13+
<!--
14+
<PackageReference Include="Microsoft.DurableTask.Client.Grpc" Version="1.5.0" />
15+
<PackageReference Include="Microsoft.DurableTask.Worker.Grpc" Version="1.5.0" />
16+
-->
17+
</ItemGroup>
18+
19+
<ItemGroup>
20+
<!-- Using p2p references so we can show latest changes in samples. -->
21+
<ProjectReference Include="$(SrcRoot)Client/Grpc/Client.Grpc.csproj" />
22+
<ProjectReference Include="$(SrcRoot)Worker/Grpc/Worker.Grpc.csproj" />
23+
</ItemGroup>
24+
25+
</Project>
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
// This app differs from samples/ConsoleApp in that we show the absolute minimum code needed to run a Durable Task application.
5+
6+
using ConsoleAppMinimal;
7+
using Microsoft.DurableTask.Client;
8+
using Microsoft.DurableTask.Worker;
9+
using Microsoft.Extensions.Hosting;
10+
11+
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
12+
13+
builder.Services.AddDurableTaskClient().UseGrpc();
14+
builder.Services.AddDurableTaskWorker()
15+
.AddTasks(tasks =>
16+
{
17+
tasks.AddOrchestrator<HelloSequenceOrchestrator>();
18+
tasks.AddActivity<SayHelloActivity>();
19+
})
20+
.UseGrpc();
21+
22+
IHost host = builder.Build();
23+
await host.StartAsync();

samples/ConsoleAppMinimal/Tasks.cs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
using Microsoft.DurableTask;
5+
6+
namespace ConsoleAppMinimal;
7+
8+
[DurableTask("HelloSequence")]
9+
public class HelloSequenceOrchestrator : TaskOrchestrator<string, IEnumerable<string>>
10+
{
11+
public override async Task<IEnumerable<string>> RunAsync(TaskOrchestrationContext context, string input)
12+
{
13+
IEnumerable<string> greetings =
14+
[
15+
await context.CallActivityAsync<string>("SayHello", "Tokyo"),
16+
await context.CallActivityAsync<string>("SayHello", "London"),
17+
await context.CallActivityAsync<string>("SayHello", "Seattle"),
18+
];
19+
20+
return greetings;
21+
}
22+
}
23+
24+
[DurableTask("SayHello")]
25+
public class SayHelloActivity : TaskActivity<string, string>
26+
{
27+
public override Task<string> RunAsync(TaskActivityContext context, string city)
28+
{
29+
return Task.FromResult($"Hello {city}!");
30+
}
31+
}

src/Client/Core/DependencyInjection/ServiceCollectionExtensions.cs

Lines changed: 34 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,20 @@ namespace Microsoft.DurableTask.Client;
1212
/// </summary>
1313
public static class ServiceCollectionExtensions
1414
{
15+
/// <summary>
16+
/// Adds and configures Durable Task worker services to the service collection.
17+
/// </summary>
18+
/// <param name="services">The service collection to add to.</param>
19+
/// <param name="name">The name of the builder to add.</param>
20+
/// <returns>The builder used to configured the <see cref="DurableTaskClient"/>.</returns>
21+
public static IDurableTaskClientBuilder AddDurableTaskClient(this IServiceCollection services, string? name = null)
22+
{
23+
Check.NotNull(services);
24+
IDurableTaskClientBuilder builder = GetBuilder(services, name ?? Options.DefaultName, out bool added);
25+
ConditionalConfigureBuilder(services, builder, added);
26+
return builder;
27+
}
28+
1529
/// <summary>
1630
/// Configures and adds a <see cref="DurableTaskClient" /> to the service collection.
1731
/// </summary>
@@ -37,25 +51,31 @@ public static IServiceCollection AddDurableTaskClient(
3751
services.TryAddSingleton<IDurableTaskClientProvider, DefaultDurableTaskClientProvider>();
3852
IDurableTaskClientBuilder builder = GetBuilder(services, name, out bool added);
3953
configure.Invoke(builder);
54+
ConditionalConfigureBuilder(services, builder, added);
55+
return services;
56+
}
4057

41-
if (added)
58+
static void ConditionalConfigureBuilder(
59+
IServiceCollection services, IDurableTaskClientBuilder builder, bool configure)
60+
{
61+
if (!configure)
4262
{
43-
// The added toggle logic is because we cannot use TryAddEnumerable logic as
44-
// we would have to dynamically compile a lambda to have it work correctly.
45-
ConfigureDurableOptions(services, name);
63+
return;
64+
}
4665

47-
// We do not want to register DurableTaskClient type directly so we can keep a max of 1 DurableTaskClients
48-
// registered, allowing for direct-DI of the default client.
49-
services.AddSingleton(sp => new DefaultDurableTaskClientProvider.ClientContainer(builder.Build(sp)));
66+
// The added toggle logic is because we cannot use TryAddEnumerable logic as
67+
// we would have to dynamically compile a lambda to have it work correctly.
68+
ConfigureDurableOptions(services, builder.Name);
5069

51-
if (name == Options.DefaultName)
52-
{
53-
// If we have the default options name here, we will inject this client directly.
54-
builder.RegisterDirectly();
55-
}
56-
}
70+
// We do not want to register DurableTaskClient type directly so we can keep a max of 1 DurableTaskClients
71+
// registered, allowing for direct-DI of the default client.
72+
services.AddSingleton(sp => new DefaultDurableTaskClientProvider.ClientContainer(builder.Build(sp)));
5773

58-
return services;
74+
if (builder.Name == Options.DefaultName)
75+
{
76+
// If we have the default options name here, we will inject this client directly.
77+
builder.RegisterDirectly();
78+
}
5979
}
6080

6181
static IServiceCollection ConfigureDurableOptions(IServiceCollection services, string name)

src/Client/Core/RELEASENOTES.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
- Fix filter not being passed along in `PurgeAllInstancesAsync` (https://github.com/microsoft/durabletask-dotnet/pull/289)
1+
- Add new `IDurableTaskClientBuilder AddDurableTaskClient(IServiceCollection, string?)` API

0 commit comments

Comments
 (0)