Skip to content

Latest commit

 

History

History

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 
 
 
 
 

README.md

Function Chaining Pattern

Description of the Sample

This sample demonstrates the function chaining pattern with the Azure Durable Task Scheduler using the .NET SDK. Function chaining is a fundamental workflow pattern where activities are executed in a sequence, with the output of one activity passed as the input to the next activity.

In this sample:

  1. The orchestrator calls the SayHelloActivity which greets the user with their name
  2. The result is passed to the ProcessGreetingActivity which adds to the greeting
  3. The result is then passed to the FinalizeResponseActivity which completes the greeting
  4. The final greeting message is returned to the client

This pattern is useful for:

  • Creating sequential workflows where steps must execute in order
  • Passing data between steps with data transformations at each step
  • Building pipelines where each activity adds value to the result
  • Multi-stage text processing, document generation, and conversational workflows

Prerequisites

  1. .NET 8 SDK or later
  2. Docker (for running the emulator) installed
  3. Azure CLI (if using a deployed Durable Task Scheduler)

Configuring Durable Task Scheduler

There are two ways to run this sample locally:

Using the Emulator (Recommended)

The emulator simulates a scheduler and taskhub in a Docker container, making it ideal for development and learning.

  1. Pull the Docker Image for the Emulator:

    docker pull mcr.microsoft.com/dts/dts-emulator:latest
  2. Run the Emulator:

    docker run --name dtsemulator -d -p 8080:8080 -p 8082:8082 mcr.microsoft.com/dts/dts-emulator:latest

Wait a few seconds for the container to be ready.

