Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,3 +0,0 @@
{
"editor.formatOnSave": true
}
2 changes: 1 addition & 1 deletion docs/cloud/get-started/api-keys.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -415,7 +415,7 @@ To use your API key with a Temporal SDK, see the instructions in each SDK sectio

[How to connect to Temporal Cloud using an API Key with the TypeScript SDK](/develop/typescript/temporal-client#connect-to-temporal-cloud)

[How to connect to Temporal Cloud using an API Key with the .NET SDK](/develop/dotnet/temporal-client#connect-to-temporal-cloud)
[How to connect to Temporal Cloud using an API Key with the .NET SDK](/develop/dotnet/client/temporal-client#connect-to-temporal-cloud)

### tcld

Expand Down
2 changes: 1 addition & 1 deletion docs/cloud/get-started/certificates.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -486,7 +486,7 @@ To view the current certificate filters, use the
- [PHP SDK](/develop/php/temporal-client#connect-to-a-dev-cluster)
- [Python SDK](/develop/python/temporal-client#connect-to-temporal-cloud)
- [TypeScript SDK](/develop/typescript/temporal-client#connect-to-temporal-cloud)
- [.NET SDK](/develop/dotnet/temporal-client#connect-to-temporal-cloud)
- [.NET SDK](/develop/dotnet/client/temporal-client#connect-to-temporal-cloud)

### Configure Temporal CLI {#configure-temporal-cli}

Expand Down
4 changes: 2 additions & 2 deletions docs/cloud/get-started/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ See our guides for connecting each SDK to your Temporal Cloud Namespace:
- [Connect to Temporal Cloud in Java](/develop/java/temporal-client#connect-to-temporal-cloud)
- [Connect to Temporal Cloud in Python](/develop/python/temporal-client#connect-to-temporal-cloud)
- [Connect to Temporal Cloud in TypeScript](/develop/typescript/core-application#connect-to-temporal-cloud)
- [Connect to Temporal Cloud in .NET](/develop/dotnet/temporal-client#connect-to-temporal-cloud)
- [Connect to Temporal Cloud in .NET](/develop/dotnet/client/temporal-client#connect-to-temporal-cloud)
- [Connect to Temporal Cloud in PHP](/develop/php/temporal-client#connect-to-temporal-cloud)
- [Connect to Temporal Cloud in Ruby](/develop/ruby/temporal-client#connect-to-temporal-cloud)

Expand All @@ -68,7 +68,7 @@ See our guides for starting a workflow using each SDK:
- [Start a workflow in Java](/develop/java/temporal-client#start-workflow-execution)
- [Start a workflow in Python](/develop/python/temporal-client#start-workflow-execution)
- [Start a workflow in TypeScript](/develop/typescript/core-application#start-workflow-execution)
- [Start a workflow in .NET](/develop/dotnet/temporal-client#start-workflow)
- [Start a workflow in .NET](/develop/dotnet/client/temporal-client#start-workflow)
- [Start a workflow in PHP](/develop/php/temporal-client#start-workflow-execution)
- [Start a workflow in Ruby](/develop/ruby/temporal-client#start-workflow)

Expand Down
4 changes: 2 additions & 2 deletions docs/cloud/metrics/prometheus-grafana.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ If you're following through with the examples provided here, ensure that you hav
- [PHP](/develop/php/temporal-client#connect-to-a-dev-cluster)
- [Python](/develop/python/temporal-client#connect-to-temporal-cloud)
- [TypeScript](/develop/typescript/core-application#connect-to-temporal-cloud)
- [.NET](/develop/dotnet/temporal-client#connect-to-temporal-cloud)
- [.NET](/develop/dotnet/client/temporal-client#connect-to-temporal-cloud)

- Prometheus and Grafana installed.

Expand Down Expand Up @@ -96,7 +96,7 @@ Each language development guide has details on how to set this up.
- [Java SDK](/develop/java/observability#metrics)
- [TypeScript SDK](/develop/typescript/observability#metrics)
- [Python](/develop/python/observability#metrics)
- [.NET](/develop/dotnet/observability#metrics)
- [.NET](/develop/dotnet/workers/observability#metrics)

The following example uses the Java SDK to set the Prometheus registry and Micrometer stats reporter, set the scope, and expose an endpoint from which Prometheus can scrape the SDK metrics.

Expand Down
55 changes: 55 additions & 0 deletions docs/develop/dotnet/activities/basics.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
---
id: basics
title: Activity Basics - .NET SDK
sidebar_label: Activity Basics
description: This section explains Activity Basics with the .NET SDK
toc_max_heading_level: 4
keywords:
- .NET SDK
tags:
- .NET SDK
- Temporal SDKs
---

## Develop an Activity {#develop-activity}

One of the primary things that Workflows do is orchestrate the execution of Activities.
An Activity is a normal method execution that's intended to execute a single, well-defined action (either short or long-running), such as querying a database, calling a third-party API, or transcoding a media file.
An Activity can interact with world outside the Temporal Platform or use a Temporal Client to interact with a Temporal Service.
For the Workflow to be able to execute the Activity, we must define the [Activity Definition](/activity-definition).

You can develop an Activity Definition by using the `[Activity]` attribute from the `Temporalio.Activities` namespace on the method.
To register a method as an Activity with a custom name, use an attribute parameter, for example `[Activity("your-activity")]`.
Otherwise, the activity name is the unqualified method name (sans an "Async" suffix if the method is async).

Activities can be asynchronous or synchronous.

```csharp
using Temporalio.Activities;

public class MyActivities
{
// Activities can be async and/or static too. We just demonstrate instance methods since many
// use them that way.
[Activity]
public string MyActivity(MyActivityParams input) =>
$"{input.Greeting}, {input.Name}!";
}
```

There is no explicit limit to the total number of parameters that an [Activity Definition](/activity-definition) may support.
However, there is a limit to the total size of the data that ends up encoded into a gRPC message Payload.

A single argument is limited to a maximum size of 2 MB.
And the total size of a gRPC message, which includes all the arguments, is limited to a maximum of 4 MB.

Also, keep in mind that all Payload data is recorded in the [Workflow Execution Event History](/workflow-execution/event#event-history) and large Event Histories can affect Worker performance.
This is because the entire Event History could be transferred to a Worker Process with a [Workflow Task](/tasks#workflow-task).

Some SDKs require that you pass context objects, others do not.
When it comes to your application data—that is, data that is serialized and encoded into a Payload—we recommend that you use a single object as an argument that wraps the application data passed to Activities.
This is so that you can change what data is passed to the Activity without breaking a method signature.

Activity parameters are the method parameters of the method with the `[Activity]` attribute.
These can be any data type Temporal can convert, including records.
Technically this can be multiple parameters, but Temporal strongly encourages a single parameter containing all input fields.
36 changes: 36 additions & 0 deletions docs/develop/dotnet/activities/dynamic-activity.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
---
id: dynamic-activity
title: Dynamic Activity - .NET SDK
sidebar_label: Dynamic Activity
description: This section explains Dynamic Activities with the .NET SDK
toc_max_heading_level: 4
keywords:
- .NET SDK
tags:
- .NET SDK
- Temporal SDKs
---

## Set a Dynamic Activity {#set-a-dynamic-activity}

**How to set a Dynamic Activity using the Temporal .NET SDK**

A Dynamic Activity in Temporal is an Activity that is invoked dynamically at runtime if no other Activity with the same name is registered.
An Activity can be made dynamic by setting `Dynamic` as `true` on the `[Activity]` attribute.
You must register the Activity with the Worker before it can be invoked.
Only one Dynamic Activity can be present on a Worker.

The Activity Definition must then accept a single argument of type `Temporalio.Converters.IRawValue[]`.
The [PayloadConverter](https://dotnet.temporal.io/api/Temporalio.Activities.ActivityExecutionContext.html#Temporalio_Activities_ActivityExecutionContext_PayloadConverter) property on the `ActivityExecutionContext` is used to convert an `IRawValue` object to the desired type using extension methods in the `Temporalio.Converters` namespace.

```csharp
public class MyActivities
{
[Activity(Dynamic = true)]
public string DynamicActivity(IRawValue[] args)
{
var input = ActivityExecutionContext.Current.PayloadConverter.ToValue<MyActivityParams>(args.Single());
return $"{input.Greeting}, {input.Name}!";
}
}
```
54 changes: 54 additions & 0 deletions docs/develop/dotnet/activities/execution.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
---
id: execution
title: Activity execution - .NET SDK
description: Shows how to perform Activity execution with the .NET SDK
sidebar_label: Activity Execution
slug: /develop/dotnet/activities/execution
toc_max_heading_level: 3
tags:
- .NET SDK
- Temporal SDKs
- Activity
---

## Start Activity Execution {#activity-execution}

Calls to spawn [Activity Executions](/activity-execution) are written within a [Workflow Definition](/workflow-definition).
The call to spawn an Activity Execution generates the [ScheduleActivityTask](/references/commands#scheduleactivitytask) Command.
This results in the set of three [Activity Task](/tasks#activity-task) related Events ([ActivityTaskScheduled](/references/events#activitytaskscheduled), [ActivityTaskStarted](/references/events#activitytaskstarted), and ActivityTask[Closed])in your Workflow Execution Event History.

A single instance of the Activities implementation is shared across multiple simultaneous Activity invocations.
Activity implementation code should be _idempotent_.

The values passed to Activities through invocation parameters or returned through a result value are recorded in the Execution history.
The entire Execution history is transferred from the Temporal service to Workflow Workers when a Workflow state needs to recover.
A large Execution history can thus adversely impact the performance of your Workflow.

Therefore, be mindful of the amount of data you transfer through Activity invocation parameters or Return Values.
Otherwise, no additional limitations exist on Activity implementations.

To spawn an Activity Execution, use the `ExecuteActivityAsync` operation from within your Workflow Definition.

```csharp
using Temporalio.Workflows;

[Workflow]
public class MyWorkflow
{
public async Task<string> RunAsync(string name)
{
var param = MyActivityParams("Hello", name);
return await Workflow.ExecuteActivityAsync(
(MyActivities a) => a.MyActivity(param),
new() { StartToCloseTimeout = TimeSpan.FromMinutes(5) });
}
}
```

Activity Execution semantics rely on several parameters.
The only required value that needs to be set is either a [Schedule-To-Close Timeout](/encyclopedia/detecting-activity-failures#schedule-to-close-timeout) or a [Start-To-Close Timeout](/encyclopedia/detecting-activity-failures#start-to-close-timeout).
These values are set in the Activity Options.

### Get Activity Execution results {#get-activity-results}

The Activity result is the returned in the task from the `ExecuteActivityAsync` call.
25 changes: 25 additions & 0 deletions docs/develop/dotnet/activities/index.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
---
id: index
title: Activities - .NET SDK
sidebar_label: Activities
description: This section explains how to implement Activities with the .NET SDK
toc_max_heading_level: 4
keywords:
- .NET SDK
tags:
- .NET SDK
- Temporal SDKs
---

import * as Components from '@site/src/components';

![.NET SDK Banner](/img/assets/banner-dotnet-temporal.png)

## Activities

- [Activity Basics](/develop/dotnet/activities/basics)
- [Activity Execution](/develop/dotnet/activities/execution)
- [Timeouts](/develop/dotnet/activities/timeouts)
- [Asynchronous Activity Completion](/develop/dotnet/activities/asynchronous-activity)
- [Dynamic Activity](/develop/dotnet/activities/dynamic-activity)
- [Benign exceptions](/develop/dotnet/activities/benign-exceptions)
129 changes: 129 additions & 0 deletions docs/develop/dotnet/activities/timeouts.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
---
id: timeouts
title: Activity Timeouts - .NET SDK
sidebar_label: Timeouts
description: Optimize Workflow Execution with Temporal .NET SDK - Set Activity Timeouts and Retry Policies efficiently.
toc_max_heading_level: 4
keywords:
- .NET
- failure detection
- timeouts
tags:
- Activities
- Workflows
- Errors
- Failures
- .NET SDK
- Temporal SDKs
---

## Activity Timeouts {#activity-timeouts}

Each Activity Timeout controls the maximum duration of a different aspect of an Activity Execution.

The following Timeouts are available in the Activity Options.

- **[Schedule-To-Close Timeout](/encyclopedia/detecting-activity-failures#schedule-to-close-timeout):** is the maximum amount of time allowed for the overall [Activity Execution](/activity-execution).
- **[Start-To-Close Timeout](/encyclopedia/detecting-activity-failures#start-to-close-timeout):** is the maximum time allowed for a single [Activity Task Execution](/tasks#activity-task-execution).
- **[Schedule-To-Start Timeout](/encyclopedia/detecting-activity-failures#schedule-to-start-timeout):** is the maximum amount of time that is allowed from when an [Activity Task](/tasks#activity-task) is scheduled to when a [Worker](/workers#worker) starts that Activity Task.

An Activity Execution must have either the Start-To-Close or the Schedule-To-Close Timeout set.

These values can be set in the `ActivityOptions` when calling `ExecuteActivityAsync`.

Available timeouts are:

- ScheduleToCloseTimeout
- ScheduleToStartTimeout
- StartToCloseTimeout

```csharp
return await Workflow.ExecuteActivityAsync(
(MyActivities a) => a.MyActivity(param),
new() { StartToCloseTimeout = TimeSpan.FromMinutes(5) });
```

### Set an Activity Retry Policy {#activity-retries}

A Retry Policy works in cooperation with the timeouts to provide fine controls to optimize the execution experience.

Activity Executions are automatically associated with a default [Retry Policy](/encyclopedia/retry-policies) if a custom one is not provided.

To create an Activity Retry Policy in .NET, set the `RetryPolicy` on the `ActivityOptions` when calling `ExecuteActivityAsync`.

```csharp
return await Workflow.ExecuteActivityAsync(
(MyActivities a) => a.MyActivity(param),
new()
{
StartToCloseTimeout = TimeSpan.FromMinutes(5),
RetryPolicy = new() { MaximumInterval = TimeSpan.FromSeconds(10) },
});
```

### Override the Retry interval with `nextRetryDelay` {#next-retry-delay}

When you throw an [Application Failure](/references/failures#application-failure) and assign the `nextRetryDelay` field, its value replaces and overrides the Retry interval defined in the active Retry Policy.

For example, you might scale the next Retry delay interval based on the current number of attempts.
Here's how you'd do that in an Activity.
In the following sample, the `attempt` count is retrieved from the Activity Execution context and used to set the number of seconds for the next Retry delay:

```csharp
var attempt = ActivityExecutionContext.Current.Info.Attempt;

throw new ApplicationFailureException(
$"Something bad happened on attempt {attempt}",
errorType: "my_failure_type",
nextRetryDelay: TimeSpan.FromSeconds(3 * attempt));
```

## Heartbeat an Activity {#activity-heartbeats}

An [Activity Heartbeat](/encyclopedia/detecting-activity-failures#activity-heartbeat) is a ping from the [Worker Process](/workers#worker-process) that is executing the Activity to the [Temporal Service](/temporal-service).
Each Heartbeat informs the Temporal Service that the [Activity Execution](/activity-execution) is making progress and the Worker has not crashed.
If the Temporal Service does not receive a Heartbeat within a [Heartbeat Timeout](/encyclopedia/detecting-activity-failures#heartbeat-timeout) time period, the Activity will be considered failed and another [Activity Task Execution](/tasks#activity-task-execution) may be scheduled according to the Retry Policy.

Heartbeats may not always be sent to the Temporal Service—they may be [throttled](/encyclopedia/detecting-activity-failures#throttling) by the Worker.

Activity Cancellations are delivered to Activities from the Temporal Service when they Heartbeat. Activities that don't Heartbeat can't receive a Cancellation.
Heartbeat throttling may lead to Cancellation getting delivered later than expected.

Heartbeats can contain a `Details` field describing the Activity's current progress.
If an Activity gets retried, the Activity can access the `Details` from the last Heartbeat that was sent to the Temporal Service.

To Heartbeat an Activity Execution in .NET, use the [`Heartbeat()`](https://dotnet.temporal.io/api/Temporalio.Activities.ActivityExecutionContext.html#Temporalio_Activities_ActivityExecutionContext_Heartbeat_System_Object___) method on the `ActivityExecutionContext`.

```csharp
[Activity]
public async Task MyActivityAsync()
{
while (true)
{
// Send heartbeat
ActivityExecutionContext.Current.Heartbeat();

// Do some work, passing the cancellation token
await Task.Delay(1000, ActivityExecutionContext.Current.CancellationToken);
}
}
```

In addition to obtaining cancellation information, Heartbeats also support detail data that persists on the server for retrieval during Activity retry.
If an Activity calls `Heartbeat(123, 456)` and then fails and is retried, `HeartbeatDetails` on the `ActivityInfo` returns an collection containing `123` and `456` on the next Run.

### Set a Heartbeat Timeout {#heartbeat-timeout}

A [Heartbeat Timeout](/encyclopedia/detecting-activity-failures#heartbeat-timeout) works in conjunction with [Activity Heartbeats](/encyclopedia/detecting-activity-failures#activity-heartbeat).

`HeartbeatTimeout` is a property on `ActivityOptions` for `ExecuteActivityAsync` used to set the maximum time between Activity Heartbeats.

```csharp
await Workflow.ExecuteActivityAsync(
(MyActivities a) => a.MyActivity(param),
new()
{
StartToCloseTimeout = TimeSpan.FromMinutes(5),
HeartbeatTimeout = TimeSpan.FromSeconds(30),
});
```
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,6 @@ When working with sensitive data, you should always implement Payload encryption

## Custom Payload Codec {#custom-payload-codec}

**How to use a custom Payload Codec using the .NET SDK**

Custom Data Converters can change the default Temporal Data Conversion behavior by adding hooks, sending payloads to external storage, or performing different encoding steps.
If you only need to change the encoding performed on your payloads -- by adding compression or encryption -- you can override the default Data Converter to use a new `PayloadCodec`.

Expand Down Expand Up @@ -121,8 +119,6 @@ Temporal's Converter architecture looks like this:

### Custom Payload Converter {#custom-payload-converter}

**How to use a custom Payload Converter with the .NET SDK.**

Data converters are used to convert raw Temporal payloads to/from actual .NET types.
A custom data converter can be set via the `DataConverter` option when creating a client. Data converters are a combination of payload converters, payload codecs, and failure converters.
Payload converters convert .NET values to/from serialized bytes. Payload codecs convert bytes to bytes (e.g. for compression or encryption). Failure converters convert exceptions to/from serialized failures.
Expand Down
Loading