Skip to content

Commit 7f42b11

Browse files
jsundaiDuncanmaCopilotCopilot
authored
Standalone Activities QuickStarts (#5081)
* python draft * go quickstart draft * dotnet draft * java quickstart draft * ruby quickstart draft * typescript standalone activities draft * cards * Avoids a redirect Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Just avoids a redirect Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Fix CodeSnippet component to accept a title prop because the new quickstarts use it * Updates to remove keywords (just for consistency with where we are going), fix some code blocks, and cross link in the feature guides to the quickstart at the end --------- Co-authored-by: Duncan Mackenzie <duncanma@duncanmackenzie.net> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Co-authored-by: Duncan Mackenzie <duncan.mackenzie@temporal.io> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
1 parent 28eba6d commit 7f42b11

36 files changed

Lines changed: 1798 additions & 1381 deletions

docs/develop/dotnet/activities/basics.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ For the Workflow to be able to execute the Activity, we must define the [Activit
2020

2121
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.
2222

23-
The way you write the Activity and register it with a Worker is identical to [Workflow Activities](/develop/dotnet/activities/basics#develop-activity). The only difference is that you execute a Standalone Activity directly from your Temporal Client. See [Standalone Activities](/develop/dotnet/activities/standalone-activities).
23+
The way you write the Activity and register it with a Worker is identical to [Workflow Activities](/develop/dotnet/activities/basics#develop-activity). The only difference is that you execute a Standalone Activity directly from your Temporal Client. See [Standalone Activities](/develop/dotnet/activities/standalone-activities-quickstart).
2424

2525
You can develop an Activity Definition by using the `[Activity]` attribute from the `Temporalio.Activities` namespace on the method.
2626
To register a method as an Activity with a custom name, use an attribute parameter, for example `[Activity("your-activity")]`.

docs/develop/dotnet/activities/index.mdx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ import * as Components from '@site/src/components';
2323

2424
- [Activity basics](/develop/dotnet/activities/basics)
2525
- [Activity execution](/develop/dotnet/activities/execution)
26-
- [Standalone Activities](/develop/dotnet/activities/standalone-activities)
26+
- [Standalone Activities Quickstart](/develop/dotnet/activities/standalone-activities-quickstart)
27+
- [Standalone Activities Feature Guide](/develop/dotnet/activities/standalone-activities)
2728
- [Timeouts](/develop/dotnet/activities/timeouts)
2829
- [Asynchronous Activity completion](/develop/dotnet/activities/asynchronous-activity)
2930
- [Dynamic Activity](/develop/dotnet/activities/dynamic-activity)
Lines changed: 269 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,269 @@
1+
---
2+
id: standalone-activities-quickstart
3+
title: Standalone Activities .NET Quickstart
4+
sidebar_label: Quickstart
5+
description: Execute a Standalone Activity with the Temporal .NET SDK without writing a Workflow.
6+
tags:
7+
- Activities
8+
- Temporal Client
9+
- .NET SDK
10+
- setup
11+
- getting started
12+
hide_table_of_contents: true
13+
---
14+
15+
import { SetupSteps, SetupStep, CodeSnippet } from '@site/src/components';
16+
17+
# Quickstart
18+
19+
Standalone Activities are Activities that run independently, without being orchestrated by a
20+
Workflow. Instead of starting an Activity from within a Workflow Definition, you start a Standalone
21+
Activity directly from a Temporal Client.
22+
23+
The way you write the Activity and register it with a Worker is identical to [Workflow
24+
Activities](/develop/dotnet/activities/basics#develop-activity). The only difference is that you execute a
25+
Standalone Activity directly from your Temporal Client.
26+
27+
:::note
28+
29+
This documentation uses source code from the [StandaloneActivity](https://github.com/temporalio/samples-dotnet/tree/main/src/StandaloneActivity) sample project.
30+
31+
:::
32+
33+
<SetupSteps>
34+
35+
<SetupStep code={
36+
<>
37+
<CodeSnippet language="bash">
38+
{`brew install temporal`}
39+
</CodeSnippet>
40+
<CodeSnippet language="bash">
41+
{`temporal --version`}
42+
</CodeSnippet>
43+
<CodeSnippet language="bash">
44+
{`temporal server start-dev`}
45+
</CodeSnippet>
46+
</>
47+
}>
48+
49+
## Get started with Standalone Activities {/* #get-started */}
50+
51+
Prerequisites:
52+
53+
- **[.NET](https://dotnet.microsoft.com/download)** 8.0+
54+
55+
- **Temporal .NET SDK** (v1.12.0 or higher). See the [.NET Quickstart](/develop/dotnet/set-up-your-local-dotnet) for install instructions.
56+
57+
- **Temporal CLI** v1.7.0 or higher. Install with Homebrew, or see the [Temporal CLI install guide](/cli/setup-cli) for other platforms. Verify the installation with `temporal --version`.
58+
59+
Start the Temporal development server with `temporal server start-dev`.
60+
61+
This command automatically starts the Temporal development server with the Web UI, and creates the `default` Namespace.
62+
It uses an in-memory database, so do not use it for real use cases.
63+
64+
The Temporal Server will now be available for client connections on `localhost:7233`, and the
65+
Temporal Web UI will now be accessible at [http://localhost:8233](http://localhost:8233).
66+
67+
</SetupStep>
68+
69+
<SetupStep>
70+
71+
## Clone the sample
72+
73+
Clone the [samples-dotnet](https://github.com/temporalio/samples-dotnet) repository to follow along:
74+
75+
```
76+
git clone https://github.com/temporalio/samples-dotnet.git
77+
cd samples-dotnet
78+
```
79+
80+
The sample project is structured as follows:
81+
82+
```
83+
src/StandaloneActivity/
84+
├── MyActivities.cs
85+
├── Program.cs
86+
├── README.md
87+
└── TemporalioSamples.StandaloneActivity.csproj
88+
```
89+
90+
</SetupStep>
91+
92+
<SetupStep code={
93+
<>
94+
<CodeSnippet language="csharp" title="MyActivities.cs">
95+
{`namespace TemporalioSamples.StandaloneActivity;
96+
97+
using Temporalio.Activities;
98+
99+
public static class MyActivities
100+
{
101+
[Activity]
102+
public static Task<string> ComposeGreetingAsync(ComposeGreetingInput input) =>
103+
Task.FromResult($"{input.Greeting}, {input.Name}!");
104+
}
105+
106+
public record ComposeGreetingInput(string Greeting, string Name);`}
107+
</CodeSnippet>
108+
</>
109+
}>
110+
111+
## Define your Activity {/* #define-activity */}
112+
113+
An Activity in the Temporal .NET SDK is a method decorated with the `[Activity]` attribute. The way
114+
you write a Standalone Activity is identical to how you write an Activity orchestrated by a Workflow.
115+
In fact, the same Activity can be executed both as a Standalone Activity and as a Workflow Activity.
116+
117+
[src/StandaloneActivity/MyActivities.cs](https://github.com/temporalio/samples-dotnet/blob/main/src/StandaloneActivity/MyActivities.cs)
118+
119+
</SetupStep>
120+
121+
<SetupStep code={
122+
<>
123+
<CodeSnippet language="csharp" title="Program.cs">
124+
{`using Microsoft.Extensions.Logging;
125+
using Temporalio.Client;
126+
using Temporalio.Common.EnvConfig;
127+
using Temporalio.Worker;
128+
using TemporalioSamples.StandaloneActivity;
129+
130+
var connectOptions = ClientEnvConfig.LoadClientConnectOptions();
131+
connectOptions.TargetHost ??= "localhost:7233";
132+
connectOptions.LoggerFactory = LoggerFactory.Create(builder =>
133+
builder.
134+
AddSimpleConsole(options => options.TimestampFormat = "[HH:mm:ss] ").
135+
SetMinimumLevel(LogLevel.Information));
136+
var client = await TemporalClient.ConnectAsync(connectOptions);
137+
138+
const string taskQueue = "standalone-activity-sample";
139+
140+
using var tokenSource = new CancellationTokenSource();
141+
Console.CancelKeyPress += (_, eventArgs) =>
142+
{
143+
tokenSource.Cancel();
144+
eventArgs.Cancel = true;
145+
};
146+
147+
using var worker = new TemporalWorker(
148+
client,
149+
new TemporalWorkerOptions(taskQueue).
150+
AddActivity(MyActivities.ComposeGreetingAsync));
151+
152+
await worker.ExecuteAsync(tokenSource.Token);`}
153+
</CodeSnippet>
154+
<CodeSnippet language="bash">
155+
{`dotnet run --project src/StandaloneActivity worker`}
156+
</CodeSnippet>
157+
</>
158+
}>
159+
160+
## Run a Worker with the Activity registered {/* #run-worker */}
161+
162+
Running a Worker for Standalone Activities is the same as running a Worker for Workflow Activities —
163+
you create a Worker, register the Activity, and run the Worker. The Worker doesn't need to know
164+
whether the Activity will be invoked from a Workflow or as a Standalone Activity. See [How to develop
165+
a Worker](/develop/dotnet/workers/run-worker-process) for more details on Worker setup and
166+
configuration options.
167+
168+
[src/StandaloneActivity/Program.cs](https://github.com/temporalio/samples-dotnet/blob/main/src/StandaloneActivity/Program.cs)
169+
170+
Open a new terminal, navigate to the `samples-dotnet` directory, and run the Worker.
171+
Leave this terminal running - the Worker needs to stay up to process activities.
172+
173+
</SetupStep>
174+
175+
<SetupStep code={
176+
<>
177+
<CodeSnippet language="csharp" title="Program.cs">
178+
{`using Temporalio.Client;
179+
using Temporalio.Common.EnvConfig;
180+
using TemporalioSamples.StandaloneActivity;
181+
182+
var connectOptions = ClientEnvConfig.LoadClientConnectOptions();
183+
connectOptions.TargetHost ??= "localhost:7233";
184+
var client = await TemporalClient.ConnectAsync(connectOptions);
185+
186+
var result = await client.ExecuteActivityAsync(
187+
() => MyActivities.ComposeGreetingAsync(new ComposeGreetingInput("Hello", "World")),
188+
new("standalone-activity-id", "standalone-activity-sample")
189+
{
190+
ScheduleToCloseTimeout = TimeSpan.FromSeconds(10),
191+
});
192+
Console.WriteLine($"Activity result: {result}");`}
193+
</CodeSnippet>
194+
<CodeSnippet language="csharp">
195+
{`// Using a lambda expression (type-safe)
196+
var result = await client.ExecuteActivityAsync(
197+
() => MyActivities.ComposeGreetingAsync(new ComposeGreetingInput("Hello", "World")),
198+
new("standalone-activity-id", "standalone-activity-sample")
199+
{
200+
ScheduleToCloseTimeout = TimeSpan.FromSeconds(10),
201+
});
202+
203+
// Using a string type name
204+
var result = await client.ExecuteActivityAsync<string>(
205+
"ComposeGreeting",
206+
new object?[] { new ComposeGreetingInput("Hello", "World") },
207+
new("standalone-activity-id", "standalone-activity-sample")
208+
{
209+
ScheduleToCloseTimeout = TimeSpan.FromSeconds(10),
210+
});`}
211+
</CodeSnippet>
212+
<CodeSnippet language="bash">
213+
{`dotnet run --project src/StandaloneActivity execute-activity`}
214+
</CodeSnippet>
215+
<CodeSnippet language="bash">
216+
{`temporal activity execute \\
217+
--type ComposeGreeting \\
218+
--activity-id standalone-activity-id \\
219+
--task-queue standalone-activity-sample \\
220+
--schedule-to-close-timeout 10s \\
221+
--input '{"Greeting": "Hello", "Name": "World"}'`}
222+
</CodeSnippet>
223+
</>
224+
}>
225+
226+
## Execute a Standalone Activity {/* #execute-activity */}
227+
228+
Use
229+
[`client.ExecuteActivityAsync()`](https://dotnet.temporal.io/api/Temporalio.Client.ITemporalClientExtensions.html)
230+
to execute a Standalone Activity and wait for the result. Call this from your application code, not
231+
from inside a Workflow Definition. This durably enqueues your Standalone Activity in the Temporal
232+
Server, waits for it to be executed on your Worker, and then returns the result.
233+
234+
[src/StandaloneActivity/Program.cs](https://github.com/temporalio/samples-dotnet/blob/main/src/StandaloneActivity/Program.cs)
235+
236+
You can pass the Activity as either a lambda expression or a string Activity type name.
237+
238+
`StartActivityOptions` requires `Id`, `TaskQueue`, and at least one of `ScheduleToCloseTimeout` or
239+
`StartToCloseTimeout`. See
240+
[`StartActivityOptions`](https://dotnet.temporal.io/api/Temporalio.Client.StartActivityOptions.html)
241+
in the API reference for the full set of options.
242+
243+
To run it:
244+
245+
1. Make sure the Temporal Server is running (from the [Get Started](#get-started) step above).
246+
2. Make sure the Worker is running (from the [Run a Worker](#run-worker) step above).
247+
3. Open a new terminal, navigate to the `samples-dotnet` directory, and run `dotnet run --project src/StandaloneActivity execute-activity`.
248+
249+
Or use the Temporal CLI.
250+
251+
</SetupStep>
252+
253+
</SetupSteps>
254+
255+
## Run with Temporal Cloud
256+
257+
All code samples on this page use
258+
[`ClientEnvConfig.LoadClientConnectOptions()`](https://dotnet.temporal.io/api/Temporalio.Common.EnvConfig.ClientEnvConfig.html)
259+
to configure the Temporal Client connection. It responds to [environment
260+
variables](/references/client-environment-configuration) and [TOML configuration
261+
files](/references/client-environment-configuration), so the same code works against a local dev
262+
server and Temporal Cloud without changes. See [Run Standalone Activities with Temporal
263+
Cloud](/develop/dotnet/activities/standalone-activities#run-standalone-activities-temporal-cloud) in the Feature Guide
264+
for mTLS and API key setup.
265+
266+
## Next steps
267+
268+
- **[Standalone Activities Feature Guide](/develop/dotnet/activities/standalone-activities)**: Start without waiting, get handles, list and count Activities, and connect to Temporal Cloud.
269+
- **[Activity basics](/develop/dotnet/activities/basics)**: How to write and register Activities with the .NET SDK.

0 commit comments

Comments
 (0)