Dynamic Workflow definition in .NET SDK
A Dynamic Workflow in Temporal is a Workflow that is invoked dynamically at runtime if no other Workflow with the same name is registered. A Workflow can be made dynamic by setting Dynamic as true on the [Workflow] attribute. The Workflow Definition must then accept a single argument of type Temporalio.Converters.IRawValue[].
Dynamic Workflow registration requirement
A Dynamic Workflow must be registered with the Worker before it can be invoked. Only one Dynamic Workflow can be present on a Worker.
IRawValue conversion in Dynamic Workflow .NET
The Workflow.PayloadConverter property is used to convert an IRawValue object to the desired type using extension methods in the Temporalio.Converters namespace.
Dynamic Workflow example in .NET
```csharp
[Workflow(Dynamic = true)]
public class DynamicWorkflow
{
[WorkflowRun]
public async Task<string> RunAsync(IRawValue[] args)
{
var name = Workflow.PayloadConverter.ToValue<string>(args.Single());
var param = MyActivityParams("Hello", name);
return await Workflow.ExecuteActivityAsync(
(MyActivities a) => a.MyActivity(param),
new() { StartToCloseTimeout = TimeSpan.FromMinutes(5) });
}
}
```
This example demonstrates how to create a Dynamic Workflow in the .NET SDK that accepts an IRawValue array argument, converts it to a string, and passes it to an activity.
Asynchronous Activity completion code example - .NET
// Capture token for later completion
capturedToken = ActivityExecutionContext.Current.Info.TaskToken;
// Throw special exception that says an activity will be completed somewhere else
throw new CompleteAsyncException();
var handle = myClient.GetAsyncActivityHandle(capturedToken);
await handle.CompleteAsync("Completion value.");
Dynamic Activity example in .NET
Example of a Dynamic Activity in .NET:
```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}!";
}
}
```
This example shows a Dynamic Activity that accepts arguments as IRawValue[], converts them to a specific type (MyActivityParams) using the PayloadConverter, and returns a formatted string.
.NET SDK Standalone Activity example code
Example Activity definition:
```csharp
namespace TemporalioSamples.StandaloneActivity;
using Temporalio.Activities;
public static class MyActivities
{
[Activity]
public static Task<string> ComposeGreetingAsync(ComposeGreetingInput input) =>
Task.FromResult($"{input.Greeting}, {input.Name}!");
}
public record ComposeGreetingInput(string Greeting, string Name);
```
Example Worker setup:
```csharp
using var worker = new TemporalWorker(
client,
new TemporalWorkerOptions(taskQueue).
AddActivity(MyActivities.ComposeGreetingAsync));
await worker.ExecuteAsync(tokenSource.Token);
```
Example executing Standalone Activity:
```csharp
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}");
```
Example starting activity without waiting:
```csharp
var handle = await client.StartActivityAsync(
() => MyActivities.ComposeGreetingAsync(new ComposeGreetingInput("Hello", "World")),
new("standalone-activity-id", "standalone-activity-sample")
{
ScheduleToCloseTimeout = TimeSpan.FromSeconds(10),
});
var result = await handle.GetResultAsync();
```
Example listing activities:
```csharp
await foreach (var info in client.ListActivitiesAsync(
"TaskQueue = 'standalone-activity-sample'"))
{
Console.WriteLine(
$"ActivityID: {info.ActivityId}, Type: {info.ActivityType}, Status: {info.Status}");
}
```
Example counting activities:
```csharp
var resp = await client.CountActivitiesAsync(
"TaskQueue = 'standalone-activity-sample'");
Console.WriteLine($"Total activities: {resp.Count}");
```
Activity parameter design recommendation
Temporal strongly encourages using a single object parameter containing all input fields for Activities, rather than multiple parameters. This allows changing what data is passed to the Activity without breaking the method signature.
ExecuteChildWorkflowAsync with Parent Close Policy example .NET code
await Workflow.ExecuteChildWorkflowAsync(
(MyChildWorkflow wf) => wf.RunAsync(),
new() { ParentClosePolicy = ParentClosePolicy.Abandon });
StartChildWorkflowAsync and ExecuteChildWorkflowAsync wait for ChildWorkflowExecutionStarted
In .NET, awaiting StartChildWorkflowAsync() or ExecuteChildWorkflowAsync() internally waits for the ChildWorkflowExecutionStarted Event before returning, so the Child Workflow is guaranteed to have started once the call resolves.
Child Workflow started from non-main context timing requirement
If you start a Child Workflow from a non-main context (for example, a Signal or Update handler), make sure the Parent Workflow doesn't complete before that call resolves.
ExecuteChildWorkflowAsync method purpose
ExecuteChildWorkflowAsync() method starts a Child Workflow and waits for completion. It is a helper method that combines StartChildWorkflowAsync() plus await handle.GetResultAsync().
StartChildWorkflowAsync method purpose
StartChildWorkflowAsync() method starts a Child Workflow and returns its handle. This is useful if you want to do something after it has only started, get the Workflow/Run ID, or signal it while running.
Setting Parent Close Policy in .NET
Set the ParentClosePolicy property inside the ChildWorkflowOptions for ExecuteChildWorkflowAsync or StartChildWorkflowAsync to specify the behavior of the Child Workflow when the Parent Workflow closes.
ExecuteChildWorkflowAsync example .NET code
await Workflow.ExecuteChildWorkflowAsync((MyChildWorkflow wf) => wf.RunAsync());