askill
durable-task-dotnet

durable-task-dotnetSafety 95Repository

Build durable, fault-tolerant workflows in .NET using the Durable Task SDK with Azure Durable Task Scheduler. Use when creating orchestrations, activities, entities, or implementing patterns like function chaining, fan-out/fan-in, human interaction, or stateful agents. Applies to any .NET application requiring durable execution, state persistence, or distributed transactions without Azure Functions dependency.

46 stars
1.2k downloads
Updated 2/8/2026

Package Files

Loading files...
SKILL.md

Durable Task .NET SDK with Durable Task Scheduler

Build fault-tolerant, stateful workflows in .NET applications using the Durable Task SDK connected to Azure Durable Task Scheduler.

Quick Start

Required NuGet Packages

<ItemGroup>
  <PackageReference Include="Microsoft.DurableTask.Client.AzureManaged" Version="1.*" />
  <PackageReference Include="Microsoft.DurableTask.Worker.AzureManaged" Version="1.*" />
  <PackageReference Include="Microsoft.DurableTask.Generators" Version="1.*" OutputItemType="Analyzer" />
  <PackageReference Include="Azure.Identity" Version="1.*" />
  <PackageReference Include="Grpc.Net.Client" Version="2.*" />
</ItemGroup>

Minimal Worker Setup

using Microsoft.DurableTask;
using Microsoft.DurableTask.Worker;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

var builder = Host.CreateApplicationBuilder(args);

// Connection string format: "Endpoint={url};TaskHub={name};Authentication={type}"
var connectionString = Environment.GetEnvironmentVariable("DURABLE_TASK_SCHEDULER_CONNECTION_STRING") 
    ?? "Endpoint=http://localhost:8080;TaskHub=default;Authentication=None";

builder.Services.AddDurableTaskWorker()
    .AddTasks(registry =>
    {
        registry.AddAllGeneratedTasks(); // Registers all [DurableTask] decorated classes
    })
    .UseDurableTaskScheduler(connectionString);

var host = builder.Build();
await host.RunAsync();

Minimal Client Setup

using Microsoft.DurableTask.Client;

var connectionString = Environment.GetEnvironmentVariable("DURABLE_TASK_SCHEDULER_CONNECTION_STRING")
    ?? "Endpoint=http://localhost:8080;TaskHub=default;Authentication=None";

var client = DurableTaskClientBuilder.UseDurableTaskScheduler(connectionString).Build();

// Schedule an orchestration
string instanceId = await client.ScheduleNewOrchestrationInstanceAsync("MyOrchestration", input);

// Wait for completion
var result = await client.WaitForInstanceCompletionAsync(instanceId, getInputsAndOutputs: true);

Pattern Selection Guide

PatternUse When
Function ChainingSequential steps where each depends on the previous
Fan-Out/Fan-InParallel processing with aggregated results
Human InteractionWorkflow pauses for external input/approval
Durable EntitiesStateful objects with operations (counters, accounts)
Sub-OrchestrationsReusable workflow components or version isolation

See references/patterns.md for detailed implementations.

Orchestration Structure

Basic Orchestration

[DurableTask(nameof(MyOrchestration))]
public class MyOrchestration : TaskOrchestrator<string, string>
{
    public override async Task<string> RunAsync(TaskOrchestrationContext context, string input)
    {
        // Call activities
        var result1 = await context.CallActivityAsync<string>(nameof(Step1Activity), input);
        var result2 = await context.CallActivityAsync<string>(nameof(Step2Activity), result1);
        return result2;
    }
}

Basic Activity

[DurableTask(nameof(MyActivity))]
public class MyActivity : TaskActivity<string, string>
{
    private readonly ILogger<MyActivity> _logger;
    
    public MyActivity(ILoggerFactory loggerFactory)
    {
        _logger = loggerFactory.CreateLogger<MyActivity>();
    }

