diff --git a/daprdocs/content/en/developing-applications/building-blocks/workflow/workflow-multi-app.md b/daprdocs/content/en/developing-applications/building-blocks/workflow/workflow-multi-app.md index 6ca409e4a06..b216474c435 100644 --- a/daprdocs/content/en/developing-applications/building-blocks/workflow/workflow-multi-app.md +++ b/daprdocs/content/en/developing-applications/building-blocks/workflow/workflow-multi-app.md @@ -78,8 +78,9 @@ Currently the following are supported: - **Java** (**only** activity calls) - **Go** (**both** activities and child workflows calls) - **Python** (**both** activities and child workflows calls) -- The .NET and JavaScript SDKs support are planned for future releases -{{% /alert %}} +- **.NET** (**both** activities and child workflows calls) +- Support is planned for future releases for the JavaScript SDK. + {{% /alert %}} ## Error handling @@ -140,6 +141,28 @@ public class BusinessWorkflow implements Workflow { {{% /tab %}} +{{% tab ".NET" %}} + +```csharp +// Specify App ID during workflow registration +builder.Services.AddDaprWorkflowBuilder(opt => + { + opt.RegisterWorkflow(); + opt.RegisterActivity(); + }); + +// Call activity in another application +public sealed class WorkflowA : Workflow +{ + public override Task RunAsync(WorkflowContext context, int input) => + context.CallActivityAsync(nameof("AnotherActivity"), input, new WorkflowTaskOptions( + targetAppId: "my-other-app")); + }); +} +``` + +{{% /tab %}} + {{% tab "Python" %}} ```python @@ -181,6 +204,28 @@ func BusinessWorkflow(ctx *workflow.WorkflowContext) (any, error) { {{% /tab %}} +{{% tab ".NET" %}} + +```csharp +// Specify App ID during workflow registration +builder.Services.AddDaprWorkflowBuilder(opt => + { + opt.RegisterWorkflow(); + opt.RegisterActivity(); + }); + +// Call child workflow in another application +public sealed class WorkflowA : Workflow +{ + public override Task RunAsync(WorkflowContext context, int input) => + context.CallChildWorkflow(nameof("AnotherWorkflow"), input, new ChildWorkflowTaskOptions( + TargetAppId: "my-other-app" + }); +} +``` + +{{% /tab %}} + {{% tab "Python" %}} ```python @@ -200,8 +245,8 @@ def workflow1(ctx: wf.DaprWorkflowContext): - [Workflow overview]({{% ref workflow-overview.md %}}) - [Workflow API reference]({{% ref workflow_api.md %}}) - Try out the following examples: - - [Python](https://github.com/dapr/python-sdk/tree/master/examples/demo_workflow) - - [JavaScript](https://github.com/dapr/js-sdk/tree/main/examples/workflow) - - [.NET](https://github.com/dapr/dotnet-sdk/tree/master/examples/Workflow) - - [Java](https://github.com/dapr/java-sdk/tree/master/examples/src/main/java/io/dapr/examples/workflows) - - [Go](https://github.com/dapr/go-sdk/tree/main/examples/workflow/README.md) + - [Python](https://github.com/dapr/python-sdk/tree/master/examples/demo_workflow) + - [JavaScript](https://github.com/dapr/js-sdk/tree/main/examples/workflow) + - [.NET](https://github.com/dapr/dotnet-sdk/tree/master/examples/Workflow) + - [Java](https://github.com/dapr/java-sdk/tree/master/examples/src/main/java/io/dapr/examples/workflows) + - [Go](https://github.com/dapr/go-sdk/tree/main/examples/workflow/README.md) diff --git a/sdkdocs/dotnet/content/en/dotnet-sdk-docs/dotnet-workflow/dotnet-workflow-management-methods.md b/sdkdocs/dotnet/content/en/dotnet-sdk-docs/dotnet-workflow/dotnet-workflow-management-methods.md index 52bfe4ae2b8..9d50c95792a 100644 --- a/sdkdocs/dotnet/content/en/dotnet-sdk-docs/dotnet-workflow/dotnet-workflow-management-methods.md +++ b/sdkdocs/dotnet/content/en/dotnet-sdk-docs/dotnet-workflow/dotnet-workflow-management-methods.md @@ -8,11 +8,16 @@ description: Learn how to use the `DaprWorkflowClient` to manage workflows ## Workflow management operations with `DaprWorkflowClient` -The `DaprWorkflowClient` class provides methods to manage workflow instances. Below are the operations you can perform using the `DaprWorkflowClient`. +The `DaprWorkflowClient` class provides methods to manage workflow instances. Below are the operations you can perform +using the `DaprWorkflowClient`. ### Schedule a new workflow instance -To start a new workflow instance, use the `ScheduleNewWorkflowAsync` method. This method requires the workflow type name and an input required by the workflow. The workflow `instancedId` is an optional argument; if not provided, a new GUID is generated by the `DaprWorkflowClient`. The final optional argument is a `startTime` of type `DateTimeOffset` which can be used to define when the workflow instance should start. The method returns the `instanceId` of the scheduled workflow which is used for other workflow management operations. +To start a new workflow instance, use the `ScheduleNewWorkflowAsync` method. This method requires the workflow type +name and an input required by the workflow. The workflow `instancedId` is an optional argument; if not provided, a +new GUID is generated by the `DaprWorkflowClient`. The final optional argument is a `startTime` of type +`DateTimeOffset` which can be used to define when the workflow instance should start. The method returns the +`instanceId` of the scheduled workflow which is used for other workflow management operations. ```csharp var instanceId = $"order-workflow-{Guid.NewGuid().ToString()[..8]}"; @@ -23,9 +28,26 @@ await daprWorkflowClient.ScheduleNewWorkflowAsync( input); ``` +### Calling activities and child workflows in another application (multi-app workflows) + +Dapr supports **multi-application workflows**, where a workflow can call activities or child workflows that are hosted +(defined and registered) in a *different* Dapr application. +See [Multi Application Workflows]({{% ref "workflow-multi-app.md" %}}) for the conceptual overview and constraints. + +Using multi-application workflows in .NET requires: + +- Setting `TargetAppId` when invoking work on another application: + + - **Calling a child workflow in another app**: set `TargetAppId` on `ChildWorkflowTaskOptions`. + - **Calling an activity in another app**: set `TargetAppId` on `WorkflowTaskOptions`. + +This causes the activity/child-workflow execution to be invoked on the specified target application's App ID, while +the parent workflow continues to orchestrate and receives the result. + ### Retrieve the status of a workflow instance -To get the current status of a workflow instance, use the `GetWorkflowStateAsync` method. This method requires the instance ID of the workflow and returns a `WorkflowStatus` object containing details about the workflow's current state. +To get the current status of a workflow instance, use the `GetWorkflowStateAsync` method. This method requires the +instance ID of the workflow and returns a `WorkflowStatus` object containing details about the workflow's current state. ```csharp var workflowStatus = await daprWorkflowClient.GetWorkflowStateAsync(instanceId); @@ -33,7 +55,8 @@ var workflowStatus = await daprWorkflowClient.GetWorkflowStateAsync(instanceId); ### Raise an event to a running workflow instance -To send an event to a running workflow instance that is waiting for an external event, use the `RaiseEventAsync` method. This method requires the instance ID of the workflow, the name of the event, and optionally the event payload. +To send an event to a running workflow instance that is waiting for an external event, use the `RaiseEventAsync` +method. This method requires the instance ID of the workflow, the name of the event, and optionally the event payload. ```csharp await daprWorkflowClient.RaiseEventAsync(instanceId, "Approval", true); @@ -41,7 +64,8 @@ await daprWorkflowClient.RaiseEventAsync(instanceId, "Approval", true); ### Suspend a running workflow instance -A running workflow instance can be paused using the `SuspendWorkflowAsync` method. This method requires the instance ID of the workflow. You can optionally provide a reason for suspending the workflow. +A running workflow instance can be paused using the `SuspendWorkflowAsync` method. This method requires the instance +ID of the workflow. You can optionally provide a reason for suspending the workflow. ```csharp await daprWorkflowClient.SuspendWorkflowAsync(instanceId); @@ -49,7 +73,8 @@ await daprWorkflowClient.SuspendWorkflowAsync(instanceId); ### Resume a suspended workflow instance -A suspended workflow instance can be resumed using the `ResumeWorkflowAsync` method. This method requires the instance ID of the workflow. You can optionally provide a reason for resuming the workflow. +A suspended workflow instance can be resumed using the `ResumeWorkflowAsync` method. This method requires the instance +ID of the workflow. You can optionally provide a reason for resuming the workflow. ```csharp await daprWorkflowClient.ResumeWorkflowAsync(instanceId); @@ -57,7 +82,9 @@ await daprWorkflowClient.ResumeWorkflowAsync(instanceId); ### Terminate a workflow instance -To terminate a workflow instance, use the `TerminateWorkflowAsync` method. This method requires the instance ID of the workflow. You can optionally provide an `output` argument of type `string`. Terminating a workflow instance will also terminal all child workflow instances but it has no impact on in-flight activity executions. +To terminate a workflow instance, use the `TerminateWorkflowAsync` method. This method requires the instance ID of +the workflow. You can optionally provide an `output` argument of type `string`. Terminating a workflow instance +will also terminal all child workflow instances but it has no impact on in-flight activity executions. ```csharp await daprWorkflowClient.TerminateWorkflowAsync(instanceId); @@ -65,7 +92,9 @@ await daprWorkflowClient.TerminateWorkflowAsync(instanceId); ### Purge a workflow instance -To remove the workflow instance history from the Dapr Workflow state store, use the `PurgeWorkflowAsync` method. This method requires the instance ID of the workflow. Only completed, failed, or terminated workflow instances can be purged. +To remove the workflow instance history from the Dapr Workflow state store, use the `PurgeWorkflowAsync` method. +This method requires the instance ID of the workflow. Only completed, failed, or terminated workflow instances +can be purged. ```csharp await daprWorkflowClient.PurgeWorkflowAsync(instanceId); @@ -73,5 +102,5 @@ await daprWorkflowClient.PurgeWorkflowAsync(instanceId); ## Next steps -- [Learn how to author workflows and activities]({{% ref howto-author-workflow.md %}}) +- [Learn how to author workflows and activities]({{% ref howto-author-workflow.md %}}) - [Try the Dapr Workflow quickstart]({{% ref workflow-quickstart.md %}}) diff --git a/sdkdocs/dotnet/content/en/dotnet-sdk-docs/dotnet-workflow/dotnet-workflowclient-usage.md b/sdkdocs/dotnet/content/en/dotnet-sdk-docs/dotnet-workflow/dotnet-workflowclient-usage.md index 0e6fa5e2a93..87cdd9fba58 100644 --- a/sdkdocs/dotnet/content/en/dotnet-sdk-docs/dotnet-workflow/dotnet-workflowclient-usage.md +++ b/sdkdocs/dotnet/content/en/dotnet-sdk-docs/dotnet-workflow/dotnet-workflowclient-usage.md @@ -17,7 +17,7 @@ cleanup of resources. The `AddDaprWorkflow()` method will register the Dapr workflow services with ASP.NET Core dependency injection. This method requires an options delegate that defines each of the workflows and activities you wish to register and use in your application. -{{% alert title="Note" color="primary" %}} +{{% alert title="Note" color="primary" %}} This method will attempt to register a `DaprClient` instance, but this will only work if it hasn't already been registered with another lifetime. For example, an earlier call to `AddDaprClient()` with a singleton lifetime will always use a singleton regardless of the @@ -25,7 +25,7 @@ lifetime chose for the workflow client. The `DaprClient` instance will be used t yet registered, the lifetime provided during the `AddDaprWorkflow()` registration will be used to register the `DaprWorkflowClient` as well as its own dependencies. -{{% /alert %}} +{{% /alert %}} ### Singleton Registration @@ -119,7 +119,7 @@ Now, you can use this client to perform workflow management operations such as s Workflow activities support the same dependency injection that developers have come to expect of modern C# applications. Assuming a proper registration at startup, any such type can be injected into the constructor of the workflow activity and available to utilize during -the execution of the workflow. This makes it simple to add logging via an injected `ILogger` or access to other Dapr +the execution of the workflow. This makes it simple to add logging via an injected `ILogger` or access to other Dapr building blocks by injecting `DaprClient` or `DaprJobsClient`, for example. ```csharp @@ -145,17 +145,17 @@ internal sealed class SquareNumberActivity : WorkflowActivity ### Using ILogger in Workflow -Because workflows must be deterministic, it is not possible to inject arbitrary services into them. For example, +Because workflows must be deterministic, it is not possible to inject arbitrary services into them. For example, if you were able to inject a standard `ILogger` into a workflow and it needed to be replayed because of an error, subsequent replay from the event source log would result in the log recording additional operations that didn't actually -take place a second or third time because their results were sourced from the log. This has the potential to introduce -a significant amount of confusion. Rather, a replay-safe logger is made available for use within workflows. It will only +take place a second or third time because their results were sourced from the log. This has the potential to introduce +a significant amount of confusion. Rather, a replay-safe logger is made available for use within workflows. It will only log events the first time the workflow runs and will not log anything whenever the workflow is being replaced. This logger can be retrieved from a method present on the `WorkflowContext` available on your workflow instance and otherwise used precisely as you might otherwise use an `ILogger` instance. -An end-to-end sample demonstrating this can be seen in the +An end-to-end sample demonstrating this can be seen in the [.NET SDK repository](https://github.com/dapr/dotnet-sdk/blob/master/examples/Workflow/WorkflowConsoleApp/Workflows/OrderProcessingWorkflow.cs) but a brief extraction of this sample is available below.