Skip to content

Commit 87a90ac

Browse files
Merge branch 'master' into patch-1
2 parents 8feda0c + 412303d commit 87a90ac

File tree

137 files changed

+3191
-6
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

137 files changed

+3191
-6
lines changed

.github/env/global.env

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
DAPR_CLI_VERSION: 1.15.0
2-
DAPR_RUNTIME_VERSION: 1.15.2
1+
DAPR_CLI_VERSION: 1.15.1
2+
DAPR_RUNTIME_VERSION: 1.15.4
33
DAPR_INSTALL_URL: https://raw.githubusercontent.com/dapr/cli/v${DAPR_CLI_VERSION}/install/
44
DAPR_DEFAULT_IMAGE_REGISTRY: ghcr
55

.github/workflows/validate_tutorials.yaml

Lines changed: 61 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,64 @@ jobs:
184184
# pushd tutorials/observability
185185
# make validate
186186
# popd
187-
- name: Linkcheck README.md
188-
run: |
189-
make validate
187+
# Validation for workflows is intentionally commented out.
188+
# Mechanical markdown is not able to invoke the workflow services.
189+
# Once this feature has been added to Mechanical Markdown the
190+
# workflow validations can be enabled.
191+
#- name: Validate workflow/csharp/child-workflows
192+
# if: matrix.os == 'ubuntu-latest'
193+
# run: |
194+
# pushd tutorials/workflow/csharp/child-workflows
195+
# make validate
196+
# popd
197+
#- name: Validate workflow/csharp/combined-patterns
198+
# if: matrix.os == 'ubuntu-latest'
199+
# run: |
200+
# pushd tutorials/workflow/csharp/combined-patterns
201+
# make validate
202+
# popd
203+
#- name: Validate workflow/csharp/external-system-interaction
204+
# if: matrix.os == 'ubuntu-latest'
205+
# run: |
206+
# pushd tutorials/workflow/csharp/external-system-interaction
207+
# make validate
208+
# popd
209+
#- name: Validate workflow/csharp/fan-out-fan-in
210+
# if: matrix.os == 'ubuntu-latest'
211+
# run: |
212+
# pushd tutorials/workflow/csharp/fan-out-fan-in
213+
# make validate
214+
# popd
215+
#- name: Validate workflow/csharp/fundamentals
216+
# if: matrix.os == 'ubuntu-latest'
217+
# run: |
218+
# pushd tutorials/workflow/csharp/fundamentals
219+
# make validate
220+
# popd
221+
#- name: Validate workflow/csharp/monitor-pattern
222+
# if: matrix.os == 'ubuntu-latest'
223+
# run: |
224+
# pushd tutorials/workflow/csharp/monitor-pattern
225+
# make validate
226+
# popd
227+
#- name: Validate workflow/csharp/resiliency-and-compensation
228+
# if: matrix.os == 'ubuntu-latest'
229+
# run: |
230+
# pushd tutorials/workflow/csharp/resiliency-and-compensation
231+
# make validate
232+
# popd
233+
#- name: Validate workflow/csharp/task-chaining
234+
# if: matrix.os == 'ubuntu-latest'
235+
# run: |
236+
# pushd tutorials/workflow/csharp/task-chaining
237+
# make validate
238+
# popd
239+
#- name: Validate workflow/csharp/workflow-management
240+
# if: matrix.os == 'ubuntu-latest'
241+
# run: |
242+
# pushd tutorials/workflow/csharp/workflow-management
243+
# make validate
244+
# popd
245+
#- name: Linkcheck README.md
246+
# run: |
247+
# make validate

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ Go deeper into a topic or scenario, oftentimes using building block APIs togethe
4040
| [Bindings](./tutorials/bindings) | Demonstrates how to use Dapr to create input and output bindings to other components. Uses bindings to Kafka.|
4141
| [Observability](./tutorials/observability) | Demonstrates Dapr tracing capabilities. Uses Zipkin as a tracing component. |
4242
| [Secret Store](./tutorials/secretstore) | Demonstrates the use of Dapr Secrets API to access secret stores. |
43+
| [Workflow](./tutorials/workflow) | Demonstrates how to author and manage Dapr workflows. Includes workflow patterns, resiliency, and common challenges & tips. |
4344

