From 6608efc684661ec67cc5481cab0a45396dd93456 Mon Sep 17 00:00:00 2001 From: Andrew Yuan Date: Thu, 12 Mar 2026 19:38:44 -0700 Subject: [PATCH 1/3] dotnet doc --- docs/develop/dotnet/index.mdx | 8 + docs/develop/dotnet/standalone-activities.mdx | 423 ++++++++++++++++++ sidebars.js | 1 + 3 files changed, 432 insertions(+) create mode 100644 docs/develop/dotnet/standalone-activities.mdx diff --git a/docs/develop/dotnet/index.mdx b/docs/develop/dotnet/index.mdx index 463fa81030..2bf8385aaa 100644 --- a/docs/develop/dotnet/index.mdx +++ b/docs/develop/dotnet/index.mdx @@ -69,6 +69,14 @@ Connect to a Temporal Service and start a Workflow Execution. - [Get Workflow results](/develop/dotnet/temporal-client#get-workflow-results): Retrieve and process the results of your Workflows efficiently. +## [Standalone Activities](/develop/dotnet/standalone-activities) + +Execute Activities independently without a Workflow using the Temporal Client. + +- [How to execute a Standalone Activity](/develop/dotnet/standalone-activities#execute-activity) +- [How to start a Standalone Activity without waiting](/develop/dotnet/standalone-activities#start-activity) +- [How to get a handle to an existing Standalone Activity](/develop/dotnet/standalone-activities#get-activity-handle) + ## [Testing](/develop/dotnet/testing-suite) Set up the testing suite and test Workflows and Activities. diff --git a/docs/develop/dotnet/standalone-activities.mdx b/docs/develop/dotnet/standalone-activities.mdx new file mode 100644 index 0000000000..b688c668e3 --- /dev/null +++ b/docs/develop/dotnet/standalone-activities.mdx @@ -0,0 +1,423 @@ +--- +id: standalone-activities +title: Standalone Activities - .NET SDK +sidebar_label: Standalone Activities +toc_max_heading_level: 4 +keywords: + - standalone activity + - activity execution + - execute activity + - activity handle + - list activities + - count activities + - dotnet sdk +tags: + - Activities + - Temporal Client + - .NET SDK + - Temporal SDKs +description: Execute Activities independently without a Workflow using the Temporal .NET SDK. +--- + +:::tip SUPPORT, STABILITY, and DEPENDENCY INFO + +Temporal .NET SDK support for [Standalone Activities](/standalone-activity) is at +[Pre-release](/evaluate/development-production-features/release-stages#pre-release). + +All APIs are experimental and may be subject to backwards-incompatible changes. + +::: + +Standalone Activities are Activities that run independently, without being orchestrated by a +Workflow. Instead of starting an Activity from within a Workflow Definition, you start a Standalone +Activity directly from a Temporal Client. + +The way you write the Activity and register it with a Worker is identical to [Workflow +Activities](./core-application.mdx#develop-activities). The only difference is that you execute a +Standalone Activity directly from your Temporal Client. + +This page covers the following: + +- [Get Started with Standalone Activities](#get-started) +- [Define your Activity](#define-activity) +- [Run a Worker with the Activity registered](#run-worker) +- [Execute a Standalone Activity](#execute-activity) +- [Start a Standalone Activity without waiting for the result](#start-activity) +- [Get a handle to an existing Standalone Activity](#get-activity-handle) +- [Wait for the result of a Standalone Activity](#get-activity-result) +- [List Standalone Activities](#list-activities) +- [Count Standalone Activities](#count-activities) +- [Run Standalone Activities with Temporal Cloud](#run-standalone-activities-temporal-cloud) + +:::note + +This documentation uses source code from the [StandaloneActivity](https://github.com/temporalio/samples-dotnet/tree/main/src/StandaloneActivity) sample. + +::: + +## Get Started with Standalone Activities {#get-started} + +Prerequisites: + +- Install the [Temporal CLI](https://github.com/temporalio/cli/releases/tag/v1.6.2-standalone-activity) (Standalone Activity Prerelease version) + +- Temporal .NET SDK ( version X.Y.Z or higher). See the [.NET Quickstart](https://docs.temporal.io/develop/dotnet/set-up-your-local-dotnet) for install instructions. + +The first step in running a Standalone Activity involves starting a Temporal server: + +```bash +temporal server start-dev +``` + +This command automatically starts the Temporal development server with the Web UI, and creates the `default` Namespace. +It uses an in-memory database, so do not use it for real use cases. + +:::info Temporal Cloud + +All code samples on this page use +[`ClientEnvConfig.LoadClientConnectOptions()`](https://dotnet.temporal.io/api/Temporalio.Common.EnvConfig.ClientEnvConfig.html) +to configure the Temporal Client connection. It responds to [environment +variables](/references/client-environment-configuration) and [TOML configuration +files](/references/client-environment-configuration), so the same code works against a local dev +server and Temporal Cloud without changes. See [Run Standalone Activities with Temporal +Cloud](#run-standalone-activities-temporal-cloud) below. + +::: + +The Temporal Server will now be available for client connections on `localhost:7233`, and the +Temporal Web UI will now be accessible at [http://localhost:8233](http://localhost:8233). Standalone +Activities are available from the nav bar item located towards the top left of the page: + +Standalone Activities Web UI nav bar item + +Clone the [samples-dotnet](https://github.com/temporalio/samples-dotnet) repository to follow along: + +``` +git clone https://github.com/temporalio/samples-dotnet.git +cd samples-dotnet +``` + +The sample project is structured as follows: + +``` +src/StandaloneActivity/ +├── MyActivities.cs +├── Program.cs +├── README.md +└── TemporalioSamples.StandaloneActivity.csproj +``` + +## Define your Activity {#define-activity} + +An Activity in the Temporal .NET SDK is a method decorated with the `[Activity]` attribute. The way +you write a Standalone Activity is identical to how you write an Activity orchestrated by a Workflow. +In fact, the same Activity can be executed both as a Standalone Activity and as a Workflow Activity. + +[src/StandaloneActivity/MyActivities.cs](https://github.com/temporalio/samples-dotnet/blob/main/src/StandaloneActivity/MyActivities.cs) + +```csharp +namespace TemporalioSamples.StandaloneActivity; + +using Temporalio.Activities; + +public static class MyActivities +{ + [Activity] + public static Task ComposeGreetingAsync(ComposeGreetingInput input) => + Task.FromResult($"{input.Greeting}, {input.Name}!"); +} + +public record ComposeGreetingInput(string Greeting, string Name); +``` + +## Run a Worker with the Activity registered {#run-worker} + +Running a Worker for Standalone Activities is the same as running a Worker for Workflow Activities — +you create a Worker, register the Activity, and run the Worker. The Worker doesn't need to know +whether the Activity will be invoked from a Workflow or as a Standalone Activity. See [How to develop +a Worker](/develop/dotnet/core-application#run-a-dev-worker) for more details on Worker setup and +configuration options. + +[src/StandaloneActivity/Program.cs](https://github.com/temporalio/samples-dotnet/blob/main/src/StandaloneActivity/Program.cs) (worker command) + +```csharp +using Microsoft.Extensions.Logging; +using Temporalio.Client; +using Temporalio.Common.EnvConfig; +using Temporalio.Worker; +using TemporalioSamples.StandaloneActivity; + +var connectOptions = ClientEnvConfig.LoadClientConnectOptions(); +connectOptions.TargetHost ??= "localhost:7233"; +connectOptions.LoggerFactory = LoggerFactory.Create(builder => + builder. + AddSimpleConsole(options => options.TimestampFormat = "[HH:mm:ss] "). + SetMinimumLevel(LogLevel.Information)); +var client = await TemporalClient.ConnectAsync(connectOptions); + +const string taskQueue = "standalone-activity-sample"; + +using var tokenSource = new CancellationTokenSource(); +Console.CancelKeyPress += (_, eventArgs) => +{ + tokenSource.Cancel(); + eventArgs.Cancel = true; +}; + +using var worker = new TemporalWorker( + client, + new TemporalWorkerOptions(taskQueue). + AddActivity(MyActivities.ComposeGreetingAsync)); + +await worker.ExecuteAsync(tokenSource.Token); +``` + +Run the Worker (in a separate terminal): + +``` +dotnet run --project src/StandaloneActivity worker +``` + +## Execute a Standalone Activity {#execute-activity} + +Use +[`client.ExecuteActivityAsync()`](https://dotnet.temporal.io/api/Temporalio.Client.ITemporalClientExtensions.html) +to execute a Standalone Activity and wait for the result. Call this from your application code, not +from inside a Workflow Definition. This durably enqueues your Standalone Activity in the Temporal +Server, waits for it to be executed on your Worker, and then returns the result. + +[src/StandaloneActivity/Program.cs](https://github.com/temporalio/samples-dotnet/blob/main/src/StandaloneActivity/Program.cs) (execute-activity command) + +```csharp +using Temporalio.Client; +using Temporalio.Common.EnvConfig; +using TemporalioSamples.StandaloneActivity; + +var connectOptions = ClientEnvConfig.LoadClientConnectOptions(); +connectOptions.TargetHost ??= "localhost:7233"; +var client = await TemporalClient.ConnectAsync(connectOptions); + +var result = await client.ExecuteActivityAsync( + () => MyActivities.ComposeGreetingAsync(new ComposeGreetingInput("Hello", "World")), + new("standalone-activity-id", "standalone-activity-sample") + { + ScheduleToCloseTimeout = TimeSpan.FromSeconds(10), + }); +Console.WriteLine($"Activity result: {result}"); +``` + +You can pass the Activity as either a lambda expression or a string Activity type name: + +```csharp +// Using a lambda expression (type-safe) +var result = await client.ExecuteActivityAsync( + () => MyActivities.ComposeGreetingAsync(new ComposeGreetingInput("Hello", "World")), + new("my-activity-id", "my-task-queue") + { + ScheduleToCloseTimeout = TimeSpan.FromSeconds(10), + }); + +// Using a string type name +var result = await client.ExecuteActivityAsync( + "ComposeGreeting", + new object?[] { new ComposeGreetingInput("Hello", "World") }, + new("my-activity-id", "my-task-queue") + { + ScheduleToCloseTimeout = TimeSpan.FromSeconds(10), + }); +``` + +`StartActivityOptions` requires `Id`, `TaskQueue`, and at least one of `ScheduleToCloseTimeout` or +`StartToCloseTimeout`. See +[`StartActivityOptions`](https://dotnet.temporal.io/api/Temporalio.Client.StartActivityOptions.html) +in the API reference for the full set of options. + +Run it (with the Worker running in another terminal): + +``` +dotnet run --project src/StandaloneActivity execute-activity +``` + +Or use the Temporal CLI: + +```bash +temporal activity execute \ + --type ComposeGreeting \ + --activity-id standalone-activity-id \ + --task-queue standalone-activity-sample \ + --schedule-to-close-timeout 10s \ + --input '{"Greeting": "Hello", "Name": "World"}' +``` + +## Start a Standalone Activity without waiting for the result {#start-activity} + +Use +[`client.StartActivityAsync()`](https://dotnet.temporal.io/api/Temporalio.Client.ITemporalClient.html) +to start a Standalone Activity and get a handle without waiting for the result: + +[src/StandaloneActivity/Program.cs](https://github.com/temporalio/samples-dotnet/blob/main/src/StandaloneActivity/Program.cs) (start-activity command) + +```csharp +using Temporalio.Client; +using Temporalio.Common.EnvConfig; +using TemporalioSamples.StandaloneActivity; + +var connectOptions = ClientEnvConfig.LoadClientConnectOptions(); +connectOptions.TargetHost ??= "localhost:7233"; +var client = await TemporalClient.ConnectAsync(connectOptions); + +var handle = await client.StartActivityAsync( + () => MyActivities.ComposeGreetingAsync(new ComposeGreetingInput("Hello", "World")), + new("standalone-activity-id", "standalone-activity-sample") + { + ScheduleToCloseTimeout = TimeSpan.FromSeconds(10), + }); +Console.WriteLine($"Started activity: {handle.Id}"); + +// Wait for the result later +var result = await handle.GetResultAsync(); +Console.WriteLine($"Activity result: {result}"); +``` + +Run it (with the Worker running in another terminal): + +``` +dotnet run --project src/StandaloneActivity start-activity +``` + +Or use the Temporal CLI: + +```bash +temporal activity start \ + --type ComposeGreeting \ + --activity-id standalone-activity-id \ + --task-queue standalone-activity-sample \ + --schedule-to-close-timeout 10s \ + --input '{"Greeting": "Hello", "Name": "World"}' +``` + +## Get a handle to an existing Standalone Activity {#get-activity-handle} + +Use `client.GetActivityHandle()` to create a handle to a previously started Standalone Activity: + +```csharp +// Without a known result type +var handle = client.GetActivityHandle("my-activity-id", runId: "the-run-id"); + +// With a known result type +var typedHandle = client.GetActivityHandle("my-activity-id", runId: "the-run-id"); +``` + +You can use the handle to wait for the result, describe, cancel, or terminate the Activity. + +## Wait for the result of a Standalone Activity {#get-activity-result} + +Under the hood, calling `client.ExecuteActivityAsync()` is the same as calling +`client.StartActivityAsync()` to durably enqueue the Standalone Activity, and then calling +`await handle.GetResultAsync()` to wait for the Activity to be executed and return the result: + +```csharp +var result = await handle.GetResultAsync(); +``` + +## List Standalone Activities {#list-activities} + +Use +[`client.ListActivitiesAsync()`](https://dotnet.temporal.io/api/Temporalio.Client.ITemporalClient.html) +to list Standalone Activity Executions that match a [List Filter](/list-filter) query. The result is +an `IAsyncEnumerable` that yields `ActivityExecution` entries. + +[src/StandaloneActivity/Program.cs](https://github.com/temporalio/samples-dotnet/blob/main/src/StandaloneActivity/Program.cs) (list-activities command) + +```csharp +using Temporalio.Client; +using Temporalio.Common.EnvConfig; + +var connectOptions = ClientEnvConfig.LoadClientConnectOptions(); +connectOptions.TargetHost ??= "localhost:7233"; +var client = await TemporalClient.ConnectAsync(connectOptions); + +await foreach (var info in client.ListActivitiesAsync( + "TaskQueue = 'standalone-activity-sample'")) +{ + Console.WriteLine( + $"ActivityID: {info.ActivityId}, Type: {info.ActivityType}, Status: {info.Status}"); +} +``` + +Run it: + +``` +dotnet run --project src/StandaloneActivity list-activities +``` + +Or use the Temporal CLI: + +```bash +temporal activity list +``` + +The query parameter accepts the same [List Filter](/list-filter) syntax used for [Workflow +Visibility](/visibility). For example, `"ActivityType = 'ComposeGreeting' AND Status = 'Running'"`. + +## Count Standalone Activities {#count-activities} + +Use +[`client.CountActivitiesAsync()`](https://dotnet.temporal.io/api/Temporalio.Client.ITemporalClient.html) +to count Standalone Activity Executions that match a [List Filter](/list-filter) query. + +[src/StandaloneActivity/Program.cs](https://github.com/temporalio/samples-dotnet/blob/main/src/StandaloneActivity/Program.cs) (count-activities command) + +```csharp +using Temporalio.Client; +using Temporalio.Common.EnvConfig; + +var connectOptions = ClientEnvConfig.LoadClientConnectOptions(); +connectOptions.TargetHost ??= "localhost:7233"; +var client = await TemporalClient.ConnectAsync(connectOptions); + +var resp = await client.CountActivitiesAsync( + "TaskQueue = 'standalone-activity-sample'"); +Console.WriteLine($"Total activities: {resp.Count}"); +``` + +Run it: + +``` +dotnet run --project src/StandaloneActivity count-activities +``` + +Or use the Temporal CLI: + +```bash +temporal activity count +``` + +## Run Standalone Activities with Temporal Cloud {#run-standalone-activities-temporal-cloud} + +The code samples on this page use `ClientEnvConfig.LoadClientConnectOptions()`, so the same code +works against Temporal Cloud — just configure the connection via environment variables or a TOML +profile. No code changes are needed. + +For full details on connecting to Temporal Cloud, including Namespace creation, certificate +generation, and authentication options, see +[Connect to Temporal Cloud](/develop/dotnet/temporal-client#connect-to-temporal-cloud). + +### Connect with mTLS + +``` +export TEMPORAL_ADDRESS=..tmprl.cloud:7233 +export TEMPORAL_NAMESPACE=. +export TEMPORAL_TLS_CLIENT_CERT_PATH='path/to/your/client.pem' +export TEMPORAL_TLS_CLIENT_KEY_PATH='path/to/your/client.key' +``` + +### Connect with an API key + +``` +export TEMPORAL_ADDRESS=..api.temporal.io:7233 +export TEMPORAL_NAMESPACE=. +export TEMPORAL_API_KEY= +``` + +Then run the Worker and starter code as shown in the earlier sections. diff --git a/sidebars.js b/sidebars.js index fac693a923..bb52f90be4 100644 --- a/sidebars.js +++ b/sidebars.js @@ -282,6 +282,7 @@ module.exports = { 'develop/dotnet/set-up-your-local-dotnet', 'develop/dotnet/core-application', 'develop/dotnet/temporal-client', + 'develop/dotnet/standalone-activities', 'develop/dotnet/testing-suite', 'develop/dotnet/failure-detection', 'develop/dotnet/message-passing', From 425c97dbf794327834a96128f4fe6ec5dcb935ff Mon Sep 17 00:00:00 2001 From: Andrew Yuan Date: Wed, 18 Mar 2026 10:54:56 -0700 Subject: [PATCH 2/3] update sdk version supporting SAA Clean up new doc, small change to Go doc to match python/dotnet --- docs/develop/dotnet/standalone-activities.mdx | 12 ++++++------ docs/develop/go/standalone-activities.mdx | 10 +++++----- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/docs/develop/dotnet/standalone-activities.mdx b/docs/develop/dotnet/standalone-activities.mdx index b688c668e3..0db39d937a 100644 --- a/docs/develop/dotnet/standalone-activities.mdx +++ b/docs/develop/dotnet/standalone-activities.mdx @@ -61,7 +61,7 @@ Prerequisites: - Install the [Temporal CLI](https://github.com/temporalio/cli/releases/tag/v1.6.2-standalone-activity) (Standalone Activity Prerelease version) -- Temporal .NET SDK ( version X.Y.Z or higher). See the [.NET Quickstart](https://docs.temporal.io/develop/dotnet/set-up-your-local-dotnet) for install instructions. +- Temporal .NET SDK (v1.12.0 or higher). See the [.NET Quickstart](https://docs.temporal.io/develop/dotnet/set-up-your-local-dotnet) for install instructions. The first step in running a Standalone Activity involves starting a Temporal server: @@ -138,7 +138,7 @@ whether the Activity will be invoked from a Workflow or as a Standalone Activity a Worker](/develop/dotnet/core-application#run-a-dev-worker) for more details on Worker setup and configuration options. -[src/StandaloneActivity/Program.cs](https://github.com/temporalio/samples-dotnet/blob/main/src/StandaloneActivity/Program.cs) (worker command) +[src/StandaloneActivity/Program.cs](https://github.com/temporalio/samples-dotnet/blob/main/src/StandaloneActivity/Program.cs) ```csharp using Microsoft.Extensions.Logging; @@ -186,7 +186,7 @@ to execute a Standalone Activity and wait for the result. Call this from your ap from inside a Workflow Definition. This durably enqueues your Standalone Activity in the Temporal Server, waits for it to be executed on your Worker, and then returns the result. -[src/StandaloneActivity/Program.cs](https://github.com/temporalio/samples-dotnet/blob/main/src/StandaloneActivity/Program.cs) (execute-activity command) +[src/StandaloneActivity/Program.cs](https://github.com/temporalio/samples-dotnet/blob/main/src/StandaloneActivity/Program.cs) ```csharp using Temporalio.Client; @@ -255,7 +255,7 @@ Use [`client.StartActivityAsync()`](https://dotnet.temporal.io/api/Temporalio.Client.ITemporalClient.html) to start a Standalone Activity and get a handle without waiting for the result: -[src/StandaloneActivity/Program.cs](https://github.com/temporalio/samples-dotnet/blob/main/src/StandaloneActivity/Program.cs) (start-activity command) +[src/StandaloneActivity/Program.cs](https://github.com/temporalio/samples-dotnet/blob/main/src/StandaloneActivity/Program.cs) ```csharp using Temporalio.Client; @@ -327,7 +327,7 @@ Use to list Standalone Activity Executions that match a [List Filter](/list-filter) query. The result is an `IAsyncEnumerable` that yields `ActivityExecution` entries. -[src/StandaloneActivity/Program.cs](https://github.com/temporalio/samples-dotnet/blob/main/src/StandaloneActivity/Program.cs) (list-activities command) +[src/StandaloneActivity/Program.cs](https://github.com/temporalio/samples-dotnet/blob/main/src/StandaloneActivity/Program.cs) ```csharp using Temporalio.Client; @@ -366,7 +366,7 @@ Use [`client.CountActivitiesAsync()`](https://dotnet.temporal.io/api/Temporalio.Client.ITemporalClient.html) to count Standalone Activity Executions that match a [List Filter](/list-filter) query. -[src/StandaloneActivity/Program.cs](https://github.com/temporalio/samples-dotnet/blob/main/src/StandaloneActivity/Program.cs) (count-activities command) +[src/StandaloneActivity/Program.cs](https://github.com/temporalio/samples-dotnet/blob/main/src/StandaloneActivity/Program.cs) ```csharp using Temporalio.Client; diff --git a/docs/develop/go/standalone-activities.mdx b/docs/develop/go/standalone-activities.mdx index d96c968851..c4bee58f63 100644 --- a/docs/develop/go/standalone-activities.mdx +++ b/docs/develop/go/standalone-activities.mdx @@ -50,7 +50,7 @@ This page covers the following: :::note This documentation uses source code from the -[Go sample](https://github.com/temporalio/samples-go/tree/main/standalone-activity/helloworld). +[standalone-activity/helloworld](https://github.com/temporalio/samples-go/tree/main/standalone-activity/helloworld). ::: @@ -99,10 +99,6 @@ git clone https://github.com/temporalio/samples-go.git cd samples-go ``` -## Define your Activity {#define-activity} - -Define your Activity in a shared file so that both the Worker and starter can reference it. - The sample project is structured as follows: ``` @@ -114,6 +110,10 @@ standalone-activity/helloworld/ └── main.go ``` +## Define your Activity {#define-activity} + +Define your Activity in a shared file so that both the Worker and starter can reference it. + [standalone-activity/helloworld/activity.go](https://github.com/temporalio/samples-go/blob/main/standalone-activity/helloworld/activity.go) ```go From bf4936835f82a035c8deada91ffc2c21f29ba854 Mon Sep 17 00:00:00 2001 From: Jwahir Sundai Date: Thu, 19 Mar 2026 14:21:30 -0500 Subject: [PATCH 3/3] change link + add project clarification --- docs/develop/dotnet/standalone-activities.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/develop/dotnet/standalone-activities.mdx b/docs/develop/dotnet/standalone-activities.mdx index 0db39d937a..85bb2549a0 100644 --- a/docs/develop/dotnet/standalone-activities.mdx +++ b/docs/develop/dotnet/standalone-activities.mdx @@ -33,7 +33,7 @@ Workflow. Instead of starting an Activity from within a Workflow Definition, you Activity directly from a Temporal Client. The way you write the Activity and register it with a Worker is identical to [Workflow -Activities](./core-application.mdx#develop-activities). The only difference is that you execute a +Activities](/develop/dotnet/core-application#develop-activity). The only difference is that you execute a Standalone Activity directly from your Temporal Client. This page covers the following: @@ -51,7 +51,7 @@ This page covers the following: :::note -This documentation uses source code from the [StandaloneActivity](https://github.com/temporalio/samples-dotnet/tree/main/src/StandaloneActivity) sample. +This documentation uses source code from the [StandaloneActivity](https://github.com/temporalio/samples-dotnet/tree/main/src/StandaloneActivity) sample project. :::