--- name: durable-task-dotnet description: 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. --- # 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 ```xml ``` ### Minimal Worker Setup ```csharp 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 ```csharp 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 | Pattern | Use When | |---------|----------| | **Function Chaining** | Sequential steps where each depends on the previous | | **Fan-Out/Fan-In** | Parallel processing with aggregated results | | **Human Interaction** | Workflow pauses for external input/approval | | **Durable Entities** | Stateful objects with operations (counters, accounts) | | **Sub-Orchestrations** | Reusable workflow components or version isolation | See [references/patterns.md](references/patterns.md) for detailed implementations. ## Orchestration Structure ### Basic Orchestration ```csharp [DurableTask(nameof(MyOrchestration))] public class MyOrchestration : TaskOrchestrator { public override async Task RunAsync(TaskOrchestrationContext context, string input) { // Call activities var result1 = await context.CallActivityAsync(nameof(Step1Activity), input); var result2 = await context.CallActivityAsync(nameof(Step2Activity), result1); return result2; } } ``` ### Basic Activity ```csharp [DurableTask(nameof(MyActivity))] public class MyActivity : TaskActivity { private readonly ILogger _logger; public MyActivity(ILoggerFactory loggerFactory) { _logger = loggerFactory.CreateLogger(); } public override Task 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()` - `context.CallSubOrchestrationAsync()` - `context.CreateTimer()` - `context.WaitForExternalEvent()` - `context.CurrentUtcDateTime` - `context.NewGuid()` - `context.SetCustomStatus()` ### Error Handling ```csharp public override async Task RunAsync(TaskOrchestrationContext context, string input) { try { return await context.CallActivityAsync(nameof(RiskyActivity), input); } catch (TaskFailedException ex) { // Activity failed - implement compensation or retry context.SetCustomStatus(new { Error = ex.Message }); return await context.CallActivityAsync(nameof(CompensationActivity), input); } } ``` ### Retry Policies ```csharp var options = new TaskOptions { Retry = new RetryPolicy( maxNumberOfAttempts: 3, firstRetryInterval: TimeSpan.FromSeconds(5), backoffCoefficient: 2.0, maxRetryInterval: TimeSpan.FromMinutes(1)) }; await context.CallActivityAsync(nameof(UnreliableActivity), input, options); ``` ## Connection & Authentication ### Connection String Formats ```csharp // 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 ```csharp 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 ```bash # 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](references/patterns.md)** - Detailed pattern implementations (Fan-Out/Fan-In, Human Interaction, Entities, Sub-Orchestrations) - **[setup.md](references/setup.md)** - Azure Durable Task Scheduler provisioning and deployment