4445
## Development
4546

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Using the Dapr Workflow API with C#
2+
3+
This folder contains tutorials of using the Dapr Workflow API with C#. All examples can be run locally on your machine.
4+
5+
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/).
6+
7+
## Prerequisites
8+
9+
- [Docker Desktop](https://www.docker.com/products/docker-desktop/)
10+
- [Dapr CLI](https://docs.dapr.io/getting-started/install-dapr-cli/) & [Initialization](https://docs.dapr.io/getting-started/install-dapr-selfhost/)
11+
- [.NET 9](https://dotnet.microsoft.com/download/dotnet/9.0)
12+
- Optional: An IDE such as [VSCode](https://code.visualstudio.com/download) with a [REST client](https://marketplace.visualstudio.com/items?itemName=humao.rest-client).
13+
14+
## Tutorials
15+
16+
- [Workflow Basics](./fundamentals/README.md)
17+
- [Task Chaining](./task-chaining/README.md)
18+
- [Fan-out/Fan-in](./fan-out-fan-in/README.md)
19+
- [Monitor](./monitor-pattern/README.md)
20+
- [External Events](./external-system-interaction/README.md)
21+
- [Child Workflows](./child-workflows/README.md)
22+
- [Resiliency & Compensation](./resiliency-and-compensation/README.md)
23+
- [Combined Patterns](./combined-patterns/README.md)
24+
- [WorkflowManagement](./workflow-management/README.md)
25+
- [Challenges & Tips](./challenges-tips/README.md)
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
using System;
2+
using Dapr.Workflow;
3+
4+
namespace WorkflowApp;
5+
6+
internal sealed class NonDeterministicWorkflow : Workflow<string, string>
7+
{
8+
public override async Task<string> RunAsync(WorkflowContext context, string orderItem)
9+
{
10+
// Do not use non-deterministic operations in a workflow!
11+
// These operations will create a new value every time the
12+
// workflow is replayed.
13+
var orderId = Guid.NewGuid().ToString();
14+
var orderDate = DateTime.UtcNow;
15+
16+
var idResult = await context.CallActivityAsync<string>(
17+
"SubmitId",
18+
orderId);
19+
20+
await context.CallActivityAsync<string>(
21+
"SubmitDate",
22+
orderDate);
23+
24+
return idResult;
25+
}
26+
}
27+
28+
internal sealed class DeterministicWorkflow : Workflow<string, string>
29+
{
30+
public override async Task<string> RunAsync(WorkflowContext context, string orderItem)
31+
{
32+
// Do use these deterministic methods and properties on the WorkflowContext instead.
33+
// These operations create the same value when the workflow is replayed.
34+
var orderId = context.NewGuid().ToString();
35+
var orderDate = context.CurrentUtcDateTime;
36+
37+
var idResult = await context.CallActivityAsync<string>(
38+
"SubmitId",
39+
orderId);
40+
41+
await context.CallActivityAsync<string>(
42+
"SubmitDate",
43+
orderDate);
44+
45+
return idResult;
46+
}
47+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
using Dapr.Client;
2+
using Dapr.Workflow;
3+
4+
namespace WorkflowApp;
5+
6+
internal sealed class IdempotentActivity : WorkflowActivity<string, string>
7+
{
8+
public override async Task<InventoryResult> RunAsync(WorkflowActivityContext context, OrderItem orderItem)
9+
{
10+
// Beware of non-idempotent operations in an activity.
11+
// Dapr Workflow guarantees at-least-once execution of activities, so activities might be executed more than once
12+
// in case an activity is not ran to completion successfully.
13+
// For instance, can you insert a record to a database twice without side effects?
14+
// var insertSql = $"INSERT INTO Orders (Id, Description, UnitPrice, Quantity) VALUES ('{orderItem.Id}', '{orderItem.Description}', {orderItem.UnitPrice}, {orderItem.Quantity})";
15+
// It's best to perform a check if an record already exists before inserting it.
16+
}
17+
}
18+
19+
internal sealed record OrderItem(string Id, string Description, double UnitPrice, int Quantity);
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
using Dapr.Workflow;
2+
3+
namespace WorkflowApp;
4+
5+
internal sealed class LargePayloadSizeWorkflow : Workflow<string, LargeDocument>
6+
{
7+
public override async Task<LargeDocument> RunAsync(WorkflowContext context, string id)
8+
{
9+
// Do not pass large payloads between activities.
10+
// They are stored in the Dapr state store twice, one as output argument
11+
// for GetDocument, and once as input argument for UpdateDocument.
12+
var document = await context.CallActivityAsync<LargeDocument>(
13+
"GetDocument",
14+
id);
15+
16+
var updatedDocument = await context.CallActivityAsync<LargeDocument>(
17+
"UpdateDocument",
18+
document);
19+
20+
// More activities to process the updated document...
21+
22+
return updatedDocument;
23+
}
24+
}
25+
26+
27+
public class SmallPayloadSizeWorkflow : Workflow<string, string>
28+
{
29+
public override async Task<string> RunAsync(WorkflowContext context, string id)
30+
{
31+
// Do pass small payloads between activities, preferably IDs only, or objects that are quick to (de)serialize in large volumes.
32+
// Combine multiple actions, such as document retrieval and update, into a single activity.
33+
var documentId = await context.CallActivityAsync<string>(
34+
"GetAndUpdateDocument",
35+
id);
36+
37+
// More activities to process the updated document...
38+
39+
return documentId;
40+
}
41+
}
42+
43+
internal sealed record LargeDocument(string Id, object Data);
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Workflow Challenges & Tips
2+
3+
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).
4+
5+
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.
6+
7+
- [Deterministic workflows](DeterministicWorkflow.cs)
8+
- [Idempotent activities](IdempotentActivity.cs)
9+
- [Versioning workflows](VersioningWorkflow.cs)
10+
- [Workflow & activity payload size](PayloadSizeWorkflow.cs)
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
using Dapr.Workflow;
2+
3+
namespace WorkflowApp;
4+
5+
/// <summary>
6+
/// This is the initial version of the workflow.
7+
/// Note that the input argument for both activities is the orderItem (string).
8+
/// </summary>
9+
internal sealed class VersioningWorkflow1 : Workflow<string, int>
10+
{
11+
public override async Task<int> RunAsync(WorkflowContext context, string orderItem)
12+
{
13+
var resultA = await context.CallActivityAsync<int>(
14+
"ActivityA",
15+
orderItem);
16+
17+
var resultB = await context.CallActivityAsync<int>(
18+
"ActivityB",
19+
orderItem);
20+
21+
return resultA + resultB;
22+
}
23+
}
24+
25+
/// <summary>
26+
/// This is the updated version of the workflow.
27+
/// The input for ActivityB has changed from orderItem (string) to resultA (int).
28+
/// If there are in-flight workflow instances that were started with the previous version
29+
/// of this workflow, these will fail when the new version of the workflow is deployed
30+
/// and the workflow name remains the same, since the runtime parameters do not match with the persisted state.
31+
/// It is recommended to version workflows by creating a new workflow class with a new name:
32+
/// {workflowname}1 -> {workflowname}2
33+
/// Try to avoid making breaking changes in perpetual workflows (that use the `ContinueAsNew` method)
34+
/// since these are difficult to replace with a new version.
35+
/// </summary>
36+
internal sealed class VersioningWorkflow2 : Workflow<string, int>
37+
{
38+
public override async Task<int> RunAsync(WorkflowContext context, string orderItem)
39+
{
40+
var resultA = await context.CallActivityAsync<int>(
41+
"ActivityA",
42+
orderItem);
43+
44+
var resultB = await context.CallActivityAsync<int>(
45+
"ActivityB",
46+
resultA);
47+
48+
return resultA + resultB;
49+
}
50+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
using Dapr.Workflow;
2+
3+
namespace ChildWorkflows.Activities;
4+
5+
internal sealed class Activity1 : WorkflowActivity<string, string>
6+
{
7+
public override Task<string> RunAsync(WorkflowActivityContext context, string input)
8+
{
9+
Console.WriteLine($"{nameof(Activity1)}: Received input: {input}.");
10+
return Task.FromResult($"{input} is processed" );
11+
}
12+
}

0 commit comments

Comments
 (0)