# Cancellation - .NET SDK

This page shows how to interrupt a Workflow Execution.

You can interrupt a Workflow Execution in one of the following ways:

- [Cancel](#cancellation): Canceling a Workflow provides a graceful way to stop Workflow Execution.
- [Terminate](#termination): Terminating a Workflow forcefully stops Workflow Execution.

Terminating a Workflow forcefully stops Workflow Execution. This action resembles killing a process.

- The system records a `WorkflowExecutionTerminated` event in the Event History.
- The termination forcefully and immediately stops the Workflow Execution.
- The Workflow code gets no chance to handle termination.
- A Workflow Task doesn't get scheduled.

In most cases, canceling is preferable because it allows the Workflow to finish gracefully. Terminate only if the
Workflow is stuck and cannot be canceled normally.

## Cancellation 

To give a Workflow and its Activities the ability to be cancelled, do the following:

- Handle a Cancellation request within a Workflow.
- Set Activity Heartbeat Timeouts.
- Listen for and handle a Cancellation request within an Activity.
- Send a Cancellation request from a Temporal Client.

### Handle Cancellation in Workflow 

Workflow Definitions can be written to respond to cancellation requests. It is common for an Activity to be run on
Cancellation to perform cleanup.

Cancellation Requests on Workflows cancel the `Workflow.CancellationToken`. This is the token that is implicitly used
for all calls within the workflow as well (e.g. Timers, Activities, etc) and therefore cancellation is propagated to
them to be handled and bubble out.

```csharp
[WorkflowRun]
public async Task RunAsync()
{
    try
    {
        // Whether this workflow waits on the activity to handle the cancellation or not is
        // dependent upon the CancellationType option. We leave the default here which sends the
        // cancellation but does not wait on it to be handled.
        await Workflow.ExecuteActivityAsync(
            (MyActivities a) => a.MyNormalActivity(),
            new() { ScheduleToCloseTimeout = TimeSpan.FromMinutes(5) });
    }
    catch (Exception e) when (TemporalException.IsCanceledException(e))
    {
        // The "when" clause above is because we only want to apply the logic to cancellation, but
        // this kind of cleanup could be done on any/all exceptions too.
        Workflow.Logger.LogError(e, "Cancellation occurred, performing cleanup");

        // Call cleanup activity. If this throws, it will swallow the original exception which we
        // are ok with here. This could be changed to just log a failure and let the original
        // cancellation continue. 
        // The default token on Workflow.CancellationToken is now marked
        // cancelled, so we pass a different one. We use CancellationToken.None here because the
        // cleanup activity itself doesn't need to be cancellable; if it did (e.g. you want to
        // cancel cleanup from a timeout or another signal), create a new detached
        // CancellationTokenSource and pass its Token instead.
        await Workflow.ExecuteActivityAsync(
            (MyActivities a) => a.MyCancellationCleanupActivity(),
            new()
            {
                ScheduleToCloseTimeout = TimeSpan.FromMinutes(5),
                CancellationToken = CancellationToken.None,
            });

        // Rethrow the cancellation
        throw;
    }
}
```

### Handle Cancellation in an Activity 

Ensure that the Activity is [Heartbeating](/develop/dotnet/activities/timeouts#activity-heartbeats) to receive the
Cancellation request and stop execution. Also make sure that the
[Heartbeat Timeout](/develop/dotnet/activities/timeouts#heartbeat-timeout) is set on the Activity Options when calling
from the Workflow. An Activity Cancellation Request cancels the `CancellationToken` on the `ActivityExecutionContext`.

```csharp
[Activity]
public async Task MyActivityAsync()
{
    // This is a naive loop simulating work, but similar heartbeat/cancellation logic applies to
    // other scenarios as well
    while (true)
    {
        // Send heartbeat
        ActivityExecutionContext.Current.Heartbeat();

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

### Request Cancellation 

Use `CancelAsync` on the `WorkflowHandle` to cancel a Workflow Execution.

```csharp
// Get a workflow handle by its workflow ID. This could be made specific to a run by passing run ID.
// This could also just be a handle that is returned from StartWorkflowAsync instead.
var handle = myClient.GetWorkflowHandle("my-workflow-id");

// Send cancellation. This returns when cancellation is received by the server. Wait on the handle's
// result to wait for cancellation to be applied.
await handle.CancelAsync();
```

#### How to request Cancellation of an Activity

By default, Activities are automatically cancelled when the Workflow is cancelled since the workflow cancellation token
is used by activities by default. To issue a cancellation explicitly, a new cancellation token can be created.

```csharp
[WorkflowRun]
public async Task RunAsync()
{
    // Create a source linked to workflow cancellation. A new source could be created instead if we
    // didn't want it associated with workflow cancellation.
    using var cancelActivitySource = CancellationTokenSource.CreateLinkedTokenSource(
        Workflow.CancellationToken);

    // Start the activity. Whether this workflow waits on the activity to handle the cancellation
    // or not is dependent upon the CancellationType option. We leave the default here which sends
    // the cancellation but does not wait on it to be handled.
    var activityTask = Workflow.ExecuteActivityAsync(
        (MyActivities a) => a.MyNormalActivity(),
        new()
        {
            ScheduleToCloseTimeout = TimeSpan.FromMinutes(5),
            CancellationToken = cancelActivitySource.Token;
        });
    activityTask.Start();

    // Wait 5 minutes, then cancel it
    await Workflow.DelayAsync(TimeSpan.FromMinutes(5));
    cancelActivitySource.Cancel();

    // Wait on the activity which will throw cancellation which will fail the workflow
    await activityTask;
}
```

## Termination 

To Terminate a Workflow Execution in .NET, use the
[TerminateAsync()](https://dotnet.temporal.io/api/Temporalio.Client.WorkflowHandle.html#Temporalio_Client_WorkflowHandle_TerminateAsync_System_String_Temporalio_Client_WorkflowTerminateOptions_)
method on the Workflow handle.

```csharp
// Get a workflow handle by its workflow ID. This could be made specific to a run by passing run ID.
// This could also just be a handle that is returned from StartWorkflowAsync instead.
var handle = myClient.GetWorkflowHandle("my-workflow-id");

// Terminate
await handle.TerminateAsync();
```

Workflow Executions can also be Terminated directly from the WebUI. In this case, a custom note can be logged from the
UI when that happens.

## Reset a Workflow Execution 

Resetting a Workflow Execution terminates the current Workflow Execution and starts a new Workflow Execution from a
point you specify in its Event History. Use reset when a Workflow is blocked due to a non-deterministic error or other
issues that prevent it from completing.

When you reset a Workflow, the Event History up to the reset point is copied to the new Workflow Execution, and the
Workflow resumes from that point with the current code. Reset only works if you've fixed the underlying issue, such as
removing non-deterministic code. Any progress made after the reset point will be discarded. Provide a reason when
resetting, as it will be recorded in the Event History.

**Web UI**

1. Navigate to the Workflow Execution details page,
2. Click the **Reset** button in the top right dropdown menu,
3. Select the Event ID to reset to,
4. Provide a reason for the reset,
5. Confirm the reset.

The Web UI shows available reset points and creates a link to the new Workflow Execution after the reset completes.

**Temporal CLI**

Use the `temporal workflow reset` command to reset a Workflow Execution:

```bash
temporal workflow reset \
    --workflow-id <workflow-id> \
    --event-id <event-id> \
    --reason "Reason for reset"
```

For example:

```bash
temporal workflow reset \
    --workflow-id my-background-check \
    --event-id 4 \
    --reason "Fixed non-deterministic code"
```

By default, the command resets the latest Workflow Execution in the `default` Namespace. Use `--run-id` to reset a
specific run. Use `--namespace` to specify a different Namespace:

```bash
temporal workflow reset \
    --workflow-id my-background-check \
    --event-id 4 \
    --reason "Fixed non-deterministic code" \
    --namespace my-namespace \
    --tls-cert-path /path/to/cert.pem \
    --tls-key-path /path/to/key.pem
```

Monitor the new Workflow Execution after resetting to ensure it completes successfully.
