7.7 KiB
Temporal .NET SDK Reference
Overview
The Temporal .NET SDK provides a high-performance, type-safe approach to building durable workflows using C# and .NET. Workflows use attributes ([Workflow], [WorkflowRun]) and lambda expressions for type-safe invocations. Supports .NET Framework 4.6.2+ and .NET Core 3.1+ (including .NET 5+).
CRITICAL: The .NET SDK has no sandbox. Developers must be careful to avoid non-deterministic code in workflows. See the Determinism Rules section below and references/dotnet/determinism.md.
Understanding Replay
Temporal workflows are durable through history replay. For details on how this works, see references/core/determinism.md.
Quick Start
Add Dependency: Install the Temporal SDK NuGet package:
dotnet add package Temporalio
Activities.cs - Activity definitions (separate file for clarity):
using Temporalio.Activities;
public class MyActivities
{
[Activity]
public string Greet(string name)
{
return $"Hello, {name}!";
}
}
GreetingWorkflow.workflow.cs - Workflow definition:
using Temporalio.Workflows;
[Workflow]
public class GreetingWorkflow
{
[WorkflowRun]
public async Task<string> RunAsync(string name)
{
return await Workflow.ExecuteActivityAsync(
(MyActivities a) => a.Greet(name),
new() { StartToCloseTimeout = TimeSpan.FromSeconds(30) });
}
}
Worker (Program.cs) - Worker setup (registers activity and workflow, runs indefinitely and processes tasks):
using Temporalio.Client;
using Temporalio.Worker;
var client = await TemporalClient.ConnectAsync(new("localhost:7233"));
using var worker = new TemporalWorker(
client,
new TemporalWorkerOptions("my-task-queue")
.AddWorkflow<GreetingWorkflow>()
.AddAllActivities(new MyActivities()));
await worker.ExecuteAsync();
Start the dev server: Start temporal server start-dev in the background.
Start the worker: Run dotnet run in the worker project.
Starter (Program.cs) - Start a workflow execution:
using Temporalio.Client;
var client = await TemporalClient.ConnectAsync(new("localhost:7233"));
var result = await client.ExecuteWorkflowAsync(
(GreetingWorkflow wf) => wf.RunAsync("my name"),
new(id: $"greeting-{Guid.NewGuid()}", taskQueue: "my-task-queue"));
Console.WriteLine($"Result: {result}");
Run the workflow: Run dotnet run in the starter project. Should output: Result: Hello, my name!.
Key Concepts
Workflow Definition
- Use
[Workflow]attribute on class - Put any state initialization logic in the constructor of your workflow class to guarantee that it happens before signals/updates arrive. If your state initialization logic requires the workflow parameters, then add the
[WorkflowInit]attribute and parameters to your constructor. - Use
[WorkflowRun]on the async entry point method - Must return
TaskorTask<T> - Use
[WorkflowSignal],[WorkflowQuery],[WorkflowUpdate]for handlers
Activity Definition
- Use
[Activity]attribute on methods - Can be sync or async
- Instance methods support dependency injection
- Static methods are also supported
Worker Setup
- Connect client, create
TemporalWorkerwith workflows and activities - Use
AddWorkflow<T>()andAddAllActivities(instance)orAddActivity(method)
Determinism
Workflow code must be deterministic! The .NET SDK has no sandbox. See the Determinism Rules section below and references/core/determinism.md and references/dotnet/determinism.md.
File Organization Best Practice
Keep Workflow definitions in separate files from Activity definitions. While not as critical as Python (no sandbox reloading), separation improves clarity and testability. Use the .workflow.cs extension for workflow files so the .editorconfig overrides (see below) apply only to workflow code.
MyTemporalApp/
├── Workflows/
│ └── GreetingWorkflow.workflow.cs # Only Workflow classes
├── Activities/
│ └── TranslateActivities.cs # Only Activity classes
├── Models/
│ └── OrderInput.cs # Shared data models
├── Worker/
│ └── Program.cs # Worker setup
└── Starter/
└── Program.cs # Client code to start workflows
Workflow .editorconfig
Workflow code violates some standard .NET analyzer rules. The recommended approach is to use the .workflow.cs file extension for workflow files and scope the overrides to that extension:
# Configuration specific for Temporal workflows
[*.workflow.cs]
# We use getters for queries, they cannot be properties
dotnet_diagnostic.CA1024.severity = none
# Don't force workflows to have static methods
dotnet_diagnostic.CA1822.severity = none
# Do not need ConfigureAwait for workflows
dotnet_diagnostic.CA2007.severity = none
# Do not need task scheduler for workflows
dotnet_diagnostic.CA2008.severity = none
# Workflow randomness is intentionally deterministic
dotnet_diagnostic.CA5394.severity = none
# Allow async methods to not have await in them
dotnet_diagnostic.CS1998.severity = none
# Don't force workflows to call async methods
dotnet_diagnostic.VSTHRD103.severity = none
# Don't avoid, but rather encourage things using TaskScheduler.Current in workflows
dotnet_diagnostic.VSTHRD105.severity = none
Determinism Rules
The .NET SDK has no sandbox like Python or TypeScript. Developers must avoid non-deterministic operations manually. Many standard .NET Task APIs use TaskScheduler.Default implicitly, which breaks determinism.
See references/dotnet/determinism.md for the full list of forbidden operations, safe alternatives, and best practices. See references/dotnet/determinism-protection.md for details on the runtime detection mechanism.
Common Pitfalls
- Using
Task.Runin workflows — Uses default scheduler, breaks determinism. UseWorkflow.RunTaskAsync. - Using
Task.Delayin workflows — Uses system timer. UseWorkflow.DelayAsync. ConfigureAwait(false)in workflows — Leaves the deterministic scheduler. Never use in workflows.- Non-
ApplicationFailureExceptionin workflows — Other exceptions retry the workflow task forever instead of failing the workflow. - Dictionary iteration in workflows —
Dictionary<TKey, TValue>has no guaranteed order. UseSortedDictionary. - Forgetting to heartbeat — Long-running activities need
ActivityExecutionContext.Current.Heartbeat()calls. - Using
CancellationTokenSource.CancelAsync— UseCancellationTokenSource.Cancelinstead. - Logging with
Console.WriteLinein workflows — UseWorkflow.Loggerfor replay-safe logging.
Writing Tests
See references/dotnet/testing.md for info on writing tests.
Additional Resources
Reference Files
references/dotnet/patterns.md— Signals, queries, child workflows, saga pattern, etc.references/dotnet/determinism.md— Essentials of determinism in .NETreferences/dotnet/gotchas.md— .NET-specific mistakes and anti-patternsreferences/dotnet/error-handling.md— ApplicationFailureException, retry policies, non-retryable errorsreferences/dotnet/observability.md— Logging, metrics, tracingreferences/dotnet/testing.md— WorkflowEnvironment, time-skipping, activity mockingreferences/dotnet/advanced-features.md— Schedules, worker tuning, dependency injectionreferences/dotnet/data-handling.md— Data converters, payload encryption, etc.references/dotnet/versioning.md— Patching API, workflow type versioning, Worker Versioningreferences/dotnet/determinism-protection.md— Runtime task detection, .NET Task determinism rules