diff --git a/.devcontainer/localinit.sh b/.devcontainer/localinit.sh
index 80b27e4f4..69c4c1274 100644
--- a/.devcontainer/localinit.sh
+++ b/.devcontainer/localinit.sh
@@ -6,4 +6,4 @@ az extension add --name containerapp --yes
nvm install v18.12.1
# initialize Dapr
-dapr init --runtime-version=1.10.0-rc.2
\ No newline at end of file
+dapr init --runtime-version=1.14.0
\ No newline at end of file
diff --git a/.editorconfig b/.editorconfig
index a4dd20cc2..2e2e40c7a 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -22,6 +22,7 @@ charset = utf-8-bom
# Organize usings
dotnet_sort_system_directives_first = true
dotnet_separate_import_directive_groups = false
+csharp_using_directive_placement = outside_namespace
# this. preferences
dotnet_style_qualification_for_field = false:silent
diff --git a/.github/holopin.yml b/.github/holopin.yml
new file mode 100644
index 000000000..7472cc284
--- /dev/null
+++ b/.github/holopin.yml
@@ -0,0 +1,6 @@
+organization: dapr
+defaultSticker: clrqfdv4x24910fl5n4iwu5oa
+stickers:
+ -
+ id: clrqfdv4x24910fl5n4iwu5oa
+ alias: sdk-badge
\ No newline at end of file
diff --git a/.github/workflows/itests.yml b/.github/workflows/itests.yml
index 0044fec93..36741ce7c 100644
--- a/.github/workflows/itests.yml
+++ b/.github/workflows/itests.yml
@@ -19,37 +19,33 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
- dotnet-version: ['3.1', '6.0', '7.0']
+ dotnet-version: ['6.0', '7.0', '8.0']
include:
- - dotnet-version: '3.1'
- install-3: true
- display-name: '.NET Core 3.1'
- framework: 'netcoreapp3.1'
- prefix: 'netcoreapp31'
- install-version: '3.1.x' # We always need a new .NET
- dotnet-version: '6.0'
- install-3: false
display-name: '.NET 6.0'
framework: 'net6'
prefix: 'net6'
install-version: '6.0.x'
- dotnet-version: '7.0'
- install-3: false
display-name: '.NET 7.0'
framework: 'net7'
prefix: 'net7'
install-version: '7.0.x'
+ - dotnet-version: '8.0'
+ display-name: '.NET 8.0'
+ framework: 'net8'
+ prefix: 'net8'
+ install-version: '8.0.x'
env:
NUPKG_OUTDIR: bin/Release/nugets
GOVER: 1.20.3
GOOS: linux
GOARCH: amd64
GOPROXY: https://proxy.golang.org
- DAPR_CLI_VER: 1.9.1
- DAPR_RUNTIME_VER: 1.10.5
- DAPR_INSTALL_URL: https://raw.githubusercontent.com/dapr/cli/3dacfb672d55f1436c249057aaebbe597e1066f3/install/install.sh
+ DAPR_CLI_VER: 1.14.0
+ DAPR_RUNTIME_VER: 1.14.0
+ DAPR_INSTALL_URL: https://raw.githubusercontent.com/dapr/cli/release-1.14/install/install.sh
DAPR_CLI_REF: ''
- DAPR_REF: '4181de0edc65fc98a836ae7abc6042c575c8fae5'
steps:
- name: Set up Dapr CLI
run: wget -q ${{ env.DAPR_INSTALL_URL }} -O - | /bin/bash -s ${{ env.DAPR_CLI_VER }}
@@ -105,29 +101,19 @@ jobs:
- uses: actions/checkout@v1
- name: Parse release version
run: python ./.github/scripts/get_release_version.py
- - name: Install Local kafka using docker-compose
- run: |
- docker-compose -f test/Dapr.E2E.Test/deploy/local-test-kafka.yml up -d
- docker ps
- - name: Install Local Hashicorp Vault using docker-compose
- run: |
- docker-compose -f test/Dapr.E2E.Test/deploy/local-test-vault.yml up -d
- docker ps
- - name: Setup Vault's test token
- run: echo myroot > /tmp/.hashicorp_vault_token
- name: Setup ${{ matrix.display-name }}
uses: actions/setup-dotnet@v1
with:
dotnet-version: ${{ matrix.install-version }}
- - name: Setup .NET 7.0 # net7 is always required.
+ - name: Setup .NET 8.0 # net8 is always required.
uses: actions/setup-dotnet@v1
- if: ${{ matrix.install-version != '7.0.x' }}
+ if: ${{ matrix.install-version != '8.0.x' }}
with:
- dotnet-version: 7.0.x
+ dotnet-version: 8.0.x
- name: Build
# disable deterministic builds, just for test run. Deterministic builds break coverage for some reason
run: dotnet build --configuration release /p:GITHUB_ACTIONS=false
- - name: Run Test
+ - name: Run General Tests
id: tests
continue-on-error: true # proceed if tests fail, the report step will report the failure with more details.
run: |
@@ -142,8 +128,24 @@ jobs:
/p:CollectCoverage=true \
/p:CoverletOutputFormat=opencover \
/p:GITHUB_ACTIONS=false
+ - name: Run Generators Tests
+ id: generator-tests
+ continue-on-error: true # proceed if tests fail, the report step will report the failure with more details.
+ run: |
+ dotnet test ${{ github.workspace }}/test/Dapr.E2E.Test.Actors.Generators/Dapr.E2E.Test.Actors.Generators.csproj \
+ --configuration Release \
+ --framework ${{ matrix.framework }} \
+ --no-build \
+ --no-restore \
+ --logger "trx;LogFilePrefix=${{ matrix.prefix }}" \
+ --logger "GitHubActions;report-warnings=false" \
+ --logger "console;verbosity=detailed" \
+ --results-directory "${{ github.workspace }}/TestResults" \
+ /p:CollectCoverage=true \
+ /p:CoverletOutputFormat=opencover \
+ /p:GITHUB_ACTIONS=false
- name: Check test failure in PR
- if: github.event_name == 'pull_request' && steps.tests.outcome != 'success'
+ if: github.event_name == 'pull_request' && (steps.tests.outcome != 'success' || steps.generator-tests.outcome != 'success')
run: exit 1
- name: Upload test coverage
uses: codecov/codecov-action@v1
diff --git a/.github/workflows/sdk_build.yml b/.github/workflows/sdk_build.yml
index b0b6b042c..fe935bfb8 100644
--- a/.github/workflows/sdk_build.yml
+++ b/.github/workflows/sdk_build.yml
@@ -26,13 +26,13 @@ jobs:
- name: Setup .NET Core
uses: actions/setup-dotnet@v1
with:
- dotnet-version: 7.0.x
+ dotnet-version: 8.0.x
- name: Build
run: dotnet build --configuration release
- name: Generate Packages
run: dotnet pack --configuration release
- name: Upload packages
- uses: actions/upload-artifact@master
+ uses: actions/upload-artifact@v4
with:
name: packages
path: ${{ env.NUPKG_OUTDIR }}
@@ -42,14 +42,8 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
- dotnet-version: ['3.1', '6.0', '7.0']
+ dotnet-version: ['6.0', '7.0', '8.0']
include:
- - dotnet-version: '3.1'
- install-3: true
- display-name: '.NET Core 3.1'
- framework: 'netcoreapp3.1'
- prefix: 'netcoreapp31'
- install-version: '3.1.x' # We always need a new .NET
- dotnet-version: '6.0'
install-3: false
display-name: '.NET 6.0'
@@ -62,6 +56,12 @@ jobs:
framework: 'net7'
prefix: 'net7'
install-version: '7.0.x'
+ - dotnet-version: '8.0'
+ install-3: false
+ display-name: '.NET 8.0'
+ framework: 'net8'
+ prefix: 'net8'
+ install-version: '8.0.x'
steps:
- uses: actions/checkout@v1
- name: Parse release version
@@ -70,11 +70,11 @@ jobs:
uses: actions/setup-dotnet@v1
with:
dotnet-version: ${{ matrix.install-version }}
- - name: Setup .NET 7.0 # net7 is always required.
+ - name: Setup .NET 8.0 # net8 is always required.
uses: actions/setup-dotnet@v1
- if: ${{ matrix.install-version != '7.0.x' }}
+ if: ${{ matrix.install-version != '8.0.x' }}
with:
- dotnet-version: 7.0.x
+ dotnet-version: 8.0.x
- name: Build
# disable deterministic builds, just for test run. Deterministic builds break coverage for some reason
run: dotnet build --configuration release /p:GITHUB_ACTIONS=false
@@ -116,7 +116,7 @@ jobs:
if: startswith(github.ref, 'refs/tags/v') && !(endsWith(github.ref, '-rc') || endsWith(github.ref, '-dev') || endsWith(github.ref, '-prerelease'))
steps:
- name: Download release artifacts
- uses: actions/download-artifact@v2
+ uses: actions/download-artifact@v4
with:
name: packages
path: packages
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index f47877cbd..7712340a5 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -123,3 +123,13 @@ A non-exclusive list of code that must be places in `vendor/`:
## Code of Conduct
This project has adopted the [Contributor Covenant Code of Conduct](https://github.com/dapr/community/blob/master/CODE-OF-CONDUCT.md)
+
+
+
+## GitHub Dapr Bot Commands
+
+Checkout the [daprbot documentation](https://docs.dapr.io/contributing/daprbot/) for Github commands you can run in this repo for common tasks. For example, you can comment `/assign` on an issue to assign it to yourself.
+
+
+
+
diff --git a/Directory.Packages.props b/Directory.Packages.props
new file mode 100644
index 000000000..d85020770
--- /dev/null
+++ b/Directory.Packages.props
@@ -0,0 +1,46 @@
+
+
+ true
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/README.md b/README.md
index 54bb606dc..948516fe2 100644
--- a/README.md
+++ b/README.md
@@ -1,9 +1,6 @@
# Dapr SDK for .NET
-[![Build Status](https://github.com/dapr/dotnet-sdk/workflows/build/badge.svg)](https://github.com/dapr/dotnet-sdk/actions?workflow=build)
-[![codecov](https://codecov.io/gh/dapr/dotnet-sdk/branch/master/graph/badge.svg)](https://codecov.io/gh/dapr/dotnet-sdk)
-[![License: Apache](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](http://www.apache.org/licenses/LICENSE-2.0)
-[![FOSSA Status](https://app.fossa.com/api/projects/custom%2B162%2Fgithub.com%2Fdapr%2Fcomponents-contrib.svg?type=shield)](https://app.fossa.com/projects/custom%2B162%2Fgithub.com%2Fdapr%2Fcomponents-contrib?ref=badge_shield)
+[![NuGet Version](https://img.shields.io/nuget/v/Dapr.Client?logo=nuget&label=Latest%20version&style=flat)](https://www.nuget.org/packages/Dapr.Client) [![NuGet Downloads](https://img.shields.io/nuget/dt/Dapr.Client?style=flat&logo=nuget&label=Downloads)](https://www.nuget.org/packages/Dapr.Client) [![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/dapr/dotnet-sdk/.github%2Fworkflows%2Fsdk_build.yml?branch=master&label=Build&logo=github)](https://github.com/dapr/dotnet-sdk/actions/workflows/sdk_build.yml) [![codecov](https://codecov.io/gh/dapr/dotnet-sdk/branch/master/graph/badge.svg)](https://codecov.io/gh/dapr/dotnet-sdk) [![GitHub License](https://img.shields.io/github/license/dapr/dotnet-sdk?style=flat&label=License&logo=github)](https://github.com/dapr/dotnet-sdk/blob/master/LICENSE) [![GitHub issue custom search in repo](https://img.shields.io/github/issues-search/dapr/dotnet-sdk?query=type%3Aissue%20is%3Aopen%20label%3A%22good%20first%20issue%22&label=Good%20first%20issues&style=flat&logo=github)](https://github.com/dapr/dotnet-sdk/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) [![Discord](https://img.shields.io/discord/778680217417809931?label=Discord&style=flat&logo=discord)](http://bit.ly/dapr-discord) [![YouTube Channel Views](https://img.shields.io/youtube/channel/views/UCtpSQ9BLB_3EXdWAUQYwnRA?style=flat&label=YouTube%20views&logo=youtube)](https://youtube.com/@daprdev) [![X (formerly Twitter) Follow](https://img.shields.io/twitter/follow/daprdev?logo=x&style=flat)](https://twitter.com/daprdev)
Dapr SDK for .NET allows you to:
diff --git a/all.sln b/all.sln
index 47fc9098c..1a5d78efb 100644
--- a/all.sln
+++ b/all.sln
@@ -1,4 +1,3 @@
-
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.3.32929.385
@@ -33,6 +32,7 @@ EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{1BD1276E-D28A-45EA-89B1-6AD48471500D}"
ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig
+ README.md = README.md
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dapr.Actors.AspNetCore.Test", "test\Dapr.Actors.AspNetCore.Test\Dapr.Actors.AspNetCore.Test.csproj", "{9C1D6ABA-5EDE-4FA0-A8A9-0AB98CB74737}"
@@ -71,8 +71,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "StateManagement", "examples
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ServiceInvocation", "examples\Client\ServiceInvocation\ServiceInvocation.csproj", "{8B570E70-0E73-4042-A4B6-1CC3CC782A65}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PublishSubscribe", "examples\Client\PublishSubscribe\PublishSubscribe.csproj", "{DE6913E3-E5D9-4D1D-95F9-9FED87BD09BC}"
-EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dapr.E2E.Test", "test\Dapr.E2E.Test\Dapr.E2E.Test.csproj", "{4AA9E7B7-36BF-4AAE-BFA3-C9CE8740F4A0}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dapr.E2E.Test.App", "test\Dapr.E2E.Test.App\Dapr.E2E.Test.App.csproj", "{345FC3FB-D1E9-4AE8-9052-17D20AB01FA2}"
@@ -104,6 +102,26 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BulkPublishEventExample", "
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowUnitTest", "examples\Workflow\WorkflowUnitTest\WorkflowUnitTest.csproj", "{8CA09061-2BEF-4506-A763-07062D2BD6AC}"
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "GeneratedActor", "GeneratedActor", "{7592AFA4-426B-42F3-AE82-957C86814482}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ActorClient", "examples\GeneratedActor\ActorClient\ActorClient.csproj", "{61C24126-F39D-4BEA-96DC-FC87BA730554}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ActorCommon", "examples\GeneratedActor\ActorCommon\ActorCommon.csproj", "{CB903D21-4869-42EF-BDD6-5B1CFF674337}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dapr.Actors.Generators", "src\Dapr.Actors.Generators\Dapr.Actors.Generators.csproj", "{980B5FD8-0107-41F7-8FAD-E4E8BAE8A625}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ActorService", "examples\GeneratedActor\ActorService\ActorService.csproj", "{7C06FE2D-6C62-48F5-A505-F0D715C554DE}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dapr.Actors.Generators.Test", "test\Dapr.Actors.Generators.Test\Dapr.Actors.Generators.Test.csproj", "{AF89083D-4715-42E6-93E9-38497D12A8A6}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dapr.E2E.Test.Actors.Generators", "test\Dapr.E2E.Test.Actors.Generators\Dapr.E2E.Test.Actors.Generators.csproj", "{B5CDB0DC-B26D-48F1-B934-FE5C1C991940}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cryptography", "examples\Client\Cryptography\Cryptography.csproj", "{C74FBA78-13E8-407F-A173-4555AEE41FF3}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dapr.Protos", "src\Dapr.Protos\Dapr.Protos.csproj", "{DFBABB04-50E9-42F6-B470-310E1B545638}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dapr.Common", "src\Dapr.Common\Dapr.Common.csproj", "{B445B19C-A925-4873-8CB7-8317898B6970}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -248,6 +266,42 @@ Global
{DDC41278-FB60-403A-B969-2AEBD7C2D83C}.Release|Any CPU.Build.0 = Release|Any CPU
{8CA09061-2BEF-4506-A763-07062D2BD6AC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8CA09061-2BEF-4506-A763-07062D2BD6AC}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {61C24126-F39D-4BEA-96DC-FC87BA730554}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {61C24126-F39D-4BEA-96DC-FC87BA730554}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {61C24126-F39D-4BEA-96DC-FC87BA730554}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {61C24126-F39D-4BEA-96DC-FC87BA730554}.Release|Any CPU.Build.0 = Release|Any CPU
+ {CB903D21-4869-42EF-BDD6-5B1CFF674337}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {CB903D21-4869-42EF-BDD6-5B1CFF674337}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {CB903D21-4869-42EF-BDD6-5B1CFF674337}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {CB903D21-4869-42EF-BDD6-5B1CFF674337}.Release|Any CPU.Build.0 = Release|Any CPU
+ {980B5FD8-0107-41F7-8FAD-E4E8BAE8A625}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {980B5FD8-0107-41F7-8FAD-E4E8BAE8A625}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {980B5FD8-0107-41F7-8FAD-E4E8BAE8A625}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {980B5FD8-0107-41F7-8FAD-E4E8BAE8A625}.Release|Any CPU.Build.0 = Release|Any CPU
+ {7C06FE2D-6C62-48F5-A505-F0D715C554DE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {7C06FE2D-6C62-48F5-A505-F0D715C554DE}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {7C06FE2D-6C62-48F5-A505-F0D715C554DE}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {7C06FE2D-6C62-48F5-A505-F0D715C554DE}.Release|Any CPU.Build.0 = Release|Any CPU
+ {AF89083D-4715-42E6-93E9-38497D12A8A6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {AF89083D-4715-42E6-93E9-38497D12A8A6}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {AF89083D-4715-42E6-93E9-38497D12A8A6}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {AF89083D-4715-42E6-93E9-38497D12A8A6}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B5CDB0DC-B26D-48F1-B934-FE5C1C991940}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B5CDB0DC-B26D-48F1-B934-FE5C1C991940}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B5CDB0DC-B26D-48F1-B934-FE5C1C991940}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B5CDB0DC-B26D-48F1-B934-FE5C1C991940}.Release|Any CPU.Build.0 = Release|Any CPU
+ {C74FBA78-13E8-407F-A173-4555AEE41FF3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C74FBA78-13E8-407F-A173-4555AEE41FF3}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C74FBA78-13E8-407F-A173-4555AEE41FF3}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C74FBA78-13E8-407F-A173-4555AEE41FF3}.Release|Any CPU.Build.0 = Release|Any CPU
+ {DFBABB04-50E9-42F6-B470-310E1B545638}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {DFBABB04-50E9-42F6-B470-310E1B545638}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {DFBABB04-50E9-42F6-B470-310E1B545638}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {DFBABB04-50E9-42F6-B470-310E1B545638}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B445B19C-A925-4873-8CB7-8317898B6970}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B445B19C-A925-4873-8CB7-8317898B6970}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B445B19C-A925-4873-8CB7-8317898B6970}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B445B19C-A925-4873-8CB7-8317898B6970}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -293,6 +347,16 @@ Global
{4A175C27-EAFE-47E7-90F6-873B37863656} = {0EF6EA64-D7C3-420D-9890-EAE8D54A57E6}
{DDC41278-FB60-403A-B969-2AEBD7C2D83C} = {0EF6EA64-D7C3-420D-9890-EAE8D54A57E6}
{8CA09061-2BEF-4506-A763-07062D2BD6AC} = {BF3ED6BF-ADF3-4D25-8E89-02FB8D945CA9}
+ {7592AFA4-426B-42F3-AE82-957C86814482} = {D687DDC4-66C5-4667-9E3A-FD8B78ECAA78}
+ {61C24126-F39D-4BEA-96DC-FC87BA730554} = {7592AFA4-426B-42F3-AE82-957C86814482}
+ {CB903D21-4869-42EF-BDD6-5B1CFF674337} = {7592AFA4-426B-42F3-AE82-957C86814482}
+ {980B5FD8-0107-41F7-8FAD-E4E8BAE8A625} = {27C5D71D-0721-4221-9286-B94AB07B58CF}
+ {7C06FE2D-6C62-48F5-A505-F0D715C554DE} = {7592AFA4-426B-42F3-AE82-957C86814482}
+ {AF89083D-4715-42E6-93E9-38497D12A8A6} = {DD020B34-460F-455F-8D17-CF4A949F100B}
+ {B5CDB0DC-B26D-48F1-B934-FE5C1C991940} = {DD020B34-460F-455F-8D17-CF4A949F100B}
+ {C74FBA78-13E8-407F-A173-4555AEE41FF3} = {A7F41094-8648-446B-AECD-DCC2CC871F73}
+ {DFBABB04-50E9-42F6-B470-310E1B545638} = {27C5D71D-0721-4221-9286-B94AB07B58CF}
+ {B445B19C-A925-4873-8CB7-8317898B6970} = {27C5D71D-0721-4221-9286-B94AB07B58CF}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {65220BF2-EAE1-4CB2-AA58-EBE80768CB40}
diff --git a/daprdocs/content/en/dotnet-sdk-contributing/dotnet-contributing.md b/daprdocs/content/en/dotnet-sdk-contributing/dotnet-contributing.md
index a4e546ffa..6664191d6 100644
--- a/daprdocs/content/en/dotnet-sdk-contributing/dotnet-contributing.md
+++ b/daprdocs/content/en/dotnet-sdk-contributing/dotnet-contributing.md
@@ -21,3 +21,7 @@ The `daprdocs` directory contains the markdown files that are rendered into the
- All rules in the [docs guide]({{< ref contributing-docs.md >}}) should be followed in addition to these.
- All files and directories should be prefixed with `dotnet-` to ensure all file/directory names are globally unique across all Dapr documentation.
+
+## GitHub Dapr Bot Commands
+
+Checkout the [daprbot documentation](https://docs.dapr.io/contributing/daprbot/) for Github commands you can run in this repo for common tasks. For example, you can comment `/assign` on an issue to assign it to yourself.
diff --git a/daprdocs/content/en/dotnet-sdk-docs/_index.md b/daprdocs/content/en/dotnet-sdk-docs/_index.md
index 65d818723..121dde310 100644
--- a/daprdocs/content/en/dotnet-sdk-docs/_index.md
+++ b/daprdocs/content/en/dotnet-sdk-docs/_index.md
@@ -5,6 +5,11 @@ linkTitle: ".NET"
weight: 1000
description: .NET SDK packages for developing Dapr applications
no_list: true
+cascade:
+ github_repo: https://github.com/dapr/dotnet-sdk
+ github_subdir: daprdocs/content/en/dotnet-sdk-docs
+ path_base_for_github_subdir: content/en/developing-applications/sdks/dotnet/
+ github_branch: master
---
Dapr offers a variety of packages to help with the development of .NET applications. Using them you can create .NET clients, servers, and virtual actors with Dapr.
@@ -13,7 +18,7 @@ Dapr offers a variety of packages to help with the development of .NET applicati
- [Dapr CLI]({{< ref install-dapr-cli.md >}}) installed
- Initialized [Dapr environment]({{< ref install-dapr-selfhost.md >}})
-- [.NET Core 3.1 or .NET 5+](https://dotnet.microsoft.com/download) installed
+- [.NET 6+](https://dotnet.microsoft.com/download) installed
## Installation
diff --git a/daprdocs/content/en/dotnet-sdk-docs/dotnet-actors/dotnet-actors-howto.md b/daprdocs/content/en/dotnet-sdk-docs/dotnet-actors/dotnet-actors-howto.md
index 986562ca8..eaa13625d 100644
--- a/daprdocs/content/en/dotnet-sdk-docs/dotnet-actors/dotnet-actors-howto.md
+++ b/daprdocs/content/en/dotnet-sdk-docs/dotnet-actors/dotnet-actors-howto.md
@@ -45,7 +45,7 @@ This project contains the implementation of the actor client which calls MyActor
- [Dapr CLI]({{< ref install-dapr-cli.md >}}) installed.
- Initialized [Dapr environment]({{< ref install-dapr-selfhost.md >}}).
-- [.NET Core 3.1 or .NET 6+](https://dotnet.microsoft.com/download) installed. Dapr .NET SDK uses [ASP.NET Core](https://docs.microsoft.com/aspnet/core/introduction-to-aspnet-core?view=aspnetcore-6.0).
+- [.NET 6+](https://dotnet.microsoft.com/download) installed. Dapr .NET SDK uses [ASP.NET Core](https://docs.microsoft.com/aspnet/core/introduction-to-aspnet-core?view=aspnetcore-6.0).
## Step 0: Prepare
@@ -81,6 +81,7 @@ Define `IMyActor` interface and `MyData` data object. Paste the following code i
```csharp
using Dapr.Actors;
+using Dapr.Actors.Runtime;
using System.Threading.Tasks;
namespace MyActor.Interfaces
@@ -91,6 +92,7 @@ namespace MyActor.Interfaces
Task GetDataAsync();
Task RegisterReminder();
Task UnregisterReminder();
+ Task GetReminder();
Task RegisterTimer();
Task UnregisterTimer();
}
@@ -219,6 +221,14 @@ namespace MyActorService
TimeSpan.FromSeconds(5)); // Time interval between reminder invocations after the first invocation
}
+ ///
+ /// Get MyReminder reminder details with the actor
+ ///
+ public async Task GetReminder()
+ {
+ await this.GetReminderAsync("MyReminder");
+ }
+
///
/// Unregister MyReminder reminder with the actor
///
@@ -306,22 +316,11 @@ namespace MyActorService
{
app.UseDeveloperExceptionPage();
}
- else
- {
- // By default, ASP.Net Core uses port 5000 for HTTP. The HTTP
- // redirection will interfere with the Dapr runtime. You can
- // move this out of the else block if you use port 5001 in this
- // example, and developer tooling (such as the VSCode extension).
- app.UseHttpsRedirection();
- }
app.UseRouting();
- app.UseEndpoints(endpoints =>
- {
- // Register actors handlers that interface with the Dapr runtime.
- endpoints.MapActorsHandlers();
- });
+ // Register actors handlers that interface with the Dapr runtime.
+ app.MapActorsHandlers();
}
}
}
@@ -392,7 +391,7 @@ namespace MyActorClient
Console.WriteLine($"Calling GetDataAsync on {actorType}:{actorId}...");
var savedData = await proxy.GetDataAsync();
- Console.WriteLine($"Got response: {response}");
+ Console.WriteLine($"Got response: {savedData}");
}
}
}
@@ -456,7 +455,7 @@ The projects that you've created can now to test the sample.
Calling SetDataAsync on MyActor:1...
Got response: Success
Calling GetDataAsync on MyActor:1...
- Got response: Success
+ Got response: PropertyA: ValueA, PropertyB: ValueB
```
> 💡 This sample relies on a few assumptions. The default listening port for an ASP.NET Core web project is 5000, which is being passed to `dapr run` as `--app-port 5000`. The default HTTP port for the Dapr sidecar is 3500. We're telling the sidecar for `MyActorService` to use 3500 so that `MyActorClient` can rely on the default value.
diff --git a/daprdocs/content/en/dotnet-sdk-docs/dotnet-actors/dotnet-actors-serialization.md b/daprdocs/content/en/dotnet-sdk-docs/dotnet-actors/dotnet-actors-serialization.md
new file mode 100644
index 000000000..abbeb437d
--- /dev/null
+++ b/daprdocs/content/en/dotnet-sdk-docs/dotnet-actors/dotnet-actors-serialization.md
@@ -0,0 +1,317 @@
+---
+type: docs
+title: "Actor serialization in the .NET SDK"
+linkTitle: "Actor serialization"
+weight: 300000
+description: Necessary steps to serialize your types using remoted Actors in .NET
+---
+
+The Dapr actor package enables you to use Dapr virtual actors within a .NET application with strongly-typed remoting, but if you intend to send and receive strongly-typed data from your methods, there are a few key ground rules to understand. In this guide, you will learn how to configure your classes and records so they are properly serialized and deserialized at runtime.
+
+# Data Contract Serialization
+When Dapr's virtual actors are invoked via the remoting proxy, your data is serialized using a serialization engine called the [Data Contract Serializer](https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/serializable-types) implemented by the [DataContractSerializer](https://learn.microsoft.com/en-us/dotnet/api/system.runtime.serialization.datacontractserializer) class, which converts your C# types to and from XML documents. When sending or receiving primitives (like strings or ints), this serialization happens transparently and there's no requisite preparation needed on your part. However, when working with complex types such as those you create, there are some important rules to take into consideration so this process works smoothly.
+
+This serialization framework is not specific to Dapr and is separately maintained by the .NET team within the [.NET Github repository](https://github.com/dotnet/runtime/blob/main/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContractSerializer.cs).
+
+## Serializable Types
+There are several important considerations to keep in mind when using the Data Contract Serializer:
+
+- By default, all types, read/write properties (after construction) and fields marked as publicly visible are serialized
+- All types must either expose a public parameterless constructor or be decorated with the DataContractAttribute attribute
+- Init-only setters are only supported with the use of the DataContractAttribute attribute
+- Read-only fields, properties without a Get and Set method and internal or properties with private Get and Set methods are ignored during serialization
+- Serialization is supported for types that use other complex types that are not themselves marked with the DataContractAttribute attribute through the use of the KnownTypesAttribute attribute
+- If a type is marked with the DataContractAttribute attribute, all members you wish to serialize and deserialize must be decorated with the DataMemberAttribute attribute as well or they'll be set to their default values
+
+## How does deserialization work?
+The approach used for deserialization depends on whether or not the type is decorated with the [DataContractAttribute](https://learn.microsoft.com/en-us/dotnet/api/system.runtime.serialization.datacontractattribute) attribute. If this attribute isn't present, an instance of the type is created using the parameterless constructor. Each of the properties and fields are then mapped into the type using their respective setters and the instance is returned to the caller.
+
+If the type _is_ marked with `[DataContract]`, the serializer instead uses reflection to read the metadata of the type and determine which properties or fields should be included based on whether or not they're marked with the DataMemberAttribute attribute as it's performed on an opt-in basis. It then allocates an uninitialized object in memory (avoiding the use of any constructors, parameterless or not) and then sets the value directly on each mapped property or field, even if private or uses init-only setters. Serialization callbacks are invoked as applicable throughout this process and then the object is returned to the caller.
+
+Use of the serialization attributes is highly recommended as they grant more flexibility to override names and namespaces and generally use more of the modern C# functionality. While the default serializer can be relied on for primitive types, it's not recommended for any of your own types, whether they be classes, structs or records. It's recommended that if you decorate a type with the DataContractAttribute attribute, you also explicitly decorate each of the members you want to serialize or deserialize with the DataMemberAttribute attribute as well.
+
+### .NET Classes
+Classes are fully supported in the Data Contract Serializer provided that that other rules detailed on this page and the [Data Contract Serializer](https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/serializable-types) documentation are also followed.
+
+The most important thing to remember here is that you must either have a public parameterless constructor or you must decorate it with the appropriate attributes. Let's review some examples to really clarify what will and won't work.
+
+In the following example, we present a simple class named Doodad. We don't provide an explicit constructor here, so the compiler will provide an default parameterless constructor. Because we're using [supported primitive types](###supported-primitive-types) (Guid, string and int32) and all our members have a public getter and setter, no attributes are required and we'll be able to use this class without issue when sending and receiving it from a Dapr actor method.
+
+```csharp
+public class Doodad
+{
+ public Guid Id { get; set; }
+ public string Name { get; set; }
+ public int Count { get; set; }
+}
+```
+
+By default, this will serialize using the names of the members as used in the type and whatever values it was instantiated with:
+
+```xml
+
+ a06ced64-4f42-48ad-84dd-46ae6a7e333d
+ DoodadName
+ 5
+
+```
+
+So let's tweak it - let's add our own constructor and only use init-only setters on the members. This will fail to serialize and deserialize not because of the use of the init-only setters, but because there's no parameterless constructors.
+
+```csharp
+// WILL NOT SERIALIZE PROPERLY!
+public class Doodad
+{
+ public Doodad(string name, int count)
+ {
+ Id = Guid.NewGuid();
+ Name = name;
+ Count = count;
+ }
+
+ public Guid Id { get; set; }
+ public string Name { get; init; }
+ public int Count { get; init; }
+}
+```
+
+If we add a public parameterless constructor to the type, we're good to go and this will work without further annotations.
+
+```csharp
+public class Doodad
+{
+ public Doodad()
+ {
+ }
+
+ public Doodad(string name, int count)
+ {
+ Id = Guid.NewGuid();
+ Name = name;
+ Count = count;
+ }
+
+ public Guid Id { get; set; }
+ public string Name { get; set; }
+ public int Count { get; set; }
+}
+```
+
+But what if we don't want to add this constructor? Perhaps you don't want your developers to accidentally create an instance of this Doodad using an unintended constructor. That's where the more flexible attributes are useful. If you decorate your type with a [DataContractAttribute](https://learn.microsoft.com/en-us/dotnet/api/system.runtime.serialization.datacontractattribute) attribute, you can drop your parameterless constructor and it will work once again.
+
+```csharp
+[DataContract]
+public class Doodad
+{
+ public Doodad(string name, int count)
+ {
+ Id = Guid.NewGuid();
+ Name = name;
+ Count = count;
+ }
+
+ public Guid Id { get; set; }
+ public string Name { get; set; }
+ public int Count { get; set; }
+}
+```
+
+In the above example, we don't need to also use the [DataMemberAttribute](https://learn.microsoft.com/en-us/dotnet/api/system.runtime.serialization.datamemberattribute) attributes because again, we're using [built-in primitives](###supported-primitive-types) that the serializer supports. But, we do get more flexibility if we use the attributes. From the DataContractAttribute attribute, we can specify our own XML namespace with the Namespace argument and, via the Name argument, change the name of the type as used when serialized into the XML document.
+
+It's a recommended practice to append the DataContractAttribute attribute to the type and the DataMemberAttribute attributes to all the members you want to serialize anyway - if they're not necessary and you're not changing the default values, they'll just be ignored, but they give you a mechanism to opt into serializing members that wouldn't otherwise have been included such as those marked as private or that are themselves complex types or collections.
+
+Note that if you do opt into serializing your private members, their values will be serialized into plain text - they can very well be viewed, intercepted and potentially manipulated based on how you're handing the data once serialized, so it's an important consideration whether you want to mark these members or not in your use case.
+
+In the following example, we'll look at using the attributes to change the serialized names of some of the members as well as introduce the [IgnoreDataMemberAttribute](https://learn.microsoft.com/en-us/dotnet/api/system.runtime.serialization.ignoredatamemberattribute) attribute. As the name indicates, this tells the serializer to skip this property even though it'd be otherwise eligible to serialize. Further, because I'm decorating the type with the DataContractAttribute attribute, it means that I can use init-only setters on the properties.
+
+```csharp
+[DataContract(Name="Doodad")]
+public class Doodad
+{
+ public Doodad(string name = "MyDoodad", int count = 5)
+ {
+ Id = Guid.NewGuid();
+ Name = name;
+ Count = count;
+ }
+
+ [DataMember(Name = "id")]
+ public Guid Id { get; init; }
+ [IgnoreDataMember]
+ public string Name { get; init; }
+ [DataMember]
+ public int Count { get; init; }
+}
+```
+
+When this is serialized, because we're changing the names of the serialized members, we can expect a new instance of Doodad using the default values this to be serialized as:
+
+```xml
+
+ a06ced64-4f42-48ad-84dd-46ae6a7e333d
+ 5
+
+```
+
+#### Classes in C# 12 - Primary Constructors
+C# 12 brought us primary constructors on classes. Use of a primary constructor means the compiler will be prevented from creating the default implicit parameterless constructor. While a primary constructor on a class doesn't generate any public properties, it does mean that if you pass this primary constructor any arguments or have non-primitive types in your class, you'll either need to specify your own parameterless constructor or use the serialization attributes.
+
+Here's an example where we're using the primary constructor to inject an ILogger to a field and add our own parameterless constructor without the need for any attributes.
+
+```csharp
+public class Doodad(ILogger _logger)
+{
+ public Doodad() {} //Our parameterless constructor
+
+ public Doodad(string name, int count)
+ {
+ Id = Guid.NewGuid();
+ Name = name;
+ Count = count;
+ }
+
+ public Guid Id { get; set; }
+ public string Name { get; set; }
+ public int Count { get; set; }
+}
+```
+
+And using our serialization attributes (again, opting for init-only setters since we're using the serialization attributes):
+
+```csharp
+[DataContract]
+public class Doodad(ILogger _logger)
+{
+ public Doodad(string name, int count)
+ {
+ Id = Guid.NewGuid();
+ Name = name;
+ Count = count;
+ }
+
+ [DataMember]
+ public Guid Id { get; init; }
+ [DataMember]
+ public string Name { get; init; }
+ [DataMember]
+ public int Count { get; init; }
+}
+```
+
+### .NET Structs
+Structs are supported by the Data Contract serializer provided that they are marked with the DataContractAttribute attribute and the members you wish to serialize are marked with the DataMemberAttribute attribute. Further, to support deserialization, the struct will also need to have a parameterless constructor. This works even if you define your own parameterless constructor as enabled in C# 10.
+
+```csharp
+[DataContract]
+public struct Doodad
+{
+ [DataMember]
+ public int Count { get; set; }
+}
+```
+
+### .NET Records
+Records were introduced in C# 9 and follow precisely the same rules as classes when it comes to serialization. We recommend that you should decorate all your records with the DataContractAttribute attribute and members you wish to serialize with DataMemberAttribute attributes so you don't experience any deserialization issues using this or other newer C# functionalities. Because record classes use init-only setters for properties by default and encourage the use of the primary constructor, applying these attributes to your types ensures that the serializer can properly otherwise accommodate your types as-is.
+
+Typically records are presented as a simple one-line statement using the new primary constructor concept:
+
+```csharp
+public record Doodad(Guid Id, string Name, int Count);
+```
+
+This will throw an error encouraging the use of the serialization attributes as soon as you use it in a Dapr actor method invocation because there's no parameterless constructor available nor is it decorated with the aforementioned attributes.
+
+Here we add an explicit parameterless constructor and it won't throw an error, but none of the values will be set during deserialization since they're created with init-only setters. Because this doesn't use the DataContractAttribute attribute or the DataMemberAttribute attribute on any members, the serializer will be unable to map the target members correctly during deserialization.
+```csharp
+public record Doodad(Guid Id, string Name, int Count)
+{
+ public Doodad() {}
+}
+```
+
+This approach does without the additional constructor and instead relies on the serialization attributes. Because we mark the type with the DataContractAttribute attribute and decorate each member with its own DataMemberAttribute attribute, the serialization engine will be able to map from the XML document to our type without issue.
+```csharp
+[DataContract]
+public record Doodad(
+ [property: DataMember] Guid Id,
+ [property: DataMember] string Name,
+ [property: DataMember] int Count)
+```
+
+### Supported Primitive Types
+There are several types built into .NET that are considered primitive and eligible for serialization without additional effort on the part of the developer:
+
+- [Byte](https://learn.microsoft.com/en-us/dotnet/api/system.byte)
+- [SByte](https://learn.microsoft.com/en-us/dotnet/api/system.sbyte)
+- [Int16](https://learn.microsoft.com/en-us/dotnet/api/system.int16)
+- [Int32](https://learn.microsoft.com/en-us/dotnet/api/system.int32)
+- [Int64](https://learn.microsoft.com/en-us/dotnet/api/system.int64)
+- [UInt16](https://learn.microsoft.com/en-us/dotnet/api/system.uint16)
+- [UInt32](https://learn.microsoft.com/en-us/dotnet/api/system.uint32)
+- [UInt64](https://learn.microsoft.com/en-us/dotnet/api/system.uint64)
+- [Single](https://learn.microsoft.com/en-us/dotnet/api/system.single)
+- [Double](https://learn.microsoft.com/en-us/dotnet/api/system.double)
+- [Boolean](https://learn.microsoft.com/en-us/dotnet/api/system.boolean)
+- [Char](https://learn.microsoft.com/en-us/dotnet/api/system.char)
+- [Decimal](https://learn.microsoft.com/en-us/dotnet/api/system.decimal)
+- [Object](https://learn.microsoft.com/en-us/dotnet/api/system.object)
+- [String](https://learn.microsoft.com/en-us/dotnet/api/system.string)
+
+There are additional types that aren't actually primitives but have similar built-in support:
+
+- [DateTime](https://learn.microsoft.com/en-us/dotnet/api/system.datetime)
+- [TimeSpan](https://learn.microsoft.com/en-us/dotnet/api/system.timespan)
+- [Guid](https://learn.microsoft.com/en-us/dotnet/api/system.guid)
+- [Uri](https://learn.microsoft.com/en-us/dotnet/api/system.uri)
+- [XmlQualifiedName](https://learn.microsoft.com/en-us/dotnet/api/system.xml.xmlqualifiedname)
+
+Again, if you want to pass these types around via your actor methods, no additional consideration is necessary as they'll be serialized and deserialized without issue. Further, types that are themselves marked with the (SerializeableAttribute)[https://learn.microsoft.com/en-us/dotnet/api/system.serializableattribute] attribute will be serialized.
+
+### Enumeration Types
+Enumerations, including flag enumerations are serializable if appropriately marked. The enum members you wish to be serialized must be marked with the [EnumMemberAttribute](https://learn.microsoft.com/en-us/dotnet/api/system.runtime.serialization.enummemberattribute) attribute in order to be serialized. Passing a custom value into the optional Value argument on this attribute will allow you to specify the value used for the member in the serialized document instead of having the serializer derive it from the name of the member.
+
+The enum type does not require that the type be decorated with the `DataContractAttribute` attribute - only that the members you wish to serialize be decorated with the `EnumMemberAttribute` attributes.
+
+```csharp
+public enum Colors
+{
+ [EnumMember]
+ Red,
+ [EnumMember(Value="g")]
+ Green,
+ Blue, //Even if used by a type, this value will not be serialized as it's not decorated with the EnumMember attribute
+}
+```
+
+### Collection Types
+With regards to the data contact serializer, all collection types that implement the [IEnumerable](https://learn.microsoft.com/en-us/dotnet/api/system.collections.ienumerable) interface including arays and generic collections are considered collections. Those types that implement [IDictionary](https://learn.microsoft.com/en-us/dotnet/api/system.collections.idictionary) or the generic [IDictionary](https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.idictionary-2) are considered dictionary collections; all others are list collections.
+
+Not unlike other complex types, collection types must have a parameterless constructor available. Further, they must also have a method called Add so they can be properly serialized and deserialized. The types used by these collection types must themselves be marked with the `DataContractAttribute` attribute or otherwise be serializable as described throughout this document.
+
+### Data Contract Versioning
+As the data contract serializer is only used in Dapr with respect to serializing the values in the .NET SDK to and from the Dapr actor instances via the proxy methods, there's little need to consider versioning of data contracts as the data isn't being persisted between application versions using the same serializer. For those interested in learning more about data contract versioning visit [here](https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/data-contract-versioning).
+
+### Known Types
+Nesting your own complex types is easily accommodated by marking each of the types with the [DataContractAttribute](https://learn.microsoft.com/en-us/dotnet/api/system.runtime.serialization.datacontractattribute) attribute. This informs the serializer as to how deserialization should be performed.
+But what if you're working with polymorphic types and one of your members is a base class or interface with derived classes or other implementations? Here, you'll use the [KnownTypeAttribute](https://learn.microsoft.com/en-us/dotnet/api/system.runtime.serialization.knowntypeattribute) attribute to give a hint to the serializer about how to proceed.
+
+When you apply the [KnownTypeAttribute](https://learn.microsoft.com/en-us/dotnet/api/system.runtime.serialization.knowntypeattribute) attribute to a type, you are informing the data contract serializer about what subtypes it might encounter allowing it to properly handle the serialization and deserialization of these types, even when the actual type at runtime is different from the declared type.
+
+```chsarp
+[DataContract]
+[KnownType(typeof(DerivedClass))]
+public class BaseClass
+{
+ //Members of the base class
+}
+
+[DataContract]
+public class DerivedClass : BaseClass
+{
+ //Additional members of the derived class
+}
+```
+
+In this example, the `BaseClass` is marked with `[KnownType(typeof(DerivedClass))]` which tells the data contract serializer that `DerivedClass` is a possible implementation of `BaseClass` that it may need to serialize or deserialize. Without this attribute, the serialize would not be aware of the `DerivedClass` when it encounters an instance of `BaseClass` that is actually of type `DerivedClass` and this could lead to a serialization exception because the serializer would not know how to handle the derived type. By specifying all possible derived types as known types, you ensure that the serializer can process the type and its members correctly.
+
+For more information and examples about using `[KnownType]`, please refer to the [official documentation](https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/data-contract-known-types).
\ No newline at end of file
diff --git a/daprdocs/content/en/dotnet-sdk-docs/dotnet-client/_index.md b/daprdocs/content/en/dotnet-sdk-docs/dotnet-client/_index.md
index 0404857e8..cab44468b 100644
--- a/daprdocs/content/en/dotnet-sdk-docs/dotnet-client/_index.md
+++ b/daprdocs/content/en/dotnet-sdk-docs/dotnet-client/_index.md
@@ -21,13 +21,22 @@ The .NET SDK allows you to interface with all of the [Dapr building blocks]({{<
### Invoke a service
+#### HTTP
You can either use the `DaprClient` or `System.Net.Http.HttpClient` to invoke your services.
+{{% alert title="Note" color="primary" %}}
+ You can also [invoke a non-Dapr endpoint using either a named `HTTPEndpoint` or an FQDN URL to the non-Dapr environment]({{< ref "howto-invoke-non-dapr-endpoints.md#using-an-httpendpoint-resource-or-fqdn-url-for-non-dapr-endpoints" >}}).
+
+{{% /alert %}}
+
+
{{< tabs SDK HTTP>}}
{{% codetab %}}
```csharp
-using var client = new DaprClientBuilder().Build();
+using var client = new DaprClientBuilder().
+ UseTimeout(TimeSpan.FromSeconds(2)). // Optionally, set a timeout
+ Build();
// Invokes a POST method named "deposit" that takes input of type "Transaction"
var data = new { id = "17", amount = 99m };
@@ -40,15 +49,31 @@ Console.WriteLine("Returned: id:{0} | Balance:{1}", account.Id, account.Balance)
```csharp
var client = DaprClient.CreateInvokeHttpClient(appId: "routing");
+// To set a timeout on the HTTP client:
+client.Timeout = TimeSpan.FromSeconds(2);
+
var deposit = new Transaction { Id = "17", Amount = 99m };
var response = await client.PostAsJsonAsync("/deposit", deposit, cancellationToken);
var account = await response.Content.ReadFromJsonAsync(cancellationToken: cancellationToken);
Console.WriteLine("Returned: id:{0} | Balance:{1}", account.Id, account.Balance);
```
{{% /codetab %}}
-
{{< /tabs >}}
+#### gRPC
+You can use the `DaprClient` to invoke your services over gRPC.
+
+```csharp
+using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(20));
+var invoker = DaprClient.CreateInvocationInvoker(appId: myAppId, daprEndpoint: serviceEndpoint);
+var client = new MyService.MyServiceClient(invoker);
+
+var options = new CallOptions(cancellationToken: cts.Token, deadline: DateTime.UtcNow.AddSeconds(1));
+await client.MyMethodAsync(new Empty(), options);
+
+Assert.Equal(StatusCode.DeadlineExceeded, ex.StatusCode);
+```
+
- For a full guide on service invocation visit [How-To: Invoke a service]({{< ref howto-invoke-discover-services.md >}}).
### Save & get application state
@@ -105,7 +130,7 @@ Console.WriteLine("Published deposit event!");
```
- For a full list of state operations visit [How-To: Publish & subscribe]({{< ref howto-publish-subscribe.md >}}).
-- Visit [.NET SDK examples](https://github.com/dapr/dotnet-sdk/tree/master/examples/client/PublishSubscribe) for code samples and instructions to try out pub/sub
+- Visit [.NET SDK examples](https://github.com/dapr/dotnet-sdk/tree/master/examples/Client/PublishSubscribe) for code samples and instructions to try out pub/sub
### Interact with output bindings
@@ -141,7 +166,7 @@ var secrets = await client.GetSecretAsync("mysecretstore", "key-value-pair-secre
Console.WriteLine($"Got secret keys: {string.Join(", ", secrets.Keys)}");
```
-{{% / codetab %}}
+{{% /codetab %}}
{{% codetab %}}
@@ -165,7 +190,7 @@ Console.WriteLine("Got a secret value, I'm not going to be print it, it's a secr
- For a full guide on secrets visit [How-To: Retrieve secrets]({{< ref howto-secrets.md >}}).
-### Get Configuration Keys (Alpha)
+### Get Configuration Keys
```csharp
var client = new DaprClientBuilder().Build();
@@ -182,7 +207,7 @@ foreach (var item in configItems)
}
```
-### Subscribe to Configuration Keys (Alpha)
+### Subscribe to Configuration Keys
```csharp
var client = new DaprClientBuilder().Build();
@@ -199,6 +224,64 @@ await foreach (var items in subscribeConfigurationResponse.Source.WithCancellati
}
```
+### Distributed lock (Alpha)
+
+#### Acquire a lock
+
+```csharp
+using System;
+using Dapr.Client;
+
+namespace LockService
+{
+ class Program
+ {
+ [Obsolete("Distributed Lock API is in Alpha, this can be removed once it is stable.")]
+ static async Task Main(string[] args)
+ {
+ var daprLockName = "lockstore";
+ var fileName = "my_file_name";
+ var client = new DaprClientBuilder().Build();
+
+ // Locking with this approach will also unlock it automatically, as this is a disposable object
+ await using (var fileLock = await client.Lock(DAPR_LOCK_NAME, fileName, "random_id_abc123", 60))
+ {
+ if (fileLock.Success)
+ {
+ Console.WriteLine("Success");
+ }
+ else
+ {
+ Console.WriteLine($"Failed to lock {fileName}.");
+ }
+ }
+ }
+ }
+}
+```
+
+#### Unlock an existing lock
+
+```csharp
+using System;
+using Dapr.Client;
+
+namespace LockService
+{
+ class Program
+ {
+ static async Task Main(string[] args)
+ {
+ var daprLockName = "lockstore";
+ var client = new DaprClientBuilder().Build();
+
+ var response = await client.Unlock(DAPR_LOCK_NAME, "my_file_name", "random_id_abc123"));
+ Console.WriteLine(response.status);
+ }
+ }
+}
+```
+
### Manage workflow instances (Alpha)
```csharp
diff --git a/daprdocs/content/en/dotnet-sdk-docs/dotnet-client/dotnet-daprclient-usage.md b/daprdocs/content/en/dotnet-sdk-docs/dotnet-client/dotnet-daprclient-usage.md
index 00b330693..26328050c 100644
--- a/daprdocs/content/en/dotnet-sdk-docs/dotnet-client/dotnet-daprclient-usage.md
+++ b/daprdocs/content/en/dotnet-sdk-docs/dotnet-client/dotnet-daprclient-usage.md
@@ -34,8 +34,10 @@ The `DaprClientBuilder` contains settings for:
The SDK will read the following environment variables to configure the default values:
-- `DAPR_HTTP_PORT`: used to find the HTTP endpoint of the Dapr sidecar
-- `DAPR_GRPC_PORT`: used to find the gRPC endpoint of the Dapr sidecar
+- `DAPR_HTTP_ENDPOINT`: used to find the HTTP endpoint of the Dapr sidecar, example: `https://dapr-api.mycompany.com`
+- `DAPR_GRPC_ENDPOINT`: used to find the gRPC endpoint of the Dapr sidecar, example: `https://dapr-grpc-api.mycompany.com`
+- `DAPR_HTTP_PORT`: if `DAPR_HTTP_ENDPOINT` is not set, this is used to find the HTTP local endpoint of the Dapr sidecar
+- `DAPR_GRPC_PORT`: if `DAPR_GRPC_ENDPOINT` is not set, this is used to find the gRPC local endpoint of the Dapr sidecar
- `DAPR_API_TOKEN`: used to set the API Token
### Configuring gRPC channel options
diff --git a/daprdocs/content/en/dotnet-sdk-docs/dotnet-workflow/dotnet-workflow-howto.md b/daprdocs/content/en/dotnet-sdk-docs/dotnet-workflow/dotnet-workflow-howto.md
index d19aee6dd..f6d18bc58 100644
--- a/daprdocs/content/en/dotnet-sdk-docs/dotnet-workflow/dotnet-workflow-howto.md
+++ b/daprdocs/content/en/dotnet-sdk-docs/dotnet-workflow/dotnet-workflow-howto.md
@@ -8,13 +8,13 @@ description: Learn how to author and manage Dapr Workflow using the .NET SDK
Let's create a Dapr workflow and invoke it using the console. In the [provided order processing workflow example](https://github.com/dapr/dotnet-sdk/tree/master/examples/Workflow), the console prompts provide directions on how to both purchase and restock items. In this guide, you will:
-- Create a .NET console application ([WorkflowConsoleApp](./WorkflowConsoleApp)).
+- Deploy a .NET console application ([WorkflowConsoleApp](https://github.com/dapr/dotnet-sdk/tree/master/examples/Workflow/WorkflowConsoleApp)).
- Utilize the .NET workflow SDK and API calls to start and query workflow instances.
In the .NET example project:
-- The main `Program.cs` file contains the setup of the app, including the registration of the workflow and workflow activities.
-- The workflow definition is found in the `Workflows` directory.
-- The workflow activity definitions are found in the `Activities` directory.
+- The main [`Program.cs`](https://github.com/dapr/dotnet-sdk/blob/master/examples/Workflow/WorkflowConsoleApp/Program.cs) file contains the setup of the app, including the registration of the workflow and workflow activities.
+- The workflow definition is found in the [`Workflows` directory](https://github.com/dapr/dotnet-sdk/tree/master/examples/Workflow/WorkflowConsoleApp/Workflows).
+- The workflow activity definitions are found in the [`Activities` directory](https://github.com/dapr/dotnet-sdk/tree/master/examples/Workflow/WorkflowConsoleApp/Activities).
## Prerequisites
@@ -72,7 +72,7 @@ This guide focuses on the workflow API option.
{{% alert title="Note" color="primary" %}}
- You can find the commands below in the `WorkflowConsoleApp`/`demo.http` file.
- The body of the curl request is the purchase order information used as the input of the workflow.
- - The "1234" in the commands represents the unique identifier for the workflow and can be replaced with any identifier of your choosing.
+ - The "12345678" in the commands represents the unique identifier for the workflow and can be replaced with any identifier of your choosing.
{{% /alert %}}
@@ -83,9 +83,9 @@ Run the following command to start a workflow.
{{% codetab %}}
```bash
-curl -i -X POST http://localhost:3500/v1.0-alpha1/workflows/dapr/OrderProcessingWorkflow/1234/start \
+curl -i -X POST http://localhost:3500/v1.0-beta1/workflows/dapr/OrderProcessingWorkflow/start?instanceID=12345678 \
-H "Content-Type: application/json" \
- -d '{ "input" : {"Name": "Paperclips", "TotalCost": 99.95, "Quantity": 1}}'
+ -d '{"Name": "Paperclips", "TotalCost": 99.95, "Quantity": 1}'
```
{{% /codetab %}}
@@ -93,9 +93,9 @@ curl -i -X POST http://localhost:3500/v1.0-alpha1/workflows/dapr/OrderProcessing
{{% codetab %}}
```powershell
-curl -i -X POST http://localhost:3500/v1.0-alpha1/workflows/dapr/OrderProcessingWorkflow/1234/start `
+curl -i -X POST http://localhost:3500/v1.0-beta1/workflows/dapr/OrderProcessingWorkflow/start?instanceID=12345678 `
-H "Content-Type: application/json" `
- -d '{ "input" : {"Name": "Paperclips", "TotalCost": 99.95, "Quantity": 1}}'
+ -d '{"Name": "Paperclips", "TotalCost": 99.95, "Quantity": 1}'
```
{{% /codetab %}}
@@ -105,30 +105,27 @@ curl -i -X POST http://localhost:3500/v1.0-alpha1/workflows/dapr/OrderProcessing
If successful, you should see a response like the following:
```json
-{"instance_id":"1234"}
+{"instanceID":"12345678"}
```
Send an HTTP request to get the status of the workflow that was started:
```bash
-curl -i -X GET http://localhost:3500/v1.0-alpha1/workflows/dapr/OrderProcessingWorkflow/1234
+curl -i -X GET http://localhost:3500/v1.0-beta1/workflows/dapr/12345678
```
The workflow is designed to take several seconds to complete. If the workflow hasn't completed when you issue the HTTP request, you'll see the following JSON response (formatted for readability) with workflow status as `RUNNING`:
```json
{
- "WFInfo": {
- "instance_id": "1234"
- },
- "start_time": "2023-02-02T23:34:53Z",
- "metadata": {
+ "instanceID": "12345678",
+ "workflowName": "OrderProcessingWorkflow",
+ "createdAt": "2023-05-10T00:42:03.911444105Z",
+ "lastUpdatedAt": "2023-05-10T00:42:06.142214153Z",
+ "runtimeStatus": "RUNNING",
+ "properties": {
"dapr.workflow.custom_status": "",
- "dapr.workflow.input": "{\"Name\":\"Paperclips\",\"Quantity\":1,\"TotalCost\":99.95}",
- "dapr.workflow.last_updated": "2023-02-02T23:35:07Z",
- "dapr.workflow.name": "OrderProcessingWorkflow",
- "dapr.workflow.output": "{\"Processed\":true}",
- "dapr.workflow.runtime_status": "RUNNING"
+ "dapr.workflow.input": "{\"Name\": \"Paperclips\", \"TotalCost\": 99.95, \"Quantity\": 1}"
}
}
```
@@ -137,17 +134,15 @@ Once the workflow has completed running, you should see the following output, in
```json
{
- "WFInfo": {
- "instance_id": "1234"
- },
- "start_time": "2023-02-02T23:34:53Z",
- "metadata": {
+ "instanceID": "12345678",
+ "workflowName": "OrderProcessingWorkflow",
+ "createdAt": "2023-05-10T00:42:03.911444105Z",
+ "lastUpdatedAt": "2023-05-10T00:42:18.527704176Z",
+ "runtimeStatus": "COMPLETED",
+ "properties": {
"dapr.workflow.custom_status": "",
- "dapr.workflow.input": "{\"Name\":\"Paperclips\",\"Quantity\":1,\"TotalCost\":99.95}",
- "dapr.workflow.last_updated": "2023-02-02T23:35:07Z",
- "dapr.workflow.name": "OrderProcessingWorkflow",
- "dapr.workflow.output": "{\"Processed\":true}",
- "dapr.workflow.runtime_status": "COMPLETED"
+ "dapr.workflow.input": "{\"Name\": \"Paperclips\", \"TotalCost\": 99.95, \"Quantity\": 1}",
+ "dapr.workflow.output": "{\"Processed\":true}"
}
}
```
@@ -156,17 +151,23 @@ When the workflow has completed, the stdout of the workflow app should look like
```log
info: WorkflowConsoleApp.Activities.NotifyActivity[0]
- Received order 1234 for Paperclips at $99.95
+ Received order 12345678 for Paperclips at $99.95
info: WorkflowConsoleApp.Activities.ReserveInventoryActivity[0]
- Reserving inventory: 1234, Paperclips, 1
+ Reserving inventory: 12345678, Paperclips, 1
info: WorkflowConsoleApp.Activities.ProcessPaymentActivity[0]
- Processing payment: 1234, 99.95, USD
+ Processing payment: 12345678, 99.95, USD
info: WorkflowConsoleApp.Activities.NotifyActivity[0]
- Order 1234 processed successfully!
+ Order 12345678 processed successfully!
```
If you have Zipkin configured for Dapr locally on your machine, then you can view the workflow trace spans in the Zipkin web UI (typically at http://localhost:9411/zipkin/).
+## Demo
+
+Watch this video [demonstrating .NET Workflow](https://youtu.be/BxiKpEmchgQ?t=2557):
+
+
+
## Next steps
- [Try the Dapr Workflow quickstart]({{< ref workflow-quickstart.md >}})
diff --git a/examples/Actor/ActorClient/ActorClient.csproj b/examples/Actor/ActorClient/ActorClient.csproj
index 48c8318c6..0d1d94f55 100644
--- a/examples/Actor/ActorClient/ActorClient.csproj
+++ b/examples/Actor/ActorClient/ActorClient.csproj
@@ -2,7 +2,7 @@
Exe
- netcoreapp3.1
+ net6
diff --git a/examples/Actor/ActorClient/Program.cs b/examples/Actor/ActorClient/Program.cs
index 103aed6b9..f6ca26f53 100644
--- a/examples/Actor/ActorClient/Program.cs
+++ b/examples/Actor/ActorClient/Program.cs
@@ -1,4 +1,4 @@
-// ------------------------------------------------------------------------
+// ------------------------------------------------------------------------
// Copyright 2021 The Dapr Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -11,6 +11,9 @@
// limitations under the License.
// ------------------------------------------------------------------------
+using Dapr.Actors.Communication;
+using IDemoActor;
+
namespace ActorClient
{
using System;
@@ -18,8 +21,6 @@ namespace ActorClient
using System.Threading.Tasks;
using Dapr.Actors;
using Dapr.Actors.Client;
- using Dapr.Actors.Communication;
- using IDemoActorInterface;
///
/// Actor Client class.
@@ -44,10 +45,10 @@ public static async Task Main(string[] args)
// Make strongly typed Actor calls with Remoting.
// DemoActor is the type registered with Dapr runtime in the service.
- var proxy = ActorProxy.Create(actorId, "DemoActor");
+ var proxy = ActorProxy.Create(actorId, "DemoActor");
Console.WriteLine("Making call using actor proxy to save data.");
- await proxy.SaveData(data);
+ await proxy.SaveData(data, TimeSpan.FromMinutes(10));
Console.WriteLine("Making call using actor proxy to get data.");
var receivedData = await proxy.GetData();
Console.WriteLine($"Received data is {receivedData}.");
@@ -69,7 +70,7 @@ public static async Task Main(string[] args)
}
catch (ActorMethodInvocationException ex)
{
- if (ex.InnerException is NotImplementedException)
+ if (ex.InnerException is ActorInvokeException invokeEx && invokeEx.ActualExceptionType is "System.NotImplementedException")
{
Console.WriteLine($"Got Correct Exception from actor method invocation.");
}
@@ -96,6 +97,10 @@ public static async Task Main(string[] args)
receivedData = await proxy.GetData();
Console.WriteLine($"Received data is {receivedData}.");
+ Console.WriteLine("Getting details of the registered reminder");
+ var reminder = await proxy.GetReminder();
+ Console.WriteLine($"Received reminder is {reminder}.");
+
Console.WriteLine("Deregistering timer. Timers would any way stop if the actor is deactivated as part of Dapr garbage collection.");
await proxy.UnregisterTimer();
Console.WriteLine("Deregistering reminder. Reminders are durable and would not stop until an explicit deregistration or the actor is deleted.");
@@ -105,14 +110,23 @@ public static async Task Main(string[] args)
await proxy.RegisterReminderWithRepetitions(3);
Console.WriteLine("Waiting so the reminder can be triggered");
await Task.Delay(5000);
+ Console.WriteLine("Getting details of the registered reminder");
+ reminder = await proxy.GetReminder();
+ Console.WriteLine($"Received reminder is {reminder?.ToString() ?? "None"} (expecting None).");
Console.WriteLine("Registering reminder with ttl and repetitions, i.e. reminder stops when either condition is met - The reminder will repeat 2 times.");
await proxy.RegisterReminderWithTtlAndRepetitions(TimeSpan.FromSeconds(5), 2);
+ Console.WriteLine("Getting details of the registered reminder");
+ reminder = await proxy.GetReminder();
+ Console.WriteLine($"Received reminder is {reminder}.");
Console.WriteLine("Deregistering reminder. Reminders are durable and would not stop until an explicit deregistration or the actor is deleted.");
await proxy.UnregisterReminder();
Console.WriteLine("Registering reminder and Timer with TTL - The reminder will self delete after 10 seconds.");
await proxy.RegisterReminderWithTtl(TimeSpan.FromSeconds(10));
await proxy.RegisterTimerWithTtl(TimeSpan.FromSeconds(10));
+ Console.WriteLine("Getting details of the registered reminder");
+ reminder = await proxy.GetReminder();
+ Console.WriteLine($"Received reminder is {reminder}.");
// Track the reminder.
var timer = new Timer(async state => Console.WriteLine($"Received data: {await proxy.GetData()}"), null, TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(5));
diff --git a/examples/Actor/DemoActor/BankService.cs b/examples/Actor/DemoActor/BankService.cs
index 0a164183f..a24eadedb 100644
--- a/examples/Actor/DemoActor/BankService.cs
+++ b/examples/Actor/DemoActor/BankService.cs
@@ -11,9 +11,9 @@
// limitations under the License.
// ------------------------------------------------------------------------
-using IDemoActorInterface;
+using IDemoActor;
-namespace DaprDemoActor
+namespace DemoActor
{
public class BankService
{
diff --git a/examples/Actor/DemoActor/DemoActor.cs b/examples/Actor/DemoActor/DemoActor.cs
index 057b7df6d..b5ef53e93 100644
--- a/examples/Actor/DemoActor/DemoActor.cs
+++ b/examples/Actor/DemoActor/DemoActor.cs
@@ -1,4 +1,4 @@
-// ------------------------------------------------------------------------
+// ------------------------------------------------------------------------
// Copyright 2021 The Dapr Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -11,14 +11,14 @@
// limitations under the License.
// ------------------------------------------------------------------------
-namespace DaprDemoActor
-{
- using System;
- using System.Text.Json;
- using System.Threading.Tasks;
- using Dapr.Actors.Runtime;
- using IDemoActorInterface;
+using System;
+using System.Text.Json;
+using System.Threading.Tasks;
+using Dapr.Actors.Runtime;
+using IDemoActor;
+namespace DemoActor
+{
// The following example showcases a few features of Actors
//
// Every actor should inherit from the Actor type, and must implement one or more actor interfaces.
@@ -27,7 +27,7 @@ namespace DaprDemoActor
// For Actors to use Reminders, it must derive from IRemindable.
// If you don't intend to use Reminder feature, you can skip implementing IRemindable and reminder
// specific methods which are shown in the code below.
- public class DemoActor : Actor, IDemoActor, IBankActor, IRemindable
+ public class DemoActor : Actor, IDemoActor.IDemoActor, IBankActor, IRemindable
{
private const string StateName = "my_data";
@@ -41,12 +41,12 @@ public DemoActor(ActorHost host, BankService bank)
this.bank = bank;
}
- public async Task SaveData(MyData data)
+ public async Task SaveData(MyData data, TimeSpan ttl)
{
Console.WriteLine($"This is Actor id {this.Id} with data {data}.");
// Set State using StateManager, state is saved after the method execution.
- await this.StateManager.SetStateAsync(StateName, data);
+ await this.StateManager.SetStateAsync(StateName, data, ttl);
}
public Task GetData()
@@ -85,6 +85,20 @@ public async Task RegisterReminderWithTtlAndRepetitions(TimeSpan ttl, int repeti
await this.RegisterReminderAsync("TestReminder", null, TimeSpan.FromSeconds(0), TimeSpan.FromSeconds(1), repetitions, ttl);
}
+ public async Task GetReminder()
+ {
+ var reminder = await this.GetReminderAsync("TestReminder");
+
+ return reminder is not null
+ ? new ActorReminderData
+ {
+ Name = reminder.Name,
+ Period = reminder.Period,
+ DueTime = reminder.DueTime
+ }
+ : null;
+ }
+
public Task UnregisterReminder()
{
return this.UnregisterReminderAsync("TestReminder");
@@ -95,7 +109,7 @@ public async Task ReceiveReminderAsync(string reminderName, byte[] state, TimeSp
// This method is invoked when an actor reminder is fired.
var actorState = await this.StateManager.GetStateAsync(StateName);
actorState.PropertyB = $"Reminder triggered at '{DateTime.Now:yyyy-MM-ddTHH:mm:ss}'";
- await this.StateManager.SetStateAsync(StateName, actorState);
+ await this.StateManager.SetStateAsync(StateName, actorState, ttl: TimeSpan.FromMinutes(5));
}
class TimerParams
@@ -159,7 +173,7 @@ public async Task TimerCallback(byte[] data)
{
var state = await this.StateManager.GetStateAsync(StateName);
state.PropertyA = $"Timer triggered at '{DateTime.Now:yyyyy-MM-ddTHH:mm:s}'";
- await this.StateManager.SetStateAsync(StateName, state);
+ await this.StateManager.SetStateAsync(StateName, state, ttl: TimeSpan.FromMinutes(5));
var timerParams = JsonSerializer.Deserialize(data);
Console.WriteLine("Timer parameter1: " + timerParams.IntParam);
Console.WriteLine("Timer parameter2: " + timerParams.StringParam);
diff --git a/examples/Actor/DemoActor/DemoActor.csproj b/examples/Actor/DemoActor/DemoActor.csproj
index 80a3883c3..24a42ee0e 100644
--- a/examples/Actor/DemoActor/DemoActor.csproj
+++ b/examples/Actor/DemoActor/DemoActor.csproj
@@ -1,13 +1,24 @@
-
- netcoreapp3.1
-
-
-
-
-
-
-
+
+ net6
+
+
+
+ true
+ true
+ demo-actor
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/Actor/DemoActor/Program.cs b/examples/Actor/DemoActor/Program.cs
index a56681fdb..1d538b471 100644
--- a/examples/Actor/DemoActor/Program.cs
+++ b/examples/Actor/DemoActor/Program.cs
@@ -11,11 +11,11 @@
// limitations under the License.
// ------------------------------------------------------------------------
-namespace DaprDemoActor
-{
- using Microsoft.AspNetCore.Hosting;
- using Microsoft.Extensions.Hosting;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.Extensions.Hosting;
+namespace DemoActor
+{
public class Program
{
public static void Main(string[] args)
diff --git a/examples/Actor/DemoActor/Startup.cs b/examples/Actor/DemoActor/Startup.cs
index c04dfdcba..f1165e3c7 100644
--- a/examples/Actor/DemoActor/Startup.cs
+++ b/examples/Actor/DemoActor/Startup.cs
@@ -1,4 +1,4 @@
-// ------------------------------------------------------------------------
+// ------------------------------------------------------------------------
// Copyright 2021 The Dapr Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -11,14 +11,14 @@
// limitations under the License.
// ------------------------------------------------------------------------
-namespace DaprDemoActor
-{
- using Microsoft.AspNetCore.Builder;
- using Microsoft.AspNetCore.Hosting;
- using Microsoft.Extensions.Configuration;
- using Microsoft.Extensions.DependencyInjection;
- using Microsoft.Extensions.Hosting;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+namespace DemoActor
+{
public class Startup
{
public Startup(IConfiguration configuration)
diff --git a/examples/Actor/DemoActor/demo-actor.yaml b/examples/Actor/DemoActor/demo-actor.yaml
new file mode 100644
index 000000000..99a8abd34
--- /dev/null
+++ b/examples/Actor/DemoActor/demo-actor.yaml
@@ -0,0 +1,67 @@
+apiVersion: dapr.io/v1alpha1
+kind: Component
+metadata:
+ name: statestore
+spec:
+ type: state.in-memory
+ version: v1
+ metadata:
+ - name: actorStateStore
+ value: "true"
+---
+kind: Service
+apiVersion: v1
+metadata:
+ name: demoactor
+ labels:
+ app: demoactor
+spec:
+ selector:
+ app: demoactor
+ ports:
+ - name: app-port
+ protocol: TCP
+ port: 5010
+ targetPort: app-port
+ - name: dapr-http
+ protocol: TCP
+ port: 3500
+ targetPort: 3500
+ type: LoadBalancer
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: demoactor
+ labels:
+ app: demoactor
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ app: demoactor
+ template:
+ metadata:
+ labels:
+ app: demoactor
+ annotations:
+ dapr.io/enabled: "true"
+ dapr.io/app-id: "demoactor"
+ dapr.io/app-port: "5010"
+ dapr.io/enable-api-logging: "true"
+ dapr.io/sidecar-listen-addresses: "0.0.0.0"
+ spec:
+ containers:
+ - name: demoactor
+ # image: /demo-actor:latest
+ image: demo-actor:latest
+ # if you are using docker desktop, you can use imagePullPolicy: Never to use local image
+ imagePullPolicy: Never
+ env:
+ - name: APP_PORT
+ value: "5010"
+ - name: ASPNETCORE_URLS
+ value: "http://+:5010"
+ ports:
+ - name: app-port
+ containerPort: 5010
diff --git a/examples/Actor/IDemoActor/IBankActor.cs b/examples/Actor/IDemoActor/IBankActor.cs
index 95ac23844..c495f027b 100644
--- a/examples/Actor/IDemoActor/IBankActor.cs
+++ b/examples/Actor/IDemoActor/IBankActor.cs
@@ -11,12 +11,12 @@
// limitations under the License.
// ------------------------------------------------------------------------
-namespace IDemoActorInterface
-{
- using System;
- using System.Threading.Tasks;
- using Dapr.Actors;
+using System;
+using System.Threading.Tasks;
+using Dapr.Actors;
+namespace IDemoActor
+{
public interface IBankActor : IActor
{
Task GetAccountBalance();
diff --git a/examples/Actor/IDemoActor/IDemoActor.cs b/examples/Actor/IDemoActor/IDemoActor.cs
index 3220dfdbd..6f2d32801 100644
--- a/examples/Actor/IDemoActor/IDemoActor.cs
+++ b/examples/Actor/IDemoActor/IDemoActor.cs
@@ -1,4 +1,4 @@
-// ------------------------------------------------------------------------
+// ------------------------------------------------------------------------
// Copyright 2021 The Dapr Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -11,12 +11,12 @@
// limitations under the License.
// ------------------------------------------------------------------------
-namespace IDemoActorInterface
-{
- using System;
- using System.Threading.Tasks;
- using Dapr.Actors;
+using System;
+using System.Threading.Tasks;
+using Dapr.Actors;
+namespace IDemoActor
+{
///
/// Interface for Actor method.
///
@@ -26,8 +26,9 @@ public interface IDemoActor : IActor
/// Method to save data.
///
/// DAta to save.
+ /// TTL of state key.
/// A task that represents the asynchronous save operation.
- Task SaveData(MyData data);
+ Task SaveData(MyData data, TimeSpan ttl);
///
/// Method to get data.
@@ -94,6 +95,13 @@ public interface IDemoActor : IActor
/// A task that represents the asynchronous save operation.
Task RegisterReminderWithTtlAndRepetitions(TimeSpan ttl, int repetitions);
+ ///
+ /// Gets the registered reminder.
+ ///
+ /// The name of the reminder.
+ /// A task that returns the reminder after completion.
+ Task GetReminder();
+
///
/// Unregisters the registered timer.
///
@@ -124,4 +132,18 @@ public override string ToString()
return $"PropertyA: {propAValue}, PropertyB: {propBValue}";
}
}
+
+ public class ActorReminderData
+ {
+ public string Name { get; set; }
+
+ public TimeSpan DueTime { get; set; }
+
+ public TimeSpan Period { get; set; }
+
+ public override string ToString()
+ {
+ return $"Name: {this.Name}, DueTime: {this.DueTime}, Period: {this.Period}";
+ }
+ }
}
diff --git a/examples/Actor/IDemoActor/IDemoActor.csproj b/examples/Actor/IDemoActor/IDemoActor.csproj
index 23a96ffc6..9f7744796 100644
--- a/examples/Actor/IDemoActor/IDemoActor.csproj
+++ b/examples/Actor/IDemoActor/IDemoActor.csproj
@@ -1,7 +1,7 @@
- netcoreapp3.1
+ net6
diff --git a/examples/Actor/README.md b/examples/Actor/README.md
index ddc42cecf..89b6bf0bb 100644
--- a/examples/Actor/README.md
+++ b/examples/Actor/README.md
@@ -4,7 +4,7 @@ The Actor example shows how to create a virtual actor (`DemoActor`) and invoke i
## Prerequisites
-- [.NET Core 3.1 or .NET 5+](https://dotnet.microsoft.com/download) installed
+- [.NET 6+](https://dotnet.microsoft.com/download) installed
- [Dapr CLI](https://docs.dapr.io/getting-started/install-dapr-cli/)
- [Initialized Dapr environment](https://docs.dapr.io/getting-started/install-dapr-selfhost/)
- [Dapr .NET SDK](https://github.com/dapr/dotnet-sdk/)
@@ -80,3 +80,80 @@ On Windows:
```sh
curl -X POST http://127.0.0.1:3500/v1.0/actors/DemoActor/abc/method/GetData
```
+
+### Build and push Docker image
+You can build the docker image of `DemoActor` service by running the following commands in the `DemoActor` project directory:
+
+``` Bash
+dotnet publish --os linux --arch x64 /t:PublishContainer -p ContainerImageTags='"latest"' --self-contained
+```
+
+The build produce and image with tag `demo-actor:latest` and load it in the local registry.
+Now the image can be pushed to your remote Docker registry by running the following commands:
+
+``` Bash
+# Replace with your Docker registry
+docker tag demo-actor:latest /demo-actor:latest
+
+# Push the image to your Docker registry
+docker push /demo-actor:latest
+```
+
+### Deploy the Actor service to Kubernetes
+#### Prerequisites
+- A Kubernetes cluster with `kubectl` configured to access it.
+- Dapr v1.14+ installed on the Kubernetes cluster. Follow the instructions [here](https://docs.dapr.io/getting-started/install-dapr-kubernetes/).
+- A Docker registry where you pushed the `DemoActor` image.
+
+#### Deploy the Actor service
+For quick deployment you can install dapr in dev mode using the following command:
+
+``` Bash
+dapr init -k --dev
+```
+
+To deploy the `DemoActor` service to Kubernetes, you can use the provided Kubernetes manifest file `demo-actor.yaml` in the `DemoActor` project directory.
+Before applying the manifest file, replace the image name in the manifest file with the image name you pushed to your Docker registry.
+
+Part to update in `demo-actor.yaml`:
+``` YAML
+image: /demoactor:latest
+```
+
+To install the application in `default` namespace, run the following command:
+
+``` Bash
+kubectl apply -f demo-actor.yaml
+```
+
+This will deploy the `DemoActor` service to Kubernetes. You can check the status of the deployment by running:
+
+``` Bash
+kubectl get pods -n default --watch
+```
+
+The manifest create 2 services:
+
+- `demoactor` service: The service that hosts the `DemoActor` actor.
+- `demoactor-dapr` service: The service that hosts the Dapr sidecar for the `DemoActor` actor.
+
+### Make client calls to the deployed Actor service
+To make client calls to the deployed `DemoActor` service, you can use the `ActorClient` project.
+Before running the client, update the `DAPR_HTTP_PORT` environment variable in the `ActorClient` project directory to the port on which Dapr is running in the Kubernetes cluster.
+
+On Linux, MacOS:
+``` Bash
+export DAPR_HTTP_PORT=3500
+```
+
+Than port-forward the `DemoActor` service to your local machine:
+
+``` Bash
+kubectl port-forward svc/demoactor 3500:3500
+```
+
+Now you can run the client project from the `ActorClient` directory:
+
+``` Bash
+dotnet run
+```
\ No newline at end of file
diff --git a/examples/AspNetCore/ControllerSample/ControllerSample.csproj b/examples/AspNetCore/ControllerSample/ControllerSample.csproj
index 863290324..6dbe750a6 100644
--- a/examples/AspNetCore/ControllerSample/ControllerSample.csproj
+++ b/examples/AspNetCore/ControllerSample/ControllerSample.csproj
@@ -1,7 +1,7 @@
- netcoreapp3.1
+ net6
diff --git a/examples/AspNetCore/ControllerSample/Controllers/SampleController.cs b/examples/AspNetCore/ControllerSample/Controllers/SampleController.cs
index 485614150..5b339288c 100644
--- a/examples/AspNetCore/ControllerSample/Controllers/SampleController.cs
+++ b/examples/AspNetCore/ControllerSample/Controllers/SampleController.cs
@@ -11,6 +11,8 @@
// limitations under the License.
// ------------------------------------------------------------------------
+using System.Linq;
+
namespace ControllerSample.Controllers
{
using System;
@@ -43,6 +45,7 @@ public SampleController(ILogger logger)
/// State store name.
///
public const string StoreName = "statestore";
+
private readonly ILogger logger;
///
@@ -72,6 +75,11 @@ public ActionResult Get([FromState(StoreName)] StateEntry acco
[HttpPost("deposit")]
public async Task> Deposit(Transaction transaction, [FromServices] DaprClient daprClient)
{
+ // Example reading cloudevent properties from the headers
+ var headerEntries = Request.Headers.Aggregate("", (current, header) => current + ($"------- Header: {header.Key} : {header.Value}" + Environment.NewLine));
+
+ logger.LogInformation(headerEntries);
+
logger.LogInformation("Enter deposit");
var state = await daprClient.GetStateEntryAsync(StoreName, transaction.Id);
state.Value ??= new Account() { Id = transaction.Id, };
@@ -83,7 +91,7 @@ public async Task> Deposit(Transaction transaction, [FromS
}
state.Value.Balance += transaction.Amount;
- logger.LogInformation("Balance for Id {0} is {1}",state.Value.Id, state.Value.Balance);
+ logger.LogInformation("Balance for Id {0} is {1}", state.Value.Id, state.Value.Balance);
await state.SaveAsync();
return state.Value;
}
@@ -98,22 +106,23 @@ public async Task> Deposit(Transaction transaction, [FromS
[Topic("pubsub", "multideposit", "amountDeadLetterTopic", false)]
[BulkSubscribe("multideposit", 500, 2000)]
[HttpPost("multideposit")]
- public async Task> MultiDeposit([FromBody] BulkSubscribeMessage>
- bulkMessage, [FromServices] DaprClient daprClient)
+ public async Task> MultiDeposit([FromBody]
+ BulkSubscribeMessage>
+ bulkMessage, [FromServices] DaprClient daprClient)
{
logger.LogInformation("Enter bulk deposit");
-
+
List entries = new List();
foreach (var entry in bulkMessage.Entries)
- {
+ {
try
{
var transaction = entry.Event.Data;
var state = await daprClient.GetStateEntryAsync(StoreName, transaction.Id);
state.Value ??= new Account() { Id = transaction.Id, };
- logger.LogInformation("Id is {0}, the amount to be deposited is {1}",
+ logger.LogInformation("Id is {0}, the amount to be deposited is {1}",
transaction.Id, transaction.Amount);
if (transaction.Amount < 0m)
@@ -124,12 +133,16 @@ public async Task> MultiDeposit([FromBody
state.Value.Balance += transaction.Amount;
logger.LogInformation("Balance is {0}", state.Value.Balance);
await state.SaveAsync();
- entries.Add(new BulkSubscribeAppResponseEntry(entry.EntryId, BulkSubscribeAppResponseStatus.SUCCESS));
- } catch (Exception e) {
+ entries.Add(
+ new BulkSubscribeAppResponseEntry(entry.EntryId, BulkSubscribeAppResponseStatus.SUCCESS));
+ }
+ catch (Exception e)
+ {
logger.LogError(e.Message);
entries.Add(new BulkSubscribeAppResponseEntry(entry.EntryId, BulkSubscribeAppResponseStatus.RETRY));
}
}
+
return new BulkSubscribeAppResponse(entries);
}
@@ -165,6 +178,7 @@ public async Task> Withdraw(Transaction transaction, [From
{
return this.NotFound();
}
+
if (transaction.Amount < 0m)
{
return BadRequest(new { statusCode = 400, message = "bad request" });
@@ -185,7 +199,8 @@ public async Task> Withdraw(Transaction transaction, [From
/// "pubsub", the first parameter into the Topic attribute, is name of the default pub/sub configured by the Dapr CLI.
[Topic("pubsub", "withdraw", "event.type ==\"withdraw.v2\"", 1)]
[HttpPost("withdraw.v2")]
- public async Task> WithdrawV2(TransactionV2 transaction, [FromServices] DaprClient daprClient)
+ public async Task> WithdrawV2(TransactionV2 transaction,
+ [FromServices] DaprClient daprClient)
{
logger.LogInformation("Enter withdraw.v2");
if (transaction.Channel == "mobile" && transaction.Amount > 10000)
@@ -214,12 +229,15 @@ public async Task> WithdrawV2(TransactionV2 transaction, [
/// "pubsub", the first parameter into the Topic attribute, is name of the default pub/sub configured by the Dapr CLI.
[Topic("pubsub", "rawDeposit", true)]
[HttpPost("rawDeposit")]
- public async Task> RawDeposit([FromBody] JsonDocument rawTransaction, [FromServices] DaprClient daprClient)
+ public async Task> RawDeposit([FromBody] JsonDocument rawTransaction,
+ [FromServices] DaprClient daprClient)
{
var transactionString = rawTransaction.RootElement.GetProperty("data_base64").GetString();
- logger.LogInformation($"Enter deposit: {transactionString} - {Encoding.UTF8.GetString(Convert.FromBase64String(transactionString))}");
+ logger.LogInformation(
+ $"Enter deposit: {transactionString} - {Encoding.UTF8.GetString(Convert.FromBase64String(transactionString))}");
var transactionJson = JsonSerializer.Deserialize(Convert.FromBase64String(transactionString));
- var transaction = JsonSerializer.Deserialize(transactionJson.RootElement.GetProperty("data").GetRawText());
+ var transaction =
+ JsonSerializer.Deserialize(transactionJson.RootElement.GetProperty("data").GetRawText());
var state = await daprClient.GetStateEntryAsync(StoreName, transaction.Id);
state.Value ??= new Account() { Id = transaction.Id, };
logger.LogInformation("Id is {0}, the amount to be deposited is {1}", transaction.Id, transaction.Amount);
@@ -239,7 +257,8 @@ public async Task> RawDeposit([FromBody] JsonDocument rawT
/// Method for returning a BadRequest result which will cause Dapr sidecar to throw an RpcException
///
[HttpPost("throwException")]
- public async Task> ThrowException(Transaction transaction, [FromServices] DaprClient daprClient)
+ public async Task> ThrowException(Transaction transaction,
+ [FromServices] DaprClient daprClient)
{
logger.LogInformation("Enter ThrowException");
var task = Task.Delay(10);
diff --git a/examples/AspNetCore/ControllerSample/CustomTopicAttribute.cs b/examples/AspNetCore/ControllerSample/CustomTopicAttribute.cs
index 96eb918fb..5c9996aea 100644
--- a/examples/AspNetCore/ControllerSample/CustomTopicAttribute.cs
+++ b/examples/AspNetCore/ControllerSample/CustomTopicAttribute.cs
@@ -11,6 +11,8 @@
// limitations under the License.
// ------------------------------------------------------------------------
+using Dapr.AspNetCore;
+
namespace ControllerSample
{
using System;
diff --git a/examples/AspNetCore/ControllerSample/README.md b/examples/AspNetCore/ControllerSample/README.md
index a2700e94d..3b2ca02b9 100644
--- a/examples/AspNetCore/ControllerSample/README.md
+++ b/examples/AspNetCore/ControllerSample/README.md
@@ -12,7 +12,7 @@ The application also registers for pub/sub with the `deposit`, `multideposit` an
## Prerequisitess
-- [.NET Core 3.1 or .NET 5+](https://dotnet.microsoft.com/download) installed
+- [.NET 6+](https://dotnet.microsoft.com/download) installed
- [Dapr CLI](https://docs.dapr.io/getting-started/install-dapr-cli/)
- [Initialized Dapr environment](https://docs.dapr.io/getting-started/install-dapr-selfhost/)
- [Dapr .NET SDK](https://docs.dapr.io/developing-applications/sdks/dotnet/)
diff --git a/examples/AspNetCore/ControllerSample/Startup.cs b/examples/AspNetCore/ControllerSample/Startup.cs
index 11b81d8b3..ddc6d1c5f 100644
--- a/examples/AspNetCore/ControllerSample/Startup.cs
+++ b/examples/AspNetCore/ControllerSample/Startup.cs
@@ -11,6 +11,11 @@
// limitations under the License.
// ------------------------------------------------------------------------
+
+using Dapr;
+using Dapr.AspNetCore;
+
+
namespace ControllerSample
{
using Microsoft.AspNetCore.Builder;
@@ -61,7 +66,10 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
app.UseRouting();
- app.UseCloudEvents();
+ app.UseCloudEvents(new CloudEventsMiddlewareOptions
+ {
+ ForwardCloudEventPropertiesAsHeaders = true
+ });
app.UseAuthorization();
diff --git a/examples/AspNetCore/GrpcServiceSample/GrpcServiceSample.csproj b/examples/AspNetCore/GrpcServiceSample/GrpcServiceSample.csproj
index 23ead7d4d..2319f6a56 100644
--- a/examples/AspNetCore/GrpcServiceSample/GrpcServiceSample.csproj
+++ b/examples/AspNetCore/GrpcServiceSample/GrpcServiceSample.csproj
@@ -1,7 +1,7 @@
- netcoreapp3.1
+ net6true
@@ -10,15 +10,16 @@
-
-
-
-
-
+
+
+
+
+
+
diff --git a/examples/AspNetCore/GrpcServiceSample/README.md b/examples/AspNetCore/GrpcServiceSample/README.md
index 3f27d280c..d08e96cd9 100644
--- a/examples/AspNetCore/GrpcServiceSample/README.md
+++ b/examples/AspNetCore/GrpcServiceSample/README.md
@@ -11,7 +11,7 @@ The application also registers for pub/sub with the `deposit` and `withdraw` top
## Prerequisitess
-- [.NET Core 3.1 or .NET 5+](https://dotnet.microsoft.com/download) installed
+- [.NET 6+](https://dotnet.microsoft.com/download) installed
- [Dapr CLI](https://docs.dapr.io/getting-started/install-dapr-cli/)
- [Initialized Dapr environment](https://docs.dapr.io/getting-started/install-dapr-selfhost/)
- [Dapr .NET SDK](https://docs.dapr.io/developing-applications/sdks/dotnet/)
diff --git a/examples/AspNetCore/GrpcServiceSample/Services/BankingService.cs b/examples/AspNetCore/GrpcServiceSample/Services/BankingService.cs
index 56b80cad6..9518fd610 100644
--- a/examples/AspNetCore/GrpcServiceSample/Services/BankingService.cs
+++ b/examples/AspNetCore/GrpcServiceSample/Services/BankingService.cs
@@ -22,7 +22,7 @@
using GrpcServiceSample.Generated;
using Microsoft.Extensions.Logging;
-namespace GrpcServiceSample
+namespace GrpcServiceSample.Services
{
///
/// BankAccount gRPC service
diff --git a/examples/AspNetCore/GrpcServiceSample/Startup.cs b/examples/AspNetCore/GrpcServiceSample/Startup.cs
index 752d62448..4aa5ac7d3 100644
--- a/examples/AspNetCore/GrpcServiceSample/Startup.cs
+++ b/examples/AspNetCore/GrpcServiceSample/Startup.cs
@@ -11,6 +11,8 @@
// limitations under the License.
// ------------------------------------------------------------------------
+using Dapr.AspNetCore;
+using GrpcServiceSample.Services;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
diff --git a/examples/AspNetCore/RoutingSample/README.md b/examples/AspNetCore/RoutingSample/README.md
index cd545b747..901c51c40 100644
--- a/examples/AspNetCore/RoutingSample/README.md
+++ b/examples/AspNetCore/RoutingSample/README.md
@@ -12,7 +12,7 @@ The application also registers for pub/sub with the `deposit`, `multideposit`, a
## Prerequisites
-- [.NET Core 3.1 or .NET 5+](https://dotnet.microsoft.com/download) installed
+- [.NET 6+](https://dotnet.microsoft.com/download) installed
- [Dapr CLI](https://docs.dapr.io/getting-started/install-dapr-cli/)
- [Initialized Dapr environment](https://docs.dapr.io/getting-started/install-dapr-selfhost/)
- [Dapr .NET SDK](https://docs.dapr.io/developing-applications/sdks/dotnet/)
diff --git a/examples/AspNetCore/SecretStoreConfigurationProviderSample/README.md b/examples/AspNetCore/SecretStoreConfigurationProviderSample/README.md
index 5be9eb5e1..09422e474 100644
--- a/examples/AspNetCore/SecretStoreConfigurationProviderSample/README.md
+++ b/examples/AspNetCore/SecretStoreConfigurationProviderSample/README.md
@@ -2,7 +2,7 @@
## Prerequisites
-- [.NET Core 3.1 or .NET 5+](https://dotnet.microsoft.com/download) installed
+- [.NET 6+](https://dotnet.microsoft.com/download) installed
- [Dapr CLI](https://docs.dapr.io/getting-started/install-dapr-cli/)
- [Initialized Dapr environment](https://docs.dapr.io/getting-started/install-dapr-selfhost/)
- [Dapr .NET SDK](https://docs.dapr.io/developing-applications/sdks/dotnet/)
diff --git a/examples/AspNetCore/SecretStoreConfigurationProviderSample/SecretStoreConfigurationProviderSample.csproj b/examples/AspNetCore/SecretStoreConfigurationProviderSample/SecretStoreConfigurationProviderSample.csproj
index 10928470b..01fbc2079 100644
--- a/examples/AspNetCore/SecretStoreConfigurationProviderSample/SecretStoreConfigurationProviderSample.csproj
+++ b/examples/AspNetCore/SecretStoreConfigurationProviderSample/SecretStoreConfigurationProviderSample.csproj
@@ -2,7 +2,7 @@
Exe
- netcoreapp3.1
+ net6
diff --git a/examples/Client/ConfigurationApi/ConfigurationApi.csproj b/examples/Client/ConfigurationApi/ConfigurationApi.csproj
index f73fd4974..761ebb38f 100644
--- a/examples/Client/ConfigurationApi/ConfigurationApi.csproj
+++ b/examples/Client/ConfigurationApi/ConfigurationApi.csproj
@@ -1,13 +1,14 @@
- netcoreapp3.1
+ net6
+
diff --git a/examples/Client/ConfigurationApi/Controllers/ConfigurationController.cs b/examples/Client/ConfigurationApi/Controllers/ConfigurationController.cs
index 387c3f94f..55bf6df53 100644
--- a/examples/Client/ConfigurationApi/Controllers/ConfigurationController.cs
+++ b/examples/Client/ConfigurationApi/Controllers/ConfigurationController.cs
@@ -13,7 +13,6 @@ namespace ConfigurationApi.Controllers
{
[ApiController]
[Route("configuration")]
- [Obsolete]
public class ConfigurationController : ControllerBase
{
private ILogger logger;
diff --git a/examples/Client/ConfigurationApi/Program.cs b/examples/Client/ConfigurationApi/Program.cs
index 12328ebdb..f5218602d 100644
--- a/examples/Client/ConfigurationApi/Program.cs
+++ b/examples/Client/ConfigurationApi/Program.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
using Dapr.Client;
@@ -10,7 +10,6 @@ namespace ConfigurationApi
{
public class Program
{
- [Obsolete]
public static void Main(string[] args)
{
Console.WriteLine("Starting application.");
diff --git a/examples/Client/ConfigurationApi/README.md b/examples/Client/ConfigurationApi/README.md
index 41c1c831f..7425a780a 100644
--- a/examples/Client/ConfigurationApi/README.md
+++ b/examples/Client/ConfigurationApi/README.md
@@ -9,7 +9,7 @@ It demonstrates the following APIs:
## Prerequisites
-- [.NET Core 3.1 or .NET 5+](https://dotnet.microsoft.com/download) installed
+- [.NET 6+](https://dotnet.microsoft.com/download) installed
- [Dapr CLI](https://docs.dapr.io/getting-started/install-dapr-cli/)
- [Initialized Dapr environment](https://docs.dapr.io/getting-started/install-dapr-selfhost/)
- [Dapr .NET SDK](https://docs.dapr.io/developing-applications/sdks/dotnet/)
diff --git a/examples/Client/ConfigurationApi/Startup.cs b/examples/Client/ConfigurationApi/Startup.cs
index 62a77ac49..db5b921c9 100644
--- a/examples/Client/ConfigurationApi/Startup.cs
+++ b/examples/Client/ConfigurationApi/Startup.cs
@@ -1,4 +1,5 @@
using System;
+using Dapr.AspNetCore;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
diff --git a/examples/Client/Cryptography/Components/azurekeyvault.yaml b/examples/Client/Cryptography/Components/azurekeyvault.yaml
new file mode 100644
index 000000000..5932e0bc8
--- /dev/null
+++ b/examples/Client/Cryptography/Components/azurekeyvault.yaml
@@ -0,0 +1,25 @@
+apiVersion: dapr.io/v1alpha1
+kind: Component
+metadata:
+ name: azurekeyvault
+spec:
+ type: crypto.azure.keyvault
+ metadata:
+ - name: vaultName
+ value: ""
+ - name: azureEnvironment
+ value: AZUREPUBLICCLOUD
+ - name: azureTenantId
+ secretKeyRef:
+ name: read_azure_tenant_id
+ key: read_azure_tenant_id
+ - name: azureClientId
+ secretKeyRef:
+ name: read_azure_client_id
+ key: read_azure_client_id
+ - name: azureClientSecret
+ secretKeyRef:
+ name: read_azure_client_secret
+ key: read_azure_client_secret
+auth:
+ secureStore: envvar-secret-store
\ No newline at end of file
diff --git a/examples/Client/Cryptography/Components/env-secretstore.yaml b/examples/Client/Cryptography/Components/env-secretstore.yaml
new file mode 100644
index 000000000..fb191414d
--- /dev/null
+++ b/examples/Client/Cryptography/Components/env-secretstore.yaml
@@ -0,0 +1,7 @@
+apiVersion: dapr.io/v1alpha1
+kind: Component
+metadata:
+ name: envvar-secret-store
+spec:
+ type: secretstores.local.env
+ version: v1
\ No newline at end of file
diff --git a/examples/Client/Cryptography/Cryptography.csproj b/examples/Client/Cryptography/Cryptography.csproj
new file mode 100644
index 000000000..525c38562
--- /dev/null
+++ b/examples/Client/Cryptography/Cryptography.csproj
@@ -0,0 +1,25 @@
+
+
+
+ Exe
+ net6.0
+ enable
+ enable
+ latest
+
+
+
+
+
+
+
+
+
+
+
+
+ PreserveNewest
+
+
+
+
\ No newline at end of file
diff --git a/examples/Client/Cryptography/Example.cs b/examples/Client/Cryptography/Example.cs
new file mode 100644
index 000000000..2c2d41626
--- /dev/null
+++ b/examples/Client/Cryptography/Example.cs
@@ -0,0 +1,22 @@
+// ------------------------------------------------------------------------
+// Copyright 2023 The Dapr Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ------------------------------------------------------------------------
+
+namespace Cryptography
+{
+ internal abstract class Example
+ {
+ public abstract string DisplayName { get; }
+
+ public abstract Task RunAsync(CancellationToken cancellationToken);
+ }
+}
diff --git a/examples/Client/Cryptography/Examples/EncryptDecryptFileStreamExample.cs b/examples/Client/Cryptography/Examples/EncryptDecryptFileStreamExample.cs
new file mode 100644
index 000000000..aa9c404a7
--- /dev/null
+++ b/examples/Client/Cryptography/Examples/EncryptDecryptFileStreamExample.cs
@@ -0,0 +1,77 @@
+// ------------------------------------------------------------------------
+// Copyright 2023 The Dapr Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ------------------------------------------------------------------------
+
+using System.Buffers;
+using Dapr.Client;
+#pragma warning disable CS0618 // Type or member is obsolete
+
+namespace Cryptography.Examples
+{
+ internal class EncryptDecryptFileStreamExample : Example
+ {
+ public override string DisplayName => "Use Cryptography to encrypt and decrypt a file";
+ public override async Task RunAsync(CancellationToken cancellationToken)
+ {
+ using var client = new DaprClientBuilder().Build();
+
+ const string componentName = "azurekeyvault"; // Change this to match the name of the component containing your vault
+ const string keyName = "myKey";
+
+ // The name of the file we're using as an example
+ const string fileName = "file.txt";
+
+ Console.WriteLine("Original file contents:");
+ foreach (var line in await File.ReadAllLinesAsync(fileName, cancellationToken))
+ {
+ Console.WriteLine(line);
+ }
+ Console.WriteLine();
+
+ //Encrypt from a file stream and buffer the resulting bytes to an in-memory buffer
+ await using var encryptFs = new FileStream(fileName, FileMode.Open);
+
+ var bufferedEncryptedBytes = new ArrayBufferWriter();
+ await foreach (var bytes in (await client.EncryptAsync(componentName, encryptFs, keyName,
+ new EncryptionOptions(KeyWrapAlgorithm.Rsa), cancellationToken))
+ .WithCancellation(cancellationToken))
+ {
+ bufferedEncryptedBytes.Write(bytes.Span);
+ }
+
+ Console.WriteLine($"Encrypted bytes: {Convert.ToBase64String(bufferedEncryptedBytes.GetSpan())}");
+ Console.WriteLine();
+
+ //We'll write to a temporary file via a FileStream
+ var tempDecryptedFile = Path.GetTempFileName();
+ await using var decryptFs = new FileStream(tempDecryptedFile, FileMode.Create);
+
+ //We'll stream the decrypted bytes from a MemoryStream into the above temporary file
+ await using var encryptedMs = new MemoryStream(bufferedEncryptedBytes.WrittenMemory.ToArray());
+ await foreach (var result in (await client.DecryptAsync(componentName, encryptedMs, keyName,
+ cancellationToken)).WithCancellation(cancellationToken))
+ {
+ decryptFs.Write(result.Span);
+ }
+
+ decryptFs.Close();
+
+ //Let's confirm the value as written to the file
+ var decryptedValue = await File.ReadAllTextAsync(tempDecryptedFile, cancellationToken);
+ Console.WriteLine($"Decrypted value: ");
+ Console.WriteLine(decryptedValue);
+
+ //And some cleanup to delete our temp file
+ File.Delete(tempDecryptedFile);
+ }
+ }
+}
diff --git a/examples/Client/Cryptography/Examples/EncryptDecryptStringExample.cs b/examples/Client/Cryptography/Examples/EncryptDecryptStringExample.cs
new file mode 100644
index 000000000..a37ca1b8b
--- /dev/null
+++ b/examples/Client/Cryptography/Examples/EncryptDecryptStringExample.cs
@@ -0,0 +1,47 @@
+// ------------------------------------------------------------------------
+// Copyright 2023 The Dapr Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ------------------------------------------------------------------------
+
+using System.Text;
+using Dapr.Client;
+#pragma warning disable CS0618 // Type or member is obsolete
+
+namespace Cryptography.Examples
+{
+ internal class EncryptDecryptStringExample : Example
+ {
+ public override string DisplayName => "Using Cryptography to encrypt and decrypt a string";
+
+ public override async Task RunAsync(CancellationToken cancellationToken)
+ {
+ using var client = new DaprClientBuilder().Build();
+
+ const string componentName = "azurekeyvault"; //Change this to match the name of the component containing your vault
+ const string keyName = "myKey"; //Change this to match the name of the key in your Vault
+
+
+ const string plaintextStr = "This is the value we're going to encrypt today";
+ Console.WriteLine($"Original string value: '{plaintextStr}'");
+
+ //Encrypt the string
+ var plaintextBytes = Encoding.UTF8.GetBytes(plaintextStr);
+ var encryptedBytesResult = await client.EncryptAsync(componentName, plaintextBytes, keyName, new EncryptionOptions(KeyWrapAlgorithm.Rsa),
+ cancellationToken);
+
+ Console.WriteLine($"Encrypted bytes: '{Convert.ToBase64String(encryptedBytesResult.Span)}'");
+
+ //Decrypt the string
+ var decryptedBytes = await client.DecryptAsync(componentName, encryptedBytesResult, keyName, new DecryptionOptions(), cancellationToken);
+ Console.WriteLine($"Decrypted string: '{Encoding.UTF8.GetString(decryptedBytes.ToArray())}'");
+ }
+ }
+}
diff --git a/examples/Client/Cryptography/Program.cs b/examples/Client/Cryptography/Program.cs
new file mode 100644
index 000000000..da81bef8f
--- /dev/null
+++ b/examples/Client/Cryptography/Program.cs
@@ -0,0 +1,46 @@
+// ------------------------------------------------------------------------
+// Copyright 2023 The Dapr Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ------------------------------------------------------------------------
+
+using Cryptography.Examples;
+
+namespace Cryptography
+{
+ class Program
+ {
+ private static readonly Example[] Examples = new Example[]
+ {
+ new EncryptDecryptStringExample(),
+ new EncryptDecryptFileStreamExample()
+ };
+
+ static async Task Main(string[] args)
+ {
+ if (args.Length > 0 && int.TryParse(args[0], out var index) && index >= 0 && index < Examples.Length)
+ {
+ var cts = new CancellationTokenSource();
+ Console.CancelKeyPress += (object? sender, ConsoleCancelEventArgs e) => cts.Cancel();
+
+ await Examples[index].RunAsync(cts.Token);
+ return 0;
+ }
+
+ Console.WriteLine("Hello, please choose a sample to run:");
+ for (var i = 0; i < Examples.Length; i++)
+ {
+ Console.WriteLine($"{i}: {Examples[i].DisplayName}");
+ }
+ Console.WriteLine();
+ return 1;
+ }
+ }
+}
diff --git a/examples/Client/Cryptography/README.md b/examples/Client/Cryptography/README.md
new file mode 100644
index 000000000..c0c884369
--- /dev/null
+++ b/examples/Client/Cryptography/README.md
@@ -0,0 +1,92 @@
+# Dapr .NET SDK Cryptography example
+
+## Prerequisites
+
+- [.NET 8+](https://dotnet.microsoft.com/download) installed
+- [Dapr CLI](https://docs.dapr.io/getting-started/install-dapr-cli)
+- [Initialized Dapr environment](https://docs.dapr.io/getting-started/installation)
+- [Dapr .NET SDK](https://docs.dapr.io/developing-applications/sdks/dotnet/)
+- [Azure Key Vault instance](https://learn.microsoft.com/en-us/azure/key-vault/general/quick-create-portal)
+- [Entra Service Principal](https://learn.microsoft.com/en-us/entra/identity-platform/quickstart-register-app)
+
+### Service Principal/Environment Variables Setup
+In your Azure portal, open Microsoft Entra ID and click `App Registrations`. Click the button at the top to create a new registration. Select a name for your service principal
+and click register, noting this name for later.
+
+Once the registration is completed, open it from the list and select Certificates & Secrets from the left navigation. Select "Client secrets" from the page body (middle column)
+and click the button to add a new client secret giving it an optional description and changing the expiry date as you desire. Click Add to create the secret. Record the secret
+value it shows you - it will not be shown to you again without creating another client secret.
+
+Click Overview from the left navigation and record the "Application (client) ID" and the "Directory (tenant) ID" values.
+
+On your computer (assuming Windows), open your start menu and type "Environment Variables". An option should appear named "Edit the system environment variables". Select this
+and your System Properties window will open. Click the "Environment Variables" button in the bottom and said window will appear. Click the "New..." button under System variables
+to add the requisite service principal values to your environment variables. You can change these names as to want by updating the `./Components/azurekeyvault.yaml` names, but for now
+configure as follows:
+
+| Variable Name | Value |
+|--|--|
+| read_azure_client_id | Paste the value from your app registration overview for "Application (client) ID" |
+| read_azure_client_secret | Paste the value of the client secret you generated for your app registration |
+| read_azure_tenant_id | Paste the valeu from your app registration overview for "Directory (tenant) ID" |
+
+Click OK to save your environment variables and to close your System Properties window. You may need to close restart your command line tool for it to recognize the new values.
+
+### Azure Key Vault Setup
+
+This example is implemented using the Azure Key Vault and will not work without it. Assuming you have a Key Vault instance configured, ensure that
+you have the `Key Vault Crypto Officer` role assigned to yourself as you'll need to in order to generate a new key in the instance. After selecting Keys
+under the Objects header, click the `Generate/Import` button at the top of the instance panel.
+
+Under options, select `Generate` and name your key. This example is pre-configured to assume a key name of 'myKey', but feel free to change this (but also update the name in the example
+you wish to run). The other default options are fine for our purposes, so click Create at the bottom and if you've got the appropriate roles, it will show up in the list of Keys.
+
+Update your `./Components/azurekeyvault.yaml` file with the name of your Key Vault under `vaultName` where it currently reads "changeMe". This sample assumes authentication
+via a service principal, so you might also need to set this up.
+
+Back in the Azure Portal, assign at least the `Key Vault Crypto User` role to the service principal you previously created in the last step. Do this by clicking
+`Access Control (IAM)` from the left navigation, clicking "Add" from the top and clicking "Add Role Assignment". Select `Key Vault Crypto User` from the list and click the Next
+button. Ensuring that the "User, group or service principal" option is selected, click the "Select members" link and search for the name of the app registration you created. Click
+Add to add this service principal to the list of members for the new role assignment and click Review + Assign twice to assign the role. This will take effect within a few seconds
+or minutes. This step ensures that while Dapr can authenticate as your service principal, that it also has permission to access and use the key in your Key Vault.
+
+## Running the example
+
+To run the sample locally, run this command in the DaprClient directory:
+
+```sh
+dapr run --resources-path ./Components --app-id DaprClient -- dotnet run
+```
+
+Running the following command will output a list of the samples included:
+
+```sh
+dapr run --resources-path ./Components --app-id DaprClient -- dotnet run
+```
+
+Press Ctrl+C to exit, and then run the command again and provide a sample number to run the samples.
+
+For example, run this command to run the first sample from the list produced earlier (the 0th example):
+
+```sh
+dapr run --resources-path ./Components --app-id DaprClient -- dotnet run 0
+```
+
+## Encryption/Decryption with strings
+See [EncryptDecryptStringExample.cs](./EncryptDecryptStringExample.cs) for an example of using `DaprClient` for basic
+string-based encryption and decryption operations as performed against UTF-8 encoded byte arrays.
+
+## Encryption/Decryption with streams
+See [EncryptDecryptFileStreamExample.cs](./EncryptDecryptFileStreamExample.cs) for an example of using `DaprClient`
+to perform an encrypt and decrypt operation against a stream of data. In the example, we stream a local file to the
+sidecar to encrypt and write the result (as it's streamed back) to an in-memory buffer. Once the operation fully
+completes, we perform the decrypt operation against this in-memory buffer and write the decrypted result back out to a
+temporary file.
+
+In either operation, rather than load the entire stream into memory and send all at once to the
+sidecar as we do in the other string-based example (as this might cause you to run out of memory either on the
+node the app is running on or do the same to the sidecar itself), this example instead breaks the input stream into
+more manageable 4KB chunks (a value you can override via the `EncryptionOptions` or `DecryptionOptions` parameters
+respectively up to 64KB. Further, rather than waiting for the entire stream to send to the sidecar before the
+encryption operation proceeds, it immediately works to process the sidecar response, continuing to minimize resource
+usage.
diff --git a/examples/Client/Cryptography/file.txt b/examples/Client/Cryptography/file.txt
new file mode 100644
index 000000000..9e8638939
--- /dev/null
+++ b/examples/Client/Cryptography/file.txt
@@ -0,0 +1,26 @@
+# The Road Not Taken
+## By Robert Lee Frost
+
+Two roads diverged in a yellow wood,
+And sorry I could not travel both
+And be one traveler, long I stood
+And looked down one as far as I could
+To where it bent in the undergrowth;
+
+Then took the other, as just as fair
+And having perhaps the better claim,
+Because it was grassy and wanted wear;
+Though as for that, the passing there
+Had worn them really about the same,
+
+And both that morning equally lay
+In leaves no step had trodden black
+Oh, I kept the first for another day!
+Yet knowing how way leads on to way,
+I doubted if I should ever come back.
+
+I shall be telling this with a sigh
+Somewhere ages and ages hence:
+Two roads diverged in a wood, and I,
+I took the one less traveled by,
+And that has made all the difference.
\ No newline at end of file
diff --git a/examples/Client/DistributedLock/DistributedLock.csproj b/examples/Client/DistributedLock/DistributedLock.csproj
index 180c46f28..4c04fb907 100644
--- a/examples/Client/DistributedLock/DistributedLock.csproj
+++ b/examples/Client/DistributedLock/DistributedLock.csproj
@@ -3,10 +3,11 @@
+
- netcoreapp3.1
+ net6
diff --git a/examples/Client/DistributedLock/README.md b/examples/Client/DistributedLock/README.md
index 788d57e3a..cdac6f91a 100644
--- a/examples/Client/DistributedLock/README.md
+++ b/examples/Client/DistributedLock/README.md
@@ -2,7 +2,7 @@
## Prerequisites
-- [.NET Core 3.1 or .NET 5+](https://dotnet.microsoft.com/download) installed
+- [.NET 6+](https://dotnet.microsoft.com/download) installed
- [Dapr CLI](https://docs.dapr.io/getting-started/install-dapr-cli/)
- [Initialized Dapr environment](https://docs.dapr.io/getting-started/install-dapr-selfhost/)
- [Dapr .NET SDK](https://docs.dapr.io/developing-applications/sdks/dotnet/)
diff --git a/examples/Client/DistributedLock/Startup.cs b/examples/Client/DistributedLock/Startup.cs
index 0309af0f5..9f40e4752 100644
--- a/examples/Client/DistributedLock/Startup.cs
+++ b/examples/Client/DistributedLock/Startup.cs
@@ -1,4 +1,5 @@
-using DistributedLock.Services;
+using Dapr.AspNetCore;
+using DistributedLock.Services;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
diff --git a/examples/Client/PublishSubscribe/BulkPublishEventExample/BulkPublishEventExample.csproj b/examples/Client/PublishSubscribe/BulkPublishEventExample/BulkPublishEventExample.csproj
index 5c05faf9f..b1e7647c7 100644
--- a/examples/Client/PublishSubscribe/BulkPublishEventExample/BulkPublishEventExample.csproj
+++ b/examples/Client/PublishSubscribe/BulkPublishEventExample/BulkPublishEventExample.csproj
@@ -2,7 +2,7 @@
Exe
- netcoreapp3.1
+ net6Samples.Clientenable
@@ -16,9 +16,9 @@
-
-
-
+
+
+
diff --git a/examples/Client/PublishSubscribe/BulkPublishEventExample/README.md b/examples/Client/PublishSubscribe/BulkPublishEventExample/README.md
index dfcc99ca6..39d206fa2 100644
--- a/examples/Client/PublishSubscribe/BulkPublishEventExample/README.md
+++ b/examples/Client/PublishSubscribe/BulkPublishEventExample/README.md
@@ -2,7 +2,7 @@
## Prerequisites
-- [.NET Core 3.1 or .NET 5+](https://dotnet.microsoft.com/download) installed
+- [.NET 6+](https://dotnet.microsoft.com/download) installed
- [Dapr CLI](https://docs.dapr.io/getting-started/install-dapr-cli/)
- [Initialized Dapr environment](https://docs.dapr.io/getting-started/install-dapr-selfhost/)
- [Dapr .NET SDK](https://docs.dapr.io/developing-applications/sdks/dotnet/)
diff --git a/examples/Client/PublishSubscribe/PublishEventExample/PublishEventExample.csproj b/examples/Client/PublishSubscribe/PublishEventExample/PublishEventExample.csproj
index f7d335879..52b77a3e5 100644
--- a/examples/Client/PublishSubscribe/PublishEventExample/PublishEventExample.csproj
+++ b/examples/Client/PublishSubscribe/PublishEventExample/PublishEventExample.csproj
@@ -2,7 +2,7 @@
Exe
- netcoreapp3.1
+ net6Samples.Clientenable
@@ -16,9 +16,9 @@
-
-
-
+
+
+
diff --git a/examples/Client/PublishSubscribe/PublishEventExample/README.md b/examples/Client/PublishSubscribe/PublishEventExample/README.md
index 455fc2537..9f3af565f 100644
--- a/examples/Client/PublishSubscribe/PublishEventExample/README.md
+++ b/examples/Client/PublishSubscribe/PublishEventExample/README.md
@@ -2,7 +2,7 @@
## Prerequisites
-- [.NET Core 3.1 or .NET 5+](https://dotnet.microsoft.com/download) installed
+- [.NET 6+](https://dotnet.microsoft.com/download) installed
- [Dapr CLI](https://docs.dapr.io/getting-started/install-dapr-cli/)
- [Initialized Dapr environment](https://docs.dapr.io/getting-started/install-dapr-selfhost/)
- [Dapr .NET SDK](https://docs.dapr.io/developing-applications/sdks/dotnet/)
diff --git a/examples/Client/ServiceInvocation/README.md b/examples/Client/ServiceInvocation/README.md
index 171869c15..ede5a506a 100644
--- a/examples/Client/ServiceInvocation/README.md
+++ b/examples/Client/ServiceInvocation/README.md
@@ -2,7 +2,7 @@
## Prerequisites
-- [.NET Core 3.1 or .NET 5+](https://dotnet.microsoft.com/download) installed
+- [.NET 6+](https://dotnet.microsoft.com/download) installed
- [Dapr CLI](https://docs.dapr.io/getting-started/install-dapr-cli/)
- [Initialized Dapr environment](https://docs.dapr.io/getting-started/install-dapr-selfhost/)
- [Dapr .NET SDK](https://docs.dapr.io/developing-applications/sdks/dotnet/)
diff --git a/examples/Client/ServiceInvocation/ServiceInvocation.csproj b/examples/Client/ServiceInvocation/ServiceInvocation.csproj
index 790bfc53a..7b165835e 100644
--- a/examples/Client/ServiceInvocation/ServiceInvocation.csproj
+++ b/examples/Client/ServiceInvocation/ServiceInvocation.csproj
@@ -2,7 +2,7 @@
Exe
- netcoreapp3.1
+ net6Samples.Clientenable
@@ -16,9 +16,9 @@
-
-
-
+
+
+
diff --git a/examples/Client/StateManagement/README.md b/examples/Client/StateManagement/README.md
index 141f16760..fb266a242 100644
--- a/examples/Client/StateManagement/README.md
+++ b/examples/Client/StateManagement/README.md
@@ -2,7 +2,7 @@
## Prerequisites
-- [.NET Core 3.1 or .NET 5+](https://dotnet.microsoft.com/download) installed
+- [.NET 6+](https://dotnet.microsoft.com/download) installed
- [Dapr CLI](https://docs.dapr.io/getting-started/install-dapr-cli/)
- [Initialized Dapr environment](https://docs.dapr.io/getting-started/install-dapr-selfhost/)
- [Dapr .NET SDK](https://docs.dapr.io/developing-applications/sdks/dotnet/)
diff --git a/examples/Client/StateManagement/StateManagement.csproj b/examples/Client/StateManagement/StateManagement.csproj
index 790bfc53a..7b165835e 100644
--- a/examples/Client/StateManagement/StateManagement.csproj
+++ b/examples/Client/StateManagement/StateManagement.csproj
@@ -2,7 +2,7 @@
Exe
- netcoreapp3.1
+ net6Samples.Clientenable
@@ -16,9 +16,9 @@
-
-
-
+
+
+
diff --git a/examples/GeneratedActor/ActorClient/ActorClient.csproj b/examples/GeneratedActor/ActorClient/ActorClient.csproj
new file mode 100644
index 000000000..73b5c2027
--- /dev/null
+++ b/examples/GeneratedActor/ActorClient/ActorClient.csproj
@@ -0,0 +1,22 @@
+
+
+
+ Exe
+ net6
+ 10.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/GeneratedActor/ActorClient/IClientActor.cs b/examples/GeneratedActor/ActorClient/IClientActor.cs
new file mode 100644
index 000000000..c5c732cb9
--- /dev/null
+++ b/examples/GeneratedActor/ActorClient/IClientActor.cs
@@ -0,0 +1,28 @@
+// ------------------------------------------------------------------------
+// Copyright 2023 The Dapr Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ------------------------------------------------------------------------
+
+using Dapr.Actors.Generators;
+
+namespace GeneratedActor;
+
+internal sealed record ClientState(string Value);
+
+[GenerateActorClient]
+internal interface IClientActor
+{
+ [ActorMethod(Name = "GetState")]
+ Task GetStateAsync(CancellationToken cancellationToken = default);
+
+ [ActorMethod(Name = "SetState")]
+ Task SetStateAsync(ClientState state, CancellationToken cancellationToken = default);
+}
diff --git a/examples/GeneratedActor/ActorClient/Program.cs b/examples/GeneratedActor/ActorClient/Program.cs
new file mode 100644
index 000000000..87f714907
--- /dev/null
+++ b/examples/GeneratedActor/ActorClient/Program.cs
@@ -0,0 +1,30 @@
+// ------------------------------------------------------------------------
+// Copyright 2023 The Dapr Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ------------------------------------------------------------------------
+
+using Dapr.Actors;
+using Dapr.Actors.Client;
+using GeneratedActor;
+
+Console.WriteLine("Testing generated client...");
+
+var proxy = ActorProxy.Create(ActorId.CreateRandom(), "RemoteActor");
+
+using var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(30));
+
+var client = new ClientActorClient(proxy);
+
+var state = await client.GetStateAsync(cancellationTokenSource.Token);
+
+await client.SetStateAsync(new ClientState("Hello, World!"), cancellationTokenSource.Token);
+
+Console.WriteLine("Done!");
diff --git a/examples/GeneratedActor/ActorCommon/ActorCommon.csproj b/examples/GeneratedActor/ActorCommon/ActorCommon.csproj
new file mode 100644
index 000000000..2cbc61e2c
--- /dev/null
+++ b/examples/GeneratedActor/ActorCommon/ActorCommon.csproj
@@ -0,0 +1,14 @@
+
+
+
+ net6
+ 10.0
+ enable
+ enable
+
+
+
+
+
+
+
diff --git a/examples/GeneratedActor/ActorCommon/IRemoteActor.cs b/examples/GeneratedActor/ActorCommon/IRemoteActor.cs
new file mode 100644
index 000000000..6d136a704
--- /dev/null
+++ b/examples/GeneratedActor/ActorCommon/IRemoteActor.cs
@@ -0,0 +1,25 @@
+// ------------------------------------------------------------------------
+// Copyright 2023 The Dapr Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ------------------------------------------------------------------------
+
+using Dapr.Actors;
+
+namespace GeneratedActor;
+
+public sealed record RemoteState(string Value);
+
+public interface IRemoteActor : IActor
+{
+ Task GetState();
+
+ Task SetState(RemoteState state);
+}
diff --git a/examples/GeneratedActor/ActorService/ActorService.csproj b/examples/GeneratedActor/ActorService/ActorService.csproj
new file mode 100644
index 000000000..a74104363
--- /dev/null
+++ b/examples/GeneratedActor/ActorService/ActorService.csproj
@@ -0,0 +1,15 @@
+
+
+
+ net6
+ 10.0
+ enable
+ enable
+
+
+
+
+
+
+
+
diff --git a/examples/GeneratedActor/ActorService/Program.cs b/examples/GeneratedActor/ActorService/Program.cs
new file mode 100644
index 000000000..f6e62f720
--- /dev/null
+++ b/examples/GeneratedActor/ActorService/Program.cs
@@ -0,0 +1,36 @@
+// ------------------------------------------------------------------------
+// Copyright 2023 The Dapr Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ------------------------------------------------------------------------
+
+using GeneratedActor;
+
+var builder = WebApplication.CreateBuilder(args);
+
+builder.Services.AddActors(
+ options =>
+ {
+ options.UseJsonSerialization = true;
+ options.Actors.RegisterActor();
+ });
+
+var app = builder.Build();
+
+app.UseRouting();
+
+#pragma warning disable ASP0014
+app.UseEndpoints(
+ endpoints =>
+ {
+ endpoints.MapActorsHandlers();
+ });
+
+app.Run();
diff --git a/examples/GeneratedActor/ActorService/Properties/launchSettings.json b/examples/GeneratedActor/ActorService/Properties/launchSettings.json
new file mode 100644
index 000000000..8fbb1f581
--- /dev/null
+++ b/examples/GeneratedActor/ActorService/Properties/launchSettings.json
@@ -0,0 +1,31 @@
+{
+ "$schema": "https://json.schemastore.org/launchsettings.json",
+ "iisSettings": {
+ "windowsAuthentication": false,
+ "anonymousAuthentication": true,
+ "iisExpress": {
+ "applicationUrl": "http://localhost:56372",
+ "sslPort": 0
+ }
+ },
+ "profiles": {
+ "http": {
+ "commandName": "Project",
+ "dotnetRunMessages": true,
+ "launchBrowser": true,
+ "launchUrl": "swagger",
+ "applicationUrl": "http://localhost:5226",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ },
+ "IIS Express": {
+ "commandName": "IISExpress",
+ "launchBrowser": true,
+ "launchUrl": "swagger",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ }
+ }
+}
diff --git a/examples/GeneratedActor/ActorService/RemoteActor.cs b/examples/GeneratedActor/ActorService/RemoteActor.cs
new file mode 100644
index 000000000..f04921f69
--- /dev/null
+++ b/examples/GeneratedActor/ActorService/RemoteActor.cs
@@ -0,0 +1,45 @@
+// ------------------------------------------------------------------------
+// Copyright 2023 The Dapr Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ------------------------------------------------------------------------
+
+using Dapr.Actors.Runtime;
+
+namespace GeneratedActor;
+
+internal sealed class RemoteActor : Actor, IRemoteActor
+{
+ private readonly ILogger logger;
+
+ private RemoteState currentState = new("default");
+
+ public RemoteActor(ActorHost host, ILogger logger)
+ : base(host)
+ {
+ this.logger = logger;
+ }
+
+ public Task GetState()
+ {
+ this.logger.LogInformation("GetStateAsync called.");
+
+ return Task.FromResult(this.currentState);
+ }
+
+ public Task SetState(RemoteState state)
+ {
+ this.logger.LogInformation("SetStateAsync called.");
+
+ this.currentState = state;
+
+ return Task.CompletedTask;
+ }
+}
\ No newline at end of file
diff --git a/examples/GeneratedActor/ActorService/appsettings.Development.json b/examples/GeneratedActor/ActorService/appsettings.Development.json
new file mode 100644
index 000000000..ff66ba6b2
--- /dev/null
+++ b/examples/GeneratedActor/ActorService/appsettings.Development.json
@@ -0,0 +1,8 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ }
+}
diff --git a/examples/GeneratedActor/ActorService/appsettings.json b/examples/GeneratedActor/ActorService/appsettings.json
new file mode 100644
index 000000000..4d566948d
--- /dev/null
+++ b/examples/GeneratedActor/ActorService/appsettings.json
@@ -0,0 +1,9 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ },
+ "AllowedHosts": "*"
+}
diff --git a/examples/GeneratedActor/README.md b/examples/GeneratedActor/README.md
new file mode 100644
index 000000000..cd595b30e
--- /dev/null
+++ b/examples/GeneratedActor/README.md
@@ -0,0 +1,115 @@
+# Generated Actor Client Example
+
+An example of generating a strongly-typed actor client.
+
+## Prerequisites
+
+- [.NET 6+](https://dotnet.microsoft.com/download) installed
+- [Dapr CLI](https://docs.dapr.io/getting-started/install-dapr-cli/)
+- [Initialized Dapr environment](https://docs.dapr.io/getting-started/install-dapr-selfhost/)
+- [Dapr .NET SDK](https://docs.dapr.io/developing-applications/sdks/dotnet/)
+
+## Overview
+
+Two options for invoking actor methods exist in the Dapr .NET SDK, a strongly-type (remoting) option and a loosely-typed (non-remoting) option. Each has its own advantages and disadvantages. A "middle" option also exists that combines the two and gains benefits of both without some of the disadvantages of either. Using .NET Source Generators, the Dapr .NET SDK can generate a strongly-typed client implementation that uses loosely-typed method invocation under the covers.
+
+Strongly-typed clients are generated by:
+
+1. Referencing the `Dapr.Actors.Generators` NuGet package.
+
+ ```xml
+
+
+
+
+
+ ```
+
+1. Add the `Dapr.Actors.Generators.GenerateActorClientAttribute` to the actor interface.
+
+ ```csharp
+ using Dapr.Actors.Generators;
+
+ namespace Sample;
+
+ internal sealed record SampleState(string Value);
+
+ [GenerateActorClient]
+ internal interface ISampleActor
+ {
+ [ActorMethod(Name = "GetState")]
+ Task GetStateAsync(CancellationToken cancellationToken = default);
+
+ [ActorMethod(Name = "SetState")]
+ Task SetStateAsync(SampleState state, CancellationToken cancellationToken = default);
+ }
+ ```
+
+ > The `Dapr.Actors.Generators.ActorMethodAttribute` can be used to map interface methods definitions to specific actor methods should the names differ (e.g. the interface uses "Async" suffix common in .NET but the actor methods do not).
+
+1. A strongly-typed client will be generated that can be used to invoke actor methods.
+
+ ```csharp
+ using Dapr.Actors;
+ using Dapr.Actors.Client;
+ using Sample;
+
+ var proxy = ActorProxy.Create(ActorId.CreateRandom(), "SampleActor");
+
+ using var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(30));
+
+ var client = new SampleActorClient(proxy);
+
+ var state = await client.GetStateAsync(cancellationTokenSource.Token);
+
+ await client.SetStateAsync(new SampleState("Hello, World!"), cancellationTokenSource.Token);
+
+ ```
+
+## Run the example
+
+### Start the ActorService
+
+Change directory to the `ActorService` folder:
+
+```bash
+cd examples/GeneratedActor/ActorService
+```
+
+To start the `ActorService`, execute the following command:
+
+```bash
+dapr run --app-id generated-service --app-port 5226 -- dotnet run
+```
+
+### Run the ActorClient
+
+Change directory to the `ActorClient` folder:
+
+```bash
+cd examples/GeneratedActor/ActorClient
+```
+
+To run the `ActorClient`, execute the following command:
+
+```bash
+dapr run --app-id generated-client -- dotnet run
+```
+
+### Expected output
+
+You should see the following output from the `ActorClient`:
+
+```
+== APP == Testing generated client...
+== APP == Done!
+```
+
+You should see also see the following output from the `ActorService`:
+
+```
+== APP == info: GeneratedActor.RemoteActor[0]
+== APP == GetStateAsync called.
+== APP == info: GeneratedActor.RemoteActor[0]
+== APP == SetStateAsync called.
+```
\ No newline at end of file
diff --git a/examples/Workflow/README.md b/examples/Workflow/README.md
index 610077d47..1af855767 100644
--- a/examples/Workflow/README.md
+++ b/examples/Workflow/README.md
@@ -9,12 +9,24 @@ This Dapr workflow example shows how to create a Dapr workflow (`Workflow`) and
- [Initialized Dapr environment](https://docs.dapr.io/getting-started/install-dapr-selfhost/)
- [Dapr .NET SDK](https://github.com/dapr/dotnet-sdk/)
+
+## Optional Setup
+Dapr workflow, as well as this example program, now support authentication through the use of API tokens. For more information on this, view the following document: [API Token](https://github.com/dapr/dotnet-sdk/blob/master/docs/api-tokens.md)
+
## Projects in sample
This sample contains a single [WorkflowConsoleApp](./WorkflowConsoleApp) .NET project.
-It utilizes the workflow SDK as well as the workflow management API for starting and querying workflows instances.
-The main `Program.cs` file contains the main setup of the app, including the registration of the workflow and workflow activities.
-The workflow definition is found in the `Workflows` directory and the workflow activity definitions are found in the `Activities` directory.
+It utilizes the workflow SDK as well as the workflow management API for simulating inventory management and sale of goods in a store.
+The main `Program.cs` file contains the main setup of the app, the registration of the workflow and its activities, and interaction with the user. The workflow definition is found in the `Workflows` directory and the workflow activity definitions are found in the `Activities` directory.
+
+There are five activities in the directory that could be called by the workflows:
+- `NotifyActivity`: printing logs as notifications
+- `ProcessPaymentActivity`: printing logs and delaying for simulating payment processing
+- `RequestApprovalActivity`: printing logs to indicate that the order has been approved
+- `ReserveInventoryActivity`: checking if there are enough items for purchase
+- `UpdateInventoryActivity`: updating the statestore according to purchasing
+
+The `OrderProcessingWorkflow.cs` in `Workflows` directory implements the running logic of the workflow. Based on the purchase stage and outcome, it calls different activities and waits for the corresponding events to trigger interaction with the user.
This sample also contains a [WorkflowUnitTest](./WorkflowUnitTest) .NET project that utilizes [xUnit](https://xunit.net/) and [Moq](https://github.com/moq/moq) to test the workflow logic.
It works by creating an instance of the `OrderProcessingWorkflow` (defined in the `WorkflowConsoleApp` project), mocking activity calls, and testing the inputs and outputs.
@@ -49,46 +61,43 @@ For the workflow API option, two identical `curl` commands are shown, one for Li
Make note of the "1234" in the commands below. This represents the unique identifier for the workflow run and can be replaced with any identifier of your choosing.
```bash
-curl -i -X POST http://localhost:3500/v1.0-alpha1/workflows/dapr/OrderProcessingWorkflow/1234/start \
+curl -i -X POST http://localhost:3500/v1.0-beta1/workflows/dapr/OrderProcessingWorkflow/start?instanceID=1234 \
-H "Content-Type: application/json" \
- -d '{ "input" : {"Name": "Paperclips", "TotalCost": 99.95, "Quantity": 1}}'
+ -d '{"Name": "Paperclips", "TotalCost": 99.95, "Quantity": 1}'
```
On Windows (PowerShell):
```powershell
-curl -i -X POST http://localhost:3500/v1.0-alpha1/workflows/dapr/OrderProcessingWorkflow/1234/start `
+curl -i -X POST http://localhost:3500/v1.0-beta1/workflows/dapr/OrderProcessingWorkflow/start?instanceID=1234 `
-H "Content-Type: application/json" `
- -d '{ "input" : {"Name": "Paperclips", "TotalCost": 99.95, "Quantity": 1}}'
+ -d '{"Name": "Paperclips", "TotalCost": 99.95, "Quantity": 1}'
```
If successful, you should see a response like the following:
```json
-{"instance_id":"1234"}
+{"instanceID":"1234"}
```
Next, send an HTTP request to get the status of the workflow that was started:
```bash
-curl -i -X GET http://localhost:3500/v1.0-alpha1/workflows/dapr/OrderProcessingWorkflow/1234
+curl -i -X GET http://localhost:3500/v1.0-beta1/workflows/dapr/1234
```
The workflow is designed to take several seconds to complete. If the workflow hasn't completed yet when you issue the previous command, you should see the following JSON response (formatted for readability):
```json
{
- "WFInfo": {
- "instance_id": "1234"
- },
- "start_time": "2023-02-02T23:34:53Z",
- "metadata": {
+ "instanceID": "1234",
+ "workflowName": "OrderProcessingWorkflow",
+ "createdAt": "2023-05-10T00:42:03.911444105Z",
+ "lastUpdatedAt": "2023-05-10T00:42:06.142214153Z",
+ "runtimeStatus": "RUNNING",
+ "properties": {
"dapr.workflow.custom_status": "",
- "dapr.workflow.input": "{\"Name\":\"Paperclips\",\"Quantity\":1,\"TotalCost\":99.95}",
- "dapr.workflow.last_updated": "2023-02-02T23:35:07Z",
- "dapr.workflow.name": "OrderProcessingWorkflow",
- "dapr.workflow.output": "{\"Processed\":true}",
- "dapr.workflow.runtime_status": "RUNNING"
+ "dapr.workflow.input": "{\"Name\": \"Paperclips\", \"TotalCost\": 99.95, \"Quantity\": 1}"
}
}
```
@@ -97,17 +106,15 @@ Once the workflow has completed running, you should see the following output, in
```json
{
- "WFInfo": {
- "instance_id": "1234"
- },
- "start_time": "2023-02-02T23:34:53Z",
- "metadata": {
+ "instanceID": "1234",
+ "workflowName": "OrderProcessingWorkflow",
+ "createdAt": "2023-05-10T00:42:03.911444105Z",
+ "lastUpdatedAt": "2023-05-10T00:42:18.527704176Z",
+ "runtimeStatus": "COMPLETED",
+ "properties": {
"dapr.workflow.custom_status": "",
- "dapr.workflow.input": "{\"Name\":\"Paperclips\",\"Quantity\":1,\"TotalCost\":99.95}",
- "dapr.workflow.last_updated": "2023-02-02T23:35:07Z",
- "dapr.workflow.name": "OrderProcessingWorkflow",
- "dapr.workflow.output": "{\"Processed\":true}",
- "dapr.workflow.runtime_status": "COMPLETED"
+ "dapr.workflow.input": "{\"Name\": \"Paperclips\", \"TotalCost\": 99.95, \"Quantity\": 1}",
+ "dapr.workflow.output": "{\"Processed\":true}"
}
}
```
@@ -125,4 +132,4 @@ info: WorkflowConsoleApp.Activities.NotifyActivity[0]
Order 1234 processed successfully!
```
-If you have Zipkin configured for Dapr locally on your machine, then you can view the workflow trace spans in the Zipkin web UI (typically at http://localhost:9411/zipkin/).
+If you have Zipkin configured for Dapr locally on your machine, then you can view the workflow trace spans in the Zipkin web UI (typically at http://localhost:9411/zipkin/).
\ No newline at end of file
diff --git a/examples/Workflow/WorkflowConsoleApp/Activities/ProcessPaymentActivity.cs b/examples/Workflow/WorkflowConsoleApp/Activities/ProcessPaymentActivity.cs
index dc4cc531b..1ddb51bbf 100644
--- a/examples/Workflow/WorkflowConsoleApp/Activities/ProcessPaymentActivity.cs
+++ b/examples/Workflow/WorkflowConsoleApp/Activities/ProcessPaymentActivity.cs
@@ -1,7 +1,6 @@
using Dapr.Client;
using Dapr.Workflow;
using Microsoft.Extensions.Logging;
-using WorkflowConsoleApp.Models;
namespace WorkflowConsoleApp.Activities
{
diff --git a/examples/Workflow/WorkflowConsoleApp/Activities/RequestApprovalActivity.cs b/examples/Workflow/WorkflowConsoleApp/Activities/RequestApprovalActivity.cs
new file mode 100644
index 000000000..d40078fc8
--- /dev/null
+++ b/examples/Workflow/WorkflowConsoleApp/Activities/RequestApprovalActivity.cs
@@ -0,0 +1,23 @@
+using Dapr.Workflow;
+using Microsoft.Extensions.Logging;
+
+namespace WorkflowConsoleApp.Activities
+{
+ public class RequestApprovalActivity : WorkflowActivity
+ {
+ readonly ILogger logger;
+
+ public RequestApprovalActivity(ILoggerFactory loggerFactory)
+ {
+ this.logger = loggerFactory.CreateLogger();
+ }
+
+ public override Task