@@ -85,15 +85,15 @@ cd /samples/portable-sdks/dotnet/FunctionChaining
85
85
::: zone pivot="python"
86
86
87
87
``` bash
88
- cd /samples/portable-sdks/python/FunctionChaining
88
+ cd /samples/portable-sdks/python/function-chaining
89
89
```
90
90
91
91
::: zone-end
92
92
93
93
::: zone pivot="java"
94
94
95
95
``` bash
96
- cd /samples/portable-sdks/java/FunctionChaining
96
+ cd /samples/portable-sdks/java/function-chaining
97
97
```
98
98
99
99
::: zone-end
@@ -201,20 +201,40 @@ In the Azure portal, verify the orchestrations are running successfully.
201
201
202
202
::: zone pivot="csharp"
203
203
204
- ### Client Project
204
+ ### Client project
205
205
206
206
The Client project:
207
207
208
208
- Uses the same connection string logic as the worker
209
- - Schedules an orchestration instance with a name input
210
- - Waits for the orchestration to complete and displays the result
211
- - Uses WaitForInstanceCompletionAsync for efficient polling
209
+ - Implements a sequential orchestration scheduler that:
210
+ - Schedules 20 orchestration instances, one at a time
211
+ - Waits 5 seconds between scheduling each orchestration
212
+ - Tracks all orchestration instances in a list
213
+ - Waits for all orchestrations to complete before exiting
214
+ - Uses standard logging to show progress and results
212
215
213
216
``` csharp
214
- var instance = await client .WaitForInstanceCompletionAsync (
215
- instanceId ,
216
- getInputsAndOutputs : true ,
217
- cts .Token );
217
+ // Schedule 20 orchestrations sequentially
218
+ for (int i = 0 ; i < TotalOrchestrations ; i ++ )
219
+ {
220
+ // Create a unique instance ID
221
+ string instanceName = $" {name }_{i + 1 }" ;
222
+
223
+ // Schedule the orchestration
224
+ string instanceId = await client .ScheduleNewOrchestrationInstanceAsync (
225
+ " GreetingOrchestration" ,
226
+ instanceName );
227
+
228
+ // Wait 5 seconds before scheduling the next one
229
+ await Task .Delay (TimeSpan .FromSeconds (IntervalSeconds ));
230
+ }
231
+
232
+ // Wait for all orchestrations to complete
233
+ foreach (string id in allInstanceIds )
234
+ {
235
+ OrchestrationMetadata instance = await client .WaitForInstanceCompletionAsync (
236
+ id , getInputsAndOutputs : false , CancellationToken .None );
237
+ }
218
238
```
219
239
220
240
### Worker Project
@@ -254,7 +274,8 @@ public class SayHelloActivity : TaskActivity<string, string>
254
274
}
255
275
```
256
276
257
- The worker uses Microsoft.Extensions.Hosting for proper lifecycle management:
277
+ The worker uses ` Microsoft.Extensions.Hosting ` for proper lifecycle management:
278
+
258
279
``` csharp
259
280
var builder = Host .CreateApplicationBuilder ();
260
281
builder .Services .AddDurableTaskWorker ()
@@ -270,89 +291,197 @@ await host.StartAsync();
270
291
271
292
::: zone pivot="python"
272
293
273
- ### Worker Project
274
-
275
- The Worker project contains:
276
-
277
- - **** : Defines the orchestrator and activity functions in a single file
278
- - **** : Sets up the worker host with proper connection string handling
294
+ ### Client
279
295
280
- #### Orchestration Implementation
296
+ The Client project:
281
297
282
- The orchestration directly calls each activity in sequence using the standard ` CallActivityAsync ` method:
298
+ - Uses the same connection string logic as the worker
299
+ - Implements a sequential orchestration scheduler that:
300
+ - Schedules 20 orchestration instances, one at a time
301
+ - Waits 5 seconds between scheduling each orchestration
302
+ - Tracks all orchestration instances in a list
303
+ - Waits for all orchestrations to complete before exiting
304
+ - Uses standard logging to show progress and results
283
305
284
306
``` python
285
-
307
+ # Schedule a new orchestration instance
308
+ instance_id = client.schedule_new_orchestration(
309
+ " function_chaining_orchestrator" ,
310
+ input = name
311
+ )
312
+
313
+ logger.info(f " Started orchestration with ID = { instance_id} " )
314
+
315
+ # Wait for orchestration to complete
316
+ logger.info(" Waiting for orchestration to complete..." )
317
+ result = client.wait_for_orchestration_completion(
318
+ instance_id,
319
+ timeout = 30
320
+ )
286
321
```
287
322
288
- Each activity is implemented as a separate class decorated with the ` [DurableTask] ` attribute:
289
-
290
- ``` python
323
+ ### Worker
291
324
292
- ```
325
+ #### Orchestration Implementation
293
326
294
- The worker uses Microsoft.Extensions.Hosting for proper lifecycle management :
327
+ The orchestration directly calls each activity in sequence using the standard ` call_activity ` function :
295
328
296
329
``` python
297
-
330
+ # Orchestrator function
331
+ def function_chaining_orchestrator (ctx , name : str ) -> str :
332
+ """ Orchestrator that demonstrates function chaining pattern."""
333
+ logger.info(f " Starting function chaining orchestration for { name} " )
334
+
335
+ # Call first activity - passing input directly without named parameter
336
+ greeting = yield ctx.call_activity(' say_hello' , input = name)
337
+
338
+ # Call second activity with the result from first activity
339
+ processed_greeting = yield ctx.call_activity(' process_greeting' , input = greeting)
340
+
341
+ # Call third activity with the result from second activity
342
+ final_response = yield ctx.call_activity(' finalize_response' , input = processed_greeting)
343
+
344
+ return final_response
298
345
```
299
346
300
- ### Client Project
347
+ Each activity is implemented as a separate function:
301
348
302
- The Client project:
349
+ ``` python
350
+ # Activity functions
351
+ def say_hello (ctx , name : str ) -> str :
352
+ """ First activity that greets the user."""
353
+ logger.info(f " Activity say_hello called with name: { name} " )
354
+ return f " Hello { name} ! "
355
+
356
+ def process_greeting (ctx , greeting : str ) -> str :
357
+ """ Second activity that processes the greeting."""
358
+ logger.info(f " Activity process_greeting called with greeting: { greeting} " )
359
+ return f " { greeting} How are you today? "
360
+
361
+ def finalize_response (ctx , response : str ) -> str :
362
+ """ Third activity that finalizes the response."""
363
+ logger.info(f " Activity finalize_response called with response: { response} " )
364
+ return f " { response} I hope you're doing well! "
365
+ ```
303
366
304
- - Uses the same connection string logic as the worker
305
- - Schedules an orchestration instance with a name input
306
- - Waits for the orchestration to complete and displays the result
307
- - Uses WaitForInstanceCompletionAsync for efficient polling
367
+ The worker uses ` DurableTaskSchedulerWorker ` for proper lifecycle management:
308
368
309
369
``` python
310
-
370
+ with DurableTaskSchedulerWorker(
371
+ host_address = host_address,
372
+ secure_channel = endpoint != " http://localhost:8080" ,
373
+ taskhub = taskhub_name,
374
+ token_credential = credential
375
+ ) as worker:
376
+
377
+ # Register activities and orchestrators
378
+ worker.add_activity(say_hello)
379
+ worker.add_activity(process_greeting)
380
+ worker.add_activity(finalize_response)
381
+ worker.add_orchestrator(function_chaining_orchestrator)
382
+
383
+ # Start the worker (without awaiting)
384
+ worker.start()
311
385
```
312
386
313
387
314
388
::: zone-end
315
389
316
390
::: zone pivot="java"
317
391
318
- ### Worker Project
319
-
320
- The Worker project contains:
321
-
322
- - **** : Defines the orchestrator and activity functions in a single file
323
- - **** : Sets up the worker host with proper connection string handling
324
-
325
- #### Orchestration Implementation
326
-
327
- The orchestration directly calls each activity in sequence using the standard ` CallActivityAsync ` method:
328
-
329
- ``` java
330
-
331
- ```
332
-
333
- Each activity is implemented as a separate class decorated with the ` [DurableTask] ` attribute:
334
-
335
- ``` java
392
+ ### Client
336
393
337
- ```
394
+ The Client project:
338
395
339
- The worker uses Microsoft.Extensions.Hosting for proper lifecycle management:
396
+ - Uses the same connection string logic as the worker
397
+ - Implements a sequential orchestration scheduler that:
398
+ - Schedules 20 orchestration instances, one at a time
399
+ - Waits 5 seconds between scheduling each orchestration
400
+ - Tracks all orchestration instances in a list
401
+ - Waits for all orchestrations to complete before exiting
402
+ - Uses standard logging to show progress and results
340
403
341
404
``` java
342
-
405
+ // Create client using Azure-managed extensions
406
+ DurableTaskClient client = DurableTaskSchedulerClientExtensions . createClientBuilder(connectionString). build();
407
+
408
+ // Start a new instance of the registered "ActivityChaining" orchestration
409
+ String instanceId = client. scheduleNewOrchestrationInstance(
410
+ " ActivityChaining" ,
411
+ new NewOrchestrationInstanceOptions (). setInput(" Hello, world!" ));
412
+ logger. info(" Started new orchestration instance: {}" , instanceId);
413
+
414
+ // Block until the orchestration completes. Then print the final status, which includes the output.
415
+ OrchestrationMetadata completedInstance = client. waitForInstanceCompletion(
416
+ instanceId,
417
+ Duration . ofSeconds(30 ),
418
+ true );
419
+ logger. info(" Orchestration completed: {}" , completedInstance);
420
+ logger. info(" Output: {}" , completedInstance. readOutputAs(String . class))
343
421
```
344
422
345
- ### Client Project
423
+ ### Worker
346
424
347
- The Client project:
425
+ #### Orchestration Implementation
348
426
349
- - Uses the same connection string logic as the worker
350
- - Schedules an orchestration instance with a name input
351
- - Waits for the orchestration to complete and displays the result
352
- - Uses WaitForInstanceCompletionAsync for efficient polling
427
+ The orchestration directly calls each activity in sequence using the standard ` callActivity ` method:
353
428
354
429
``` java
430
+ DurableTaskGrpcWorker worker = DurableTaskSchedulerWorkerExtensions . createWorkerBuilder(connectionString)
431
+ .addOrchestration(new TaskOrchestrationFactory () {
432
+ @Override
433
+ public String getName () { return " ActivityChaining" ; }
434
+
435
+ @Override
436
+ public TaskOrchestration create () {
437
+ return ctx - > {
438
+ String input = ctx. getInput(String . class);
439
+ String x = ctx. callActivity(" Reverse" , input, String . class). await();
440
+ String y = ctx. callActivity(" Capitalize" , x, String . class). await();
441
+ String z = ctx. callActivity(" ReplaceWhitespace" , y, String . class). await();
442
+ ctx. complete(z);
443
+ };
444
+ }
445
+ })
446
+ .addActivity(new TaskActivityFactory () {
447
+ @Override
448
+ public String getName () { return " Reverse" ; }
449
+
450
+ @Override
451
+ public TaskActivity create () {
452
+ return ctx - > {
453
+ String input = ctx. getInput(String . class);
454
+ StringBuilder builder = new StringBuilder (input);
455
+ builder. reverse();
456
+ return builder. toString();
457
+ };
458
+ }
459
+ })
460
+ .addActivity(new TaskActivityFactory () {
461
+ @Override
462
+ public String getName () { return " Capitalize" ; }
463
+
464
+ @Override
465
+ public TaskActivity create () {
466
+ return ctx - > ctx. getInput(String . class). toUpperCase();
467
+ }
468
+ })
469
+ .addActivity(new TaskActivityFactory () {
470
+ @Override
471
+ public String getName () { return " ReplaceWhitespace" ; }
472
+
473
+ @Override
474
+ public TaskActivity create () {
475
+ return ctx - > {
476
+ String input = ctx. getInput(String . class);
477
+ return input. trim(). replaceAll(" \\ s" , " -" );
478
+ };
479
+ }
480
+ })
481
+ .build();
355
482
483
+ // Start the worker
484
+ worker. start();
356
485
```
357
486
358
487
::: zone-end
0 commit comments