-
Notifications
You must be signed in to change notification settings - Fork 548
Workflow tutorials C# #1182
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Workflow tutorials C# #1182
Changes from all commits
Commits
Show all changes
71 commits
Select commit
Hold shift + click to select a range
3ce6f18
Add C# TaskChaining
marcduiker 4f59915
Add C# FanOutFanIn
marcduiker eaeaaae
Add C# Monitor
marcduiker 2da416e
Add C# ExternalEvent
marcduiker b2d70f1
Add C# ChildWorkflows
marcduiker 72668f5
Add C# CombinedPatterns
marcduiker 8ee7b05
Add C# WorkflowManagement
marcduiker 9cc59c6
Fix port numbers
marcduiker 53996a5
Add C# Basics
marcduiker ca2f2ad
Add READMEs
marcduiker 7ed9923
Update READMEs
marcduiker 4bc32fd
Update folder structure
marcduiker b2f3dbc
Update Basic Workflow
marcduiker 790c73e
Add BasicWorkflow comments
marcduiker 551f81e
Add mermaid diagrams
marcduiker a44a0bd
Add code comments
marcduiker 2f154ea
Merge branch 'master' into workflow-tutorials
marcduiker d2eeaac
Combine pattern examples is working
marcduiker 2f67550
Add diagram
marcduiker 92ffc2e
Add README content for challenges& tips
marcduiker 9c04f72
Update to .NET 9
marcduiker 9562e15
Use file based namespaces, make classes internal sealed
marcduiker 39fcfcf
Make classes internal sealed
marcduiker 45d3a43
Fix paths for multi-app run
marcduiker c369d3d
Merge branch 'master' into workflow-tutorials
WhitWaldo 7214b5b
Add workflow input and outputs to READMEs
marcduiker c3906d1
Add resiliency-and-compensation
marcduiker 29443b0
Add mechanical markdown
marcduiker 5c3e991
Add make files
marcduiker c47bd4d
Update prerequisites
marcduiker fffb2c2
Add workflow validation to GH WF
marcduiker 762e1d2
PR review updates
marcduiker d9e0010
Update tutorials/workflow/csharp/fundamentals/README.md
marcduiker 7346acb
Update tutorials/workflow/csharp/fundamentals/README.md
marcduiker 238b45c
Update fundamentals
marcduiker db727ed
Add cURL to task chaining
marcduiker 80af9f5
Add comments to Program
marcduiker dfbee09
Update tutorials/workflow/csharp/fan-out-fan-in/README.md
marcduiker 61dfa56
Update tutorials/workflow/csharp/fan-out-fan-in/README.md
marcduiker 7f96082
Add cURL for fanout/fanin
marcduiker dcd7dba
Update tutorials/workflow/csharp/monitor-pattern/README.md
marcduiker 4d9cc1b
Clarify workflow
marcduiker ef2329e
Update tutorials/workflow/csharp/monitor-pattern/README.md
marcduiker c468185
Add cURL commands
marcduiker 040191f
Add optional IDE
marcduiker 61843f3
Update tutorials/workflow/csharp/external-system-interaction/README.md
marcduiker 3d46dea
Update tutorials/workflow/csharp/external-system-interaction/README.md
marcduiker b26da7a
Update tutorials/workflow/csharp/external-system-interaction/README.md
marcduiker 0545347
Update tutorials/workflow/csharp/external-system-interaction/External…
marcduiker a6d3e04
Add cURL
marcduiker 1ab6965
Update tutorials/workflow/csharp/child-workflows/README.md
marcduiker e5808e8
Update tutorials/workflow/csharp/child-workflows/README.md
marcduiker a9334b2
Update tutorials/workflow/csharp/child-workflows/README.md
marcduiker 9bc26ea
Add cURL
marcduiker 496adf1
Disable workflow tutorial validation until mechanical markdown can in…
marcduiker 48ff1d6
Update tutorials/workflow/csharp/resiliency-and-compensation/README.md
marcduiker 79d52b3
Update tutorials/workflow/csharp/resiliency-and-compensation/README.md
marcduiker ca9c95e
Update tutorials/workflow/csharp/resiliency-and-compensation/README.md
marcduiker ddce003
Update tutorials/workflow/csharp/resiliency-and-compensation/README.md
marcduiker 61873a1
Add SetCustomStatus and cURL commands
marcduiker 1403628
Update tutorials/workflow/csharp/combined-patterns/README.md
marcduiker 7ac2aed
Update tutorials/workflow/csharp/combined-patterns/WorkflowApp/Activi…
marcduiker 4ccd420
Update tutorials/workflow/csharp/combined-patterns/WorkflowApp/Activi…
marcduiker 4fdb24d
Add clarifying comments
marcduiker 85e5523
Update tutorials/workflow/csharp/combined-patterns/README.md
marcduiker eacb636
Update tutorials/workflow/csharp/combined-patterns/README.md
marcduiker 7512c2e
Add cURL
marcduiker 9faee39
Update tutorials/workflow/csharp/workflow-management/README.md
marcduiker 11ee6db
Add clarifying comments
marcduiker 1dc008f
Update tutorials/workflow/csharp/challenges-tips/README.md
marcduiker 4918bda
Add -i to get the headers
marcduiker File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| # Using the Dapr Workflow API with C# | ||
|
|
||
| This folder contains tutorials of using the Dapr Workflow API with C#. All examples can be run locally on your machine. | ||
|
|
||
| Before you start, it's recommended to read though the Dapr docs to get familiar with the many [Workflow features, concepts, and patterns](https://docs.dapr.io/developing-applications/building-blocks/workflow/). | ||
|
|
||
| ## Prerequisites | ||
|
|
||
| - [Docker Desktop](https://www.docker.com/products/docker-desktop/) | ||
| - [Dapr CLI](https://docs.dapr.io/getting-started/install-dapr-cli/) & [Initialization](https://docs.dapr.io/getting-started/install-dapr-selfhost/) | ||
| - [.NET 9](https://dotnet.microsoft.com/download/dotnet/9.0) | ||
| - Optional: An IDE such as [VSCode](https://code.visualstudio.com/download) with a [REST client](https://marketplace.visualstudio.com/items?itemName=humao.rest-client). | ||
|
|
||
| ## Tutorials | ||
|
|
||
| - [Workflow Basics](./fundamentals/README.md) | ||
| - [Task Chaining](./task-chaining/README.md) | ||
| - [Fan-out/Fan-in](./fan-out-fan-in/README.md) | ||
| - [Monitor](./monitor-pattern/README.md) | ||
| - [External Events](./external-system-interaction/README.md) | ||
| - [Child Workflows](./child-workflows/README.md) | ||
| - [Resiliency & Compensation](./resiliency-and-compensation/README.md) | ||
| - [Combined Patterns](./combined-patterns/README.md) | ||
| - [WorkflowManagement](./workflow-management/README.md) | ||
| - [Challenges & Tips](./challenges-tips/README.md) | ||
47 changes: 47 additions & 0 deletions
47
tutorials/workflow/csharp/challenges-tips/DeterministicWorkflow.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| using System; | ||
| using Dapr.Workflow; | ||
|
|
||
| namespace WorkflowApp; | ||
|
|
||
| internal sealed class NonDeterministicWorkflow : Workflow<string, string> | ||
| { | ||
| public override async Task<string> RunAsync(WorkflowContext context, string orderItem) | ||
| { | ||
| // Do not use non-deterministic operations in a workflow! | ||
| // These operations will create a new value every time the | ||
| // workflow is replayed. | ||
| var orderId = Guid.NewGuid().ToString(); | ||
| var orderDate = DateTime.UtcNow; | ||
|
|
||
| var idResult = await context.CallActivityAsync<string>( | ||
| "SubmitId", | ||
| orderId); | ||
|
|
||
| await context.CallActivityAsync<string>( | ||
| "SubmitDate", | ||
| orderDate); | ||
|
|
||
| return idResult; | ||
| } | ||
| } | ||
|
|
||
| internal sealed class DeterministicWorkflow : Workflow<string, string> | ||
| { | ||
| public override async Task<string> RunAsync(WorkflowContext context, string orderItem) | ||
| { | ||
| // Do use these deterministic methods and properties on the WorkflowContext instead. | ||
| // These operations create the same value when the workflow is replayed. | ||
| var orderId = context.NewGuid().ToString(); | ||
marcduiker marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| var orderDate = context.CurrentUtcDateTime; | ||
|
|
||
| var idResult = await context.CallActivityAsync<string>( | ||
| "SubmitId", | ||
| orderId); | ||
|
|
||
| await context.CallActivityAsync<string>( | ||
| "SubmitDate", | ||
| orderDate); | ||
|
|
||
| return idResult; | ||
| } | ||
| } | ||
19 changes: 19 additions & 0 deletions
19
tutorials/workflow/csharp/challenges-tips/IdempotentActivity.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| using Dapr.Client; | ||
| using Dapr.Workflow; | ||
|
|
||
| namespace WorkflowApp; | ||
|
|
||
| internal sealed class IdempotentActivity : WorkflowActivity<string, string> | ||
| { | ||
| public override async Task<InventoryResult> RunAsync(WorkflowActivityContext context, OrderItem orderItem) | ||
| { | ||
| // Beware of non-idempotent operations in an activity. | ||
| // Dapr Workflow guarantees at-least-once execution of activities, so activities might be executed more than once | ||
| // in case an activity is not ran to completion successfully. | ||
| // For instance, can you insert a record to a database twice without side effects? | ||
| // var insertSql = $"INSERT INTO Orders (Id, Description, UnitPrice, Quantity) VALUES ('{orderItem.Id}', '{orderItem.Description}', {orderItem.UnitPrice}, {orderItem.Quantity})"; | ||
| // It's best to perform a check if an record already exists before inserting it. | ||
| } | ||
| } | ||
|
|
||
| internal sealed record OrderItem(string Id, string Description, double UnitPrice, int Quantity); | ||
marcduiker marked this conversation as resolved.
Show resolved
Hide resolved
|
||
43 changes: 43 additions & 0 deletions
43
tutorials/workflow/csharp/challenges-tips/PayloadSizeWorkflow.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,43 @@ | ||
| using Dapr.Workflow; | ||
|
|
||
| namespace WorkflowApp; | ||
|
|
||
| internal sealed class LargePayloadSizeWorkflow : Workflow<string, LargeDocument> | ||
| { | ||
| public override async Task<LargeDocument> RunAsync(WorkflowContext context, string id) | ||
| { | ||
| // Do not pass large payloads between activities. | ||
| // They are stored in the Dapr state store twice, one as output argument | ||
| // for GetDocument, and once as input argument for UpdateDocument. | ||
| var document = await context.CallActivityAsync<LargeDocument>( | ||
| "GetDocument", | ||
| id); | ||
|
|
||
| var updatedDocument = await context.CallActivityAsync<LargeDocument>( | ||
| "UpdateDocument", | ||
| document); | ||
|
|
||
| // More activities to process the updated document... | ||
|
|
||
| return updatedDocument; | ||
| } | ||
| } | ||
|
|
||
|
|
||
| public class SmallPayloadSizeWorkflow : Workflow<string, string> | ||
| { | ||
| public override async Task<string> RunAsync(WorkflowContext context, string id) | ||
| { | ||
| // Do pass small payloads between activities, preferably IDs only, or objects that are quick to (de)serialize in large volumes. | ||
| // Combine multiple actions, such as document retrieval and update, into a single activity. | ||
| var documentId = await context.CallActivityAsync<string>( | ||
| "GetAndUpdateDocument", | ||
| id); | ||
|
|
||
| // More activities to process the updated document... | ||
|
|
||
| return documentId; | ||
| } | ||
| } | ||
|
|
||
| internal sealed record LargeDocument(string Id, object Data); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| # Workflow Challenges & Tips | ||
|
|
||
| Workflow systems are very powerful tools but also have their challenges & limitations as described in the [Dapr docs](https://docs.dapr.io/developing-applications/building-blocks/workflow/workflow-features-concepts/#limitations). | ||
|
|
||
| This section provides some tips with code snippets to understand the limitations and get the most out of the Dapr Workflow API. Read through the following examples to learn best practices to develop Dapr workflows. | ||
|
|
||
| - [Deterministic workflows](DeterministicWorkflow.cs) | ||
| - [Idempotent activities](IdempotentActivity.cs) | ||
| - [Versioning workflows](VersioningWorkflow.cs) | ||
| - [Workflow & activity payload size](PayloadSizeWorkflow.cs) |
50 changes: 50 additions & 0 deletions
50
tutorials/workflow/csharp/challenges-tips/VersioningWorkflow.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| using Dapr.Workflow; | ||
|
|
||
| namespace WorkflowApp; | ||
|
|
||
| /// <summary> | ||
| /// This is the initial version of the workflow. | ||
| /// Note that the input argument for both activities is the orderItem (string). | ||
| /// </summary> | ||
| internal sealed class VersioningWorkflow1 : Workflow<string, int> | ||
| { | ||
marcduiker marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| public override async Task<int> RunAsync(WorkflowContext context, string orderItem) | ||
| { | ||
| var resultA = await context.CallActivityAsync<int>( | ||
| "ActivityA", | ||
| orderItem); | ||
|
|
||
| var resultB = await context.CallActivityAsync<int>( | ||
| "ActivityB", | ||
| orderItem); | ||
|
|
||
| return resultA + resultB; | ||
| } | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// This is the updated version of the workflow. | ||
| /// The input for ActivityB has changed from orderItem (string) to resultA (int). | ||
| /// If there are in-flight workflow instances that were started with the previous version | ||
| /// of this workflow, these will fail when the new version of the workflow is deployed | ||
| /// and the workflow name remains the same, since the runtime parameters do not match with the persisted state. | ||
| /// It is recommended to version workflows by creating a new workflow class with a new name: | ||
marcduiker marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| /// {workflowname}1 -> {workflowname}2 | ||
| /// Try to avoid making breaking changes in perpetual workflows (that use the `ContinueAsNew` method) | ||
| /// since these are difficult to replace with a new version. | ||
| /// </summary> | ||
| internal sealed class VersioningWorkflow2 : Workflow<string, int> | ||
| { | ||
| public override async Task<int> RunAsync(WorkflowContext context, string orderItem) | ||
| { | ||
| var resultA = await context.CallActivityAsync<int>( | ||
| "ActivityA", | ||
| orderItem); | ||
|
|
||
| var resultB = await context.CallActivityAsync<int>( | ||
| "ActivityB", | ||
| resultA); | ||
|
|
||
| return resultA + resultB; | ||
| } | ||
| } | ||
12 changes: 12 additions & 0 deletions
12
tutorials/workflow/csharp/child-workflows/ChildWorkflows/Activities/Activity1.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| using Dapr.Workflow; | ||
|
|
||
| namespace ChildWorkflows.Activities; | ||
|
|
||
| internal sealed class Activity1 : WorkflowActivity<string, string> | ||
| { | ||
| public override Task<string> RunAsync(WorkflowActivityContext context, string input) | ||
| { | ||
| Console.WriteLine($"{nameof(Activity1)}: Received input: {input}."); | ||
| return Task.FromResult($"{input} is processed" ); | ||
| } | ||
| } |
12 changes: 12 additions & 0 deletions
12
tutorials/workflow/csharp/child-workflows/ChildWorkflows/Activities/Activity2.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| using Dapr.Workflow; | ||
|
|
||
| namespace ChildWorkflows.Activities; | ||
|
|
||
| internal sealed class Activity2 : WorkflowActivity<string, string> | ||
| { | ||
| public override Task<string> RunAsync(WorkflowActivityContext context, string input) | ||
| { | ||
| Console.WriteLine($"{nameof(Activity2)}: Received input: {input}."); | ||
| return Task.FromResult($"{input} as a child workflow." ); | ||
| } | ||
| } |
22 changes: 22 additions & 0 deletions
22
tutorials/workflow/csharp/child-workflows/ChildWorkflows/ChildWorkflow.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| using System; | ||
| using System.Collections.Generic; | ||
| using System.Linq; | ||
| using System.Threading.Tasks; | ||
| using ChildWorkflows.Activities; | ||
| using Dapr.Workflow; | ||
|
|
||
| namespace ChildWorkflows; | ||
| internal sealed class ChildWorkflow : Workflow<string, string> | ||
| { | ||
| public override async Task<string> RunAsync(WorkflowContext context, string input) | ||
| { | ||
| var result1 = await context.CallActivityAsync<string>( | ||
| nameof(Activity1), | ||
| input); | ||
| var childWorkflowResult = await context.CallActivityAsync<string>( | ||
| nameof(Activity2), | ||
| result1); | ||
|
|
||
| return childWorkflowResult; | ||
| } | ||
| } |
13 changes: 13 additions & 0 deletions
13
tutorials/workflow/csharp/child-workflows/ChildWorkflows/ChildWorkflows.csproj
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| <Project Sdk="Microsoft.NET.Sdk.Web"> | ||
|
|
||
| <PropertyGroup> | ||
| <TargetFramework>net9.0</TargetFramework> | ||
| <Nullable>enable</Nullable> | ||
| <ImplicitUsings>enable</ImplicitUsings> | ||
| </PropertyGroup> | ||
|
|
||
| <ItemGroup> | ||
| <PackageReference Include="Dapr.Workflow" Version="1.15.3" /> | ||
| </ItemGroup> | ||
|
|
||
| </Project> |
27 changes: 27 additions & 0 deletions
27
tutorials/workflow/csharp/child-workflows/ChildWorkflows/ParentWorkflow.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| using System; | ||
| using System.Collections.Generic; | ||
| using System.Linq; | ||
| using System.Threading.Tasks; | ||
| using Dapr.Workflow; | ||
| using ChildWorkflows.Activities; | ||
|
|
||
| namespace ChildWorkflows; | ||
|
|
||
| internal sealed class ParentWorkflow : Workflow<string[], string[]> | ||
| { | ||
| public override async Task<string[]> RunAsync(WorkflowContext context, string[] input) | ||
| { | ||
| List<Task<string>> childWorkflowTasks = []; | ||
|
|
||
| foreach (string item in input) | ||
| { | ||
| childWorkflowTasks.Add(context.CallChildWorkflowAsync<string>( | ||
| nameof(ChildWorkflow), | ||
| item)); | ||
| } | ||
|
|
||
| var allChildWorkflowResults = await Task.WhenAll(childWorkflowTasks); | ||
|
|
||
| return allChildWorkflowResults; | ||
| } | ||
| } |
26 changes: 26 additions & 0 deletions
26
tutorials/workflow/csharp/child-workflows/ChildWorkflows/Program.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| using ChildWorkflows; | ||
| using ChildWorkflows.Activities; | ||
| using Dapr.Workflow; | ||
|
|
||
| var builder = WebApplication.CreateBuilder(args); | ||
| builder.Services.AddDaprWorkflow(options => | ||
| { | ||
| options.RegisterWorkflow<ParentWorkflow>(); | ||
| options.RegisterWorkflow<ChildWorkflow>(); | ||
| options.RegisterActivity<Activity1>(); | ||
| options.RegisterActivity<Activity2>(); | ||
| }); | ||
| var app = builder.Build(); | ||
|
|
||
| app.MapPost("/start", async ( | ||
| string[] input, | ||
| DaprWorkflowClient workflowClient) => | ||
| { | ||
| var instanceId = await workflowClient.ScheduleNewWorkflowAsync( | ||
| name: nameof(ParentWorkflow), | ||
| input: input); | ||
|
|
||
| return Results.Accepted(instanceId); | ||
| }); | ||
|
|
||
| app.Run(); |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.