Note: The example code automatically uses the default emulator settings (endpoint: http://localhost:8080, taskhub: default). You don't need to set any environment variables.

Using a Deployed Scheduler and Taskhub in Azure

  1. Install the durable task scheduler CLI extension:

    az upgrade
    az extension add --name durabletask --allow-preview true
  2. Create a resource group in a region where the Durable Task Scheduler is available:

    az provider show --namespace Microsoft.DurableTask --query "resourceTypes[?resourceType=='schedulers'].locations | [0]" --out table
    az group create --name my-resource-group --location <location>
  3. Create a durable task scheduler resource:

    az durabletask scheduler create \
        --resource-group my-resource-group \
        --name my-scheduler \
        --ip-allowlist '["0.0.0.0/0"]' \
        --sku-name "Dedicated" \
        --sku-capacity 1 \
        --tags "{'myattribute':'myvalue'}"
  4. Create a task hub within the scheduler resource:

    az durabletask taskhub create \
        --resource-group my-resource-group \
        --scheduler-name my-scheduler \
        --name "my-taskhub"
  5. Grant the current user permission to connect to the my-taskhub task hub:

    subscriptionId=$(az account show --query "id" -o tsv)
    loggedInUser=$(az account show --query "user.name" -o tsv)
    
    az role assignment create \
        --assignee $loggedInUser \
        --role "Durable Task Data Contributor" \
        --scope "/subscriptions/$subscriptionId/resourceGroups/my-resource-group/providers/Microsoft.DurableTask/schedulers/my-scheduler/taskHubs/my-taskhub"

Authentication

The sample includes smart detection of the environment and configures authentication automatically:

  • For local development with the emulator (when endpoint is http://localhost:8080), no authentication is required.
  • For local development with a deployed scheduler, DefaultAzure authentication is used, which utilizes DefaultAzureCredential behind the scenes and tries multiple authentication methods:
    • Managed Identity
    • Environment variables (AZURE_CLIENT_ID, AZURE_TENANT_ID, AZURE_CLIENT_SECRET)
    • Azure CLI login
    • Visual Studio login
    • and more

The connection string is constructed dynamically based on the environment:

// For local emulator
connectionString = $"Endpoint={schedulerEndpoint};TaskHub={taskHubName};Authentication=None";

// For Azure deployed emulator
connectionString = $"Endpoint={schedulerEndpoint};TaskHub={taskHubName};Authentication=DefaultAzure";

How to Run the Sample

Once you have set up either the emulator or deployed scheduler, follow these steps to run the sample:

  1. If you're using a deployed scheduler, you need set Environment Variables.

    export ENDPOINT=$(az durabletask scheduler show \
        --resource-group my-resource-group \
        --name my-scheduler \
        --query "properties.endpoint" \
        --output tsv)
    
    export TASKHUB="my-taskhub"
  2. Start the worker in a terminal:

    cd samples/durable-task-sdks/dotnet/FunctionChaining/Worker
    dotnet run

    You should see output indicating the worker has started and registered the orchestration and activities.

  3. In a new terminal, run the client:

    Note: Remember to set the environment variables again if you're using a deployed scheduler.

    cd samples/durable-task-sdks/dotnet/FunctionChaining/Client
    dotnet run

Deploying with Azure Developer CLI (AZD)

This sample includes an azure.yaml configuration file that allows you to deploy the entire solution to Azure using Azure Developer CLI (AZD).

Note: This sample uses the shared infrastructure templates located at samples/infra/.

Prerequisites for AZD Deployment

  1. Install Azure Developer CLI
  2. Authenticate with Azure:
    azd auth login

Deployment Steps

  1. Navigate to the Function Chaining sample directory:

    cd samples/durable-task-sdks/dotnet/FunctionChaining
  2. Initialize the Azure Developer CLI project (only needed the first time):

    azd init

    This step prepares the environment for deployment and creates necessary configuration files.

  3. Provision resources and deploy the application:

    azd up

    This command will:

    • Provision Azure resources (including Azure Container Apps and Durable Task Scheduler)
    • Build and deploy both the Client and Worker components
    • Set up the necessary connections between components
  4. After deployment completes, AZD will display URLs for your deployed services.

  5. Monitor your orchestrations using the Azure Portal by navigating to your Durable Task Scheduler resource.

  6. To confirm the sample is working correctly, view the application logs through the Azure Portal:

    • Navigate to the Azure Portal (https://portal.azure.com)
    • Go to your resource group where the application was deployed
    • Find and select the Container Apps for both the worker and client components
    • For each Container App:
      • Click on "Log stream" in the left navigation menu under "Monitoring"
      • View the real-time logs showing orchestrations being scheduled, activities executing, and results being processed

    These logs will show the same information as when running locally, allowing you to confirm the application is working correctly.

Understanding the Code Structure

Worker Project

The Worker project contains:

  • GreetingOrchestration.cs: Defines the orchestrator and activity functions in a single file
  • Program.cs: Sets up the worker host with proper connection string handling

Orchestration Implementation

The orchestration directly calls each activity in sequence using the standard CallActivityAsync method:

public override async Task<string> RunAsync(TaskOrchestrationContext context, string name)
{
    // Step 1: Say hello to the person
    string greeting = await context.CallActivityAsync<string>(nameof(SayHelloActivity), name);
    
    // Step 2: Process the greeting
    string processedGreeting = await context.CallActivityAsync<string>(nameof(ProcessGreetingActivity), greeting);
    
    // Step 3: Finalize the response
    string finalResponse = await context.CallActivityAsync<string>(nameof(FinalizeResponseActivity), processedGreeting);
    
    return finalResponse;
}

Each activity is implemented as a separate class decorated with the [DurableTask] attribute:

[DurableTask]
public class SayHelloActivity : TaskActivity<string, string>
{
    // Implementation details
}

The worker uses Microsoft.Extensions.Hosting for proper lifecycle management:

var builder = Host.CreateApplicationBuilder();
builder.Services.AddDurableTaskWorker()
    .AddTasks(registry => {
        registry.AddAllGeneratedTasks();
    })
    .UseDurableTaskScheduler(connectionString);
var host = builder.Build();
await host.StartAsync();

Client Project

The Client project:

  • Uses the same connection string logic as the worker
  • Implements a sequential orchestration scheduler that:
    • Schedules 20 orchestration instances, one at a time
    • Waits 5 seconds between scheduling each orchestration
    • Tracks all orchestration instances in a list
    • Waits for all orchestrations to complete before exiting
  • Uses standard logging to show progress and results
// Schedule 20 orchestrations sequentially
for (int i = 0; i < TotalOrchestrations; i++)
{
    // Create a unique instance ID
    string instanceName = $"{name}_{i+1}";
    
    // Schedule the orchestration
    string instanceId = await client.ScheduleNewOrchestrationInstanceAsync(
        "GreetingOrchestration", 
        instanceName);
    
    // Wait 5 seconds before scheduling the next one
    await Task.Delay(TimeSpan.FromSeconds(IntervalSeconds));
}

// Wait for all orchestrations to complete
foreach (string id in allInstanceIds)
{
    OrchestrationMetadata instance = await client.WaitForInstanceCompletionAsync(
        id, getInputsAndOutputs: false, CancellationToken.None);
}

Understanding the Output

When you run the client, you should see:

  1. The client starting an orchestration with a name input
  2. The worker processing the chained activities
  3. The client displaying the final result from the completed orchestration

Example output:

Starting greeting orchestration for name: GitHub Copilot
Started orchestration with ID: 7f8e9a6b-1c2d-3e4f-5a6b-7c8d9e0f1a2b
Waiting for orchestration to complete...
Orchestration completed with status: Completed
Greeting result: Hello GitHub Copilot! It's nice to meet you. Welcome to the Durable Task Framework!

When you run the sample, you'll see output from both the worker and client processes:

Worker Output

The worker shows:

  • Registration of the orchestrator and activities
  • Log entries when each activity is called, showing details of the order processing
  • The progression through the chain of activities from order processing to payment to shipping to notification

Client Output

The client shows:

  • Starting the orchestration with the order details
  • The unique orchestration instance ID
  • The final result, which includes:
    • The order ID
    • Status (which should be "Completed")
    • Payment ID (randomly generated)
    • Shipping ID (randomly generated)

This demonstrates the chaining of functions in a sequence, with each function building on the state from the previous one.

Reviewing the Orchestration in the Durable Task Scheduler Dashboard

To access the Durable Task Scheduler Dashboard and review your orchestration:

Using the Emulator

  1. Navigate to http://localhost:8082 in your web browser
  2. Click on the "default" task hub
  3. You'll see the orchestration instance in the list
  4. Click on the instance ID to view the execution details, which will show:
    • The sequential execution of the four activities
    • The input and output at each step
    • The time taken for each step

Using a Deployed Scheduler

  1. Navigate to the Scheduler resource in the Azure portal
  2. Go to the Task Hub subresource that you're using
  3. Click on the dashboard URL in the top right corner
  4. Search for your orchestration instance ID
  5. Review the execution details

The dashboard visualizes the sequential nature of function chaining, making it easy to see the flow of data from one activity to the next throughout the order processing workflow.