    public override Task<string> RunAsync(TaskActivityContext context, string input)
    {
        _logger.LogInformation("Processing: {Input}", input);
        return Task.FromResult($"Processed: {input}");
    }
}

Critical Rules

Orchestration Determinism

Orchestrations replay from history - all code MUST be deterministic:

NEVER do inside orchestrations:

  • DateTime.Now, DateTime.UtcNow → Use context.CurrentUtcDateTime
  • Guid.NewGuid() → Use context.NewGuid()
  • Random → Pass random values from activities
  • Direct I/O, HTTP calls, database access → Move to activities
  • Task.Delay() → Use context.CreateTimer()
  • Non-deterministic LINQ (parallel, unordered)

ALWAYS safe:

  • context.CallActivityAsync<T>()
  • context.CallSubOrchestrationAsync<T>()
  • context.CreateTimer()
  • context.WaitForExternalEvent<T>()
  • context.CurrentUtcDateTime
  • context.NewGuid()
  • context.SetCustomStatus()

Error Handling

public override async Task<string> RunAsync(TaskOrchestrationContext context, string input)
{
    try
    {
        return await context.CallActivityAsync<string>(nameof(RiskyActivity), input);
    }
    catch (TaskFailedException ex)
    {
        // Activity failed - implement compensation or retry
        context.SetCustomStatus(new { Error = ex.Message });
        return await context.CallActivityAsync<string>(nameof(CompensationActivity), input);
    }
}

Retry Policies

var options = new TaskOptions
{
    Retry = new RetryPolicy(
        maxNumberOfAttempts: 3,
        firstRetryInterval: TimeSpan.FromSeconds(5),
        backoffCoefficient: 2.0,
        maxRetryInterval: TimeSpan.FromMinutes(1))
};

await context.CallActivityAsync<string>(nameof(UnreliableActivity), input, options);

Connection & Authentication

Connection String Formats

// Local emulator (no auth)
"Endpoint=http://localhost:8080;TaskHub=default;Authentication=None"

// Azure with DefaultAzureCredential
"Endpoint=https://my-scheduler.region.durabletask.io;TaskHub=my-hub;Authentication=DefaultAzure"

// Azure with Managed Identity
"Endpoint=https://my-scheduler.region.durabletask.io;TaskHub=my-hub;Authentication=ManagedIdentity"

// Azure with specific credential
"Endpoint=https://my-scheduler.region.durabletask.io;TaskHub=my-hub;Authentication=AzureCLI"

Authentication Helper

static string GetConnectionString()
{
    var endpoint = Environment.GetEnvironmentVariable("ENDPOINT") ?? "http://localhost:8080";
    var taskHub = Environment.GetEnvironmentVariable("TASKHUB") ?? "default";
    
    var authType = endpoint.StartsWith("http://localhost") ? "None" : "DefaultAzure";
    return $"Endpoint={endpoint};TaskHub={taskHub};Authentication={authType}";
}

Local Development with Emulator

# Pull and run the emulator
docker pull mcr.microsoft.com/dts/dts-emulator:latest
docker run -d -p 8080:8080 -p 8082:8082 --name dts-emulator mcr.microsoft.com/dts/dts-emulator:latest

# Dashboard available at http://localhost:8082

References

  • patterns.md - Detailed pattern implementations (Fan-Out/Fan-In, Human Interaction, Entities, Sub-Orchestrations)
  • setup.md - Azure Durable Task Scheduler provisioning and deployment

Install

Download ZIP
Requires askill CLI v1.0+

AI Quality Score

92/100Analyzed 2/11/2026

A comprehensive and highly actionable guide for building .NET workflows using the Durable Task SDK. It includes setup code, pattern guides, and critical determinism rules.

95
95
90
95
95

Metadata

Licenseunknown
Version-
Updated2/8/2026
PublisherAzure-Samples

Tags

apici-cddatabasegithub-actionssecurity