.github/skills/durable-task-dotnet/SKILL.md
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.
npx skillsauth add Azure-Samples/Durable-Task-Scheduler durable-task-dotnetInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Build fault-tolerant, stateful workflows in .NET applications using the Durable Task SDK connected to Azure Durable Task Scheduler.
<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>
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();
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 | 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 for detailed implementations.
[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;
}
}
[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}");
}
}
Orchestrations replay from history - all code MUST be deterministic:
NEVER do inside orchestrations:
DateTime.Now, DateTime.UtcNow → Use context.CurrentUtcDateTimeGuid.NewGuid() → Use context.NewGuid()Random → Pass random values from activitiesTask.Delay() → Use context.CreateTimer()ALWAYS safe:
context.CallActivityAsync<T>()context.CallSubOrchestrationAsync<T>()context.CreateTimer()context.WaitForExternalEvent<T>()context.CurrentUtcDateTimecontext.NewGuid()context.SetCustomStatus()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);
}
}
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);
// 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"
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}";
}
# 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
development
Migrate existing Azure Durable Functions apps from existing backend storage providers (Azure Storage, Netherite, MSSQL) to the Durable Task Scheduler. Use when switching backends, converting to azureManaged storage provider, upgrading from Azure Storage default provider, migrating from Netherite Event Hubs-based backend, migrating from Microsoft SQL Server backend, or modernizing Durable Functions infrastructure. Applies to .NET, Python, JavaScript/TypeScript, and Java Durable Functions apps that need to adopt the managed Durable Task Scheduler service.
development
Build durable, fault-tolerant workflows in Python 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 Python application requiring durable execution, state persistence, or distributed transactions without Azure Functions dependency.
development
Build durable, fault-tolerant workflows in JavaScript/TypeScript 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 durable timers. Applies to any Node.js application requiring durable execution, state persistence, or distributed coordination without Azure Functions dependency.
development
Build durable, fault-tolerant workflows in Java using the Durable Task SDK with Azure Durable Task Scheduler. Use when creating orchestrations, activities, or implementing patterns like function chaining, fan-out/fan-in, human interaction, or monitoring. Applies to any Java application requiring durable execution, state persistence, or distributed transactions without Azure Functions dependency.