mirror of https://github.com/dapr/dotnet-sdk.git
Compare commits
112 Commits
Author | SHA1 | Date |
---|---|---|
|
6b49bed7f1 | |
|
faeeb8eaca | |
|
0873c5ef6f | |
|
32d06a7136 | |
|
6f07643280 | |
|
55895fa19d | |
|
c14fcea0d4 | |
|
bb47132f98 | |
|
cb065f2089 | |
|
19fd40390b | |
|
94dcdfd5b2 | |
|
c94b61e0d6 | |
|
52f0851780 | |
|
2b288a1135 | |
|
0dc268f501 | |
|
e9ee4d21bf | |
|
89d9d56bd5 | |
|
ab3ef305f2 | |
|
9e0672525e | |
|
676c0d7a7f | |
|
ef54d75f70 | |
|
01b4833474 | |
|
8b9f932c06 | |
|
da8b21bac4 | |
|
a61db8bf97 | |
|
2849ec6341 | |
|
1454043ff8 | |
|
3d500e84f7 | |
|
3a930c26d2 | |
|
ccf2bfdce3 | |
|
8bc031887e | |
|
7b5ca4fb6c | |
|
cfd4fbee84 | |
|
232f461682 | |
|
da01dcd644 | |
|
2e0fa8026a | |
|
80f0c749ea | |
|
0b80c853b6 | |
|
ef04cad901 | |
|
f769eb1205 | |
|
57a656bd2b | |
|
9d838fca9c | |
|
651e5c74cb | |
|
f3979ec080 | |
|
74a98111dc | |
|
6e2841a14a | |
|
91ee78aff4 | |
|
682df6fec9 | |
|
e7d3c47615 | |
|
7356c9dea2 | |
|
dfe7feef00 | |
|
03038fa519 | |
|
c61b15d5b1 | |
|
94b97e224f | |
|
ee8be67337 | |
|
5f21620ecf | |
|
5548c670f4 | |
|
1e148874bb | |
|
d5c32f4ecb | |
|
23e8df0295 | |
|
aa8b0fd351 | |
|
236567786e | |
|
0a978458bb | |
|
2450ced25f | |
|
5b185ce729 | |
|
8948152a87 | |
|
4d78706eb9 | |
|
920d7ad80c | |
|
72c53d841b | |
|
8948dd836f | |
|
ce4b674b28 | |
|
19cb481557 | |
|
c24a0828cd | |
|
315b08ea50 | |
|
1a7ca132a2 | |
|
c47e65021b | |
|
512a021919 | |
|
ba516bab32 | |
|
156ed567ce | |
|
23f82fae5f | |
|
f11644e73c | |
|
bc62fd5b63 | |
|
a090d6635e | |
|
70d092ea79 | |
|
2f20e1afbd | |
|
3d1fa01d0f | |
|
d355f044e5 | |
|
167c5226a0 | |
|
366a3b390e | |
|
98be2a3717 | |
|
0709a586f9 | |
|
74f6b0127f | |
|
b8e2767289 | |
|
56367963f4 | |
|
3768a983b7 | |
|
ddce8a2972 | |
|
ad3cdbec37 | |
|
76d2c3eada | |
|
84962532f1 | |
|
512c9eaaf4 | |
|
2e94bb1905 | |
|
fba9dfd531 | |
|
190156f212 | |
|
23c484e1f0 | |
|
fd71812dc5 | |
|
ba1341510b | |
|
ad3350f70e | |
|
64c2f4809b | |
|
ca0bffa440 | |
|
bdca3b320b | |
|
31af35b6c6 | |
|
1b7c9f4f80 |
|
@ -10,11 +10,15 @@
|
|||
"ghcr.io/devcontainers/features/azure-cli:1": {
|
||||
"version": "2.38"
|
||||
},
|
||||
"ghcr.io/devcontainers/features/docker-from-docker:1": {
|
||||
"version": "20.10"
|
||||
"ghcr.io/devcontainers/features/docker-in-docker": {
|
||||
"version": "latest"
|
||||
},
|
||||
"ghcr.io/devcontainers/features/dotnet:1": {
|
||||
"version": "6.0"
|
||||
"ghcr.io/devcontainers/features/dotnet": {
|
||||
"version": "8.0",
|
||||
"additionalVersions": [
|
||||
"6.0",
|
||||
"7.0"
|
||||
]
|
||||
},
|
||||
"ghcr.io/devcontainers/features/github-cli:1": {
|
||||
"version": "2"
|
||||
|
@ -32,7 +36,8 @@
|
|||
"ms-dotnettools.csharp",
|
||||
"ms-dotnettools.vscode-dotnet-runtime",
|
||||
"ms-azuretools.vscode-dapr",
|
||||
"GitHub.copilot"
|
||||
"GitHub.copilot",
|
||||
"ms-dotnettools.csdevkit"
|
||||
],
|
||||
"forwardPorts": [
|
||||
3000,
|
||||
|
@ -42,10 +47,9 @@
|
|||
5000,
|
||||
5007
|
||||
],
|
||||
"postCreateCommand": ".devcontainer/localinit.sh",
|
||||
"postCreateCommand": "chmod +x .devcontainer/localinit.sh && .devcontainer/localinit.sh",
|
||||
"remoteUser": "vscode",
|
||||
"hostRequirements": {
|
||||
"memory": "8gb"
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
||||
dapr init --runtime-version=1.14.0
|
|
@ -1,6 +1,6 @@
|
|||
organization: dapr
|
||||
defaultSticker: clmjkxscc122740fl0mkmb7egi
|
||||
defaultSticker: clrqfdv4x24910fl5n4iwu5oa
|
||||
stickers:
|
||||
-
|
||||
id: clmjkxscc122740fl0mkmb7egi
|
||||
alias: ghc2023
|
||||
id: clrqfdv4x24910fl5n4iwu5oa
|
||||
alias: sdk-badge
|
|
@ -18,8 +18,9 @@ jobs:
|
|||
name: run integration tests
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
dotnet-version: ['6.0', '7.0', '8.0']
|
||||
dotnet-version: ['6.0', '7.0', '8.0', '9.0']
|
||||
include:
|
||||
- dotnet-version: '6.0'
|
||||
display-name: '.NET 6.0'
|
||||
|
@ -36,15 +37,20 @@ jobs:
|
|||
framework: 'net8'
|
||||
prefix: 'net8'
|
||||
install-version: '8.0.x'
|
||||
- dotnet-version: '9.0'
|
||||
display-name: '.NET 9.0'
|
||||
framework: 'net9'
|
||||
prefix: 'net9'
|
||||
install-version: '9.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.12.0
|
||||
DAPR_RUNTIME_VER: 1.12.0
|
||||
DAPR_INSTALL_URL: https://raw.githubusercontent.com/dapr/cli/release-1.12/install/install.sh
|
||||
DAPR_CLI_VER: 1.15.0
|
||||
DAPR_RUNTIME_VER: 1.15.3
|
||||
DAPR_INSTALL_URL: https://raw.githubusercontent.com/dapr/cli/release-1.14/install/install.sh
|
||||
DAPR_CLI_REF: ''
|
||||
steps:
|
||||
- name: Set up Dapr CLI
|
||||
|
@ -102,14 +108,22 @@ jobs:
|
|||
- name: Parse release version
|
||||
run: python ./.github/scripts/get_release_version.py
|
||||
- name: Setup ${{ matrix.display-name }}
|
||||
uses: actions/setup-dotnet@v1
|
||||
uses: actions/setup-dotnet@v3
|
||||
with:
|
||||
dotnet-version: ${{ matrix.install-version }}
|
||||
- name: Setup .NET 8.0 # net8 is always required.
|
||||
uses: actions/setup-dotnet@v1
|
||||
dotnet-quality: 'ga' # Prefer a GA release, but use the RC if not available
|
||||
- name: Setup .NET 8 (required)
|
||||
uses: actions/setup-dotnet@v3
|
||||
if: ${{ matrix.install-version != '8.0.x' }}
|
||||
with:
|
||||
dotnet-version: 8.0.x
|
||||
dotnet-version: '8.0.x'
|
||||
dotnet-quality: 'ga'
|
||||
- name: Setup .NET 9 (required)
|
||||
uses: actions/setup-dotnet@v3
|
||||
if: ${{ matrix.install-version != '9.0.x' }}
|
||||
with:
|
||||
dotnet-version: '9.0.x'
|
||||
dotnet-quality: 'ga'
|
||||
- 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
|
||||
|
|
|
@ -24,15 +24,16 @@ jobs:
|
|||
- name: Parse release version
|
||||
run: python ./.github/scripts/get_release_version.py
|
||||
- name: Setup .NET Core
|
||||
uses: actions/setup-dotnet@v1
|
||||
uses: actions/setup-dotnet@v3
|
||||
with:
|
||||
dotnet-version: 8.0.x
|
||||
dotnet-version: 9.0.x
|
||||
dotnet-quality: 'ga'
|
||||
- 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 }}
|
||||
|
@ -41,40 +42,51 @@ jobs:
|
|||
name: Test .NET ${{ matrix.dotnet-version }}
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
dotnet-version: ['6.0', '7.0', '8.0']
|
||||
dotnet-version: ['6.0', '7.0', '8.0', '9.0']
|
||||
include:
|
||||
- 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'
|
||||
install-3: false
|
||||
display-name: '.NET 8.0'
|
||||
framework: 'net8'
|
||||
prefix: 'net8'
|
||||
install-version: '8.0.x'
|
||||
- dotnet-version: '9.0'
|
||||
display-name: '.NET 9.0'
|
||||
framework: 'net9'
|
||||
prefix: 'net9'
|
||||
install-version: '9.0.x'
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Parse release version
|
||||
run: python ./.github/scripts/get_release_version.py
|
||||
- name: Setup ${{ matrix.display-name }}
|
||||
uses: actions/setup-dotnet@v1
|
||||
uses: actions/setup-dotnet@v3
|
||||
with:
|
||||
dotnet-version: ${{ matrix.install-version }}
|
||||
- name: Setup .NET 8.0 # net8 is always required.
|
||||
uses: actions/setup-dotnet@v1
|
||||
dotnet-quality: 'ga' # Prefer a GA release, but use the RC if not available
|
||||
- name: Setup .NET 8 (required)
|
||||
uses: actions/setup-dotnet@v3
|
||||
if: ${{ matrix.install-version != '8.0.x' }}
|
||||
with:
|
||||
dotnet-version: 8.0.x
|
||||
dotnet-version: '8.0.x'
|
||||
dotnet-quality: 'ga'
|
||||
- name: Setup .NET 9 (required)
|
||||
uses: actions/setup-dotnet@v3
|
||||
if: ${{ matrix.install-version != '9.0.x' }}
|
||||
with:
|
||||
dotnet-version: '9.0.x'
|
||||
dotnet-quality: 'ga'
|
||||
- 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 +128,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
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
<Project>
|
||||
<PropertyGroup>
|
||||
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
||||
<CentralPackageTransitivePinningEnabled>true</CentralPackageTransitivePinningEnabled>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageVersion Include="BenchmarkDotNet" Version="0.14.0" />
|
||||
<PackageVersion Include="coverlet.collector" Version="6.0.2" />
|
||||
<PackageVersion Include="coverlet.msbuild" Version="6.0.2" />
|
||||
<PackageVersion Include="GitHubActionsTestLogger" Version="1.1.2" />
|
||||
<PackageVersion Include="Google.Api.CommonProtos" Version="2.2.0" />
|
||||
<PackageVersion Include="Google.Protobuf" Version="3.30.2" />
|
||||
<PackageVersion Include="Grpc.AspNetCore" Version="2.71.0" />
|
||||
<PackageVersion Include="Grpc.Core.Testing" Version="2.46.6" />
|
||||
<PackageVersion Include="Grpc.Net.Client" Version="2.71.0" />
|
||||
<PackageVersion Include="Grpc.Net.ClientFactory" Version="2.71.0" />
|
||||
<PackageVersion Include="Grpc.Tools" Version="2.71.0" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="6.0.35" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.TestHost" Version="6.0.35" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.Common" Version="4.8.0" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.8.0" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.SourceGenerators.Testing" Version="1.1.2" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.SourceGenerators.Testing.XUnit" Version="1.1.2" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.8.0" />
|
||||
<PackageVersion Include="Microsoft.DurableTask.Client.Grpc" Version="1.10.0" />
|
||||
<PackageVersion Include="Microsoft.DurableTask.Worker.Grpc" Version="1.10.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Configuration" Version="6.0.1" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Configuration.Abstractions" Version="6.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="8.0.1" />
|
||||
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.2" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Logging" Version="6.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="6.0.4" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="6.0.1" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Http" Version="6.0.0" />
|
||||
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="16.8.3" />
|
||||
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="1.1.1" />
|
||||
<PackageVersion Include="MinVer" Version="2.3.0" />
|
||||
<PackageVersion Include="Moq" Version="4.20.72" />
|
||||
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageVersion Include="protobuf-net.Grpc.AspNetCore" Version="1.2.2" />
|
||||
<PackageVersion Include="Serilog.AspNetCore" Version="6.1.0" />
|
||||
<PackageVersion Include="Serilog.Sinks.Console" Version="4.1.0" />
|
||||
<PackageVersion Include="Serilog.Sinks.File" Version="5.0.0" />
|
||||
<PackageVersion Include="Shouldly" Version="4.2.1" />
|
||||
<PackageVersion Include="System.Formats.Asn1" Version="6.0.1" />
|
||||
<PackageVersion Include="System.Text.Json" Version="6.0.10" />
|
||||
<PackageVersion Include="xunit" Version="2.9.2" />
|
||||
<PackageVersion Include="xunit.extensibility.core" Version="2.9.2" />
|
||||
<PackageVersion Include="xunit.runner.visualstudio" Version="2.8.2" />
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -1,9 +1,6 @@
|
|||
# Dapr SDK for .NET
|
||||
|
||||
[](https://github.com/dapr/dotnet-sdk/actions?workflow=build)
|
||||
[](https://codecov.io/gh/dapr/dotnet-sdk)
|
||||
[](http://www.apache.org/licenses/LICENSE-2.0)
|
||||
[](https://app.fossa.com/projects/custom%2B162%2Fgit%40github.com%3Adapr%2Fdotnet-sdk.git?ref=badge_shield)
|
||||
[](https://www.nuget.org/packages/Dapr.Client) [](https://www.nuget.org/packages/Dapr.Client) [](https://github.com/dapr/dotnet-sdk/actions/workflows/sdk_build.yml) [](https://codecov.io/gh/dapr/dotnet-sdk) [](https://github.com/dapr/dotnet-sdk/blob/master/LICENSE) [](https://github.com/dapr/dotnet-sdk/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) [](http://bit.ly/dapr-discord) [](https://youtube.com/@daprdev) [](https://twitter.com/daprdev)
|
||||
|
||||
|
||||
Dapr SDK for .NET allows you to:
|
||||
|
|
169
all.sln
169
all.sln
|
@ -1,4 +1,3 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.3.32929.385
|
||||
|
@ -15,11 +14,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dapr.Client", "src\Dapr.Cli
|
|||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dapr.AspNetCore", "src\Dapr.AspNetCore\Dapr.AspNetCore.csproj", "{08D602F6-7C11-4653-B70B-B56333BF6FD2}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{B2DB41EE-45F5-447B-95E8-38E1E8B70C4E}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
samples\.editorconfig = samples\.editorconfig
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{DD020B34-460F-455F-8D17-CF4A949F100B}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dapr.Client.Test", "test\Dapr.Client.Test\Dapr.Client.Test.csproj", "{383609C1-F43F-49EB-85E4-1964EE7F0F14}"
|
||||
|
@ -33,6 +27,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 +66,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}"
|
||||
|
@ -106,18 +99,61 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowUnitTest", "example
|
|||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "GeneratedActor", "GeneratedActor", "{7592AFA4-426B-42F3-AE82-957C86814482}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ActorClient", "examples\GeneratedActor\ActorClient\ActorClient.csproj", "{61C24126-F39D-4BEA-96DC-FC87BA730554}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ActorClient", "examples\GeneratedActor\ActorClient\ActorClient.csproj", "{61C24126-F39D-4BEA-96DC-FC87BA730554}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ActorCommon", "examples\GeneratedActor\ActorCommon\ActorCommon.csproj", "{CB903D21-4869-42EF-BDD6-5B1CFF674337}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ActorCommon", "examples\GeneratedActor\ActorCommon\ActorCommon.csproj", "{CB903D21-4869-42EF-BDD6-5B1CFF674337}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dapr.Actors.Generators", "src\Dapr.Actors.Generators\Dapr.Actors.Generators.csproj", "{980B5FD8-0107-41F7-8FAD-E4E8BAE8A625}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dapr.Actors.Generators", "src\Dapr.Actors.Generators\Dapr.Actors.Generators.csproj", "{980B5FD8-0107-41F7-8FAD-E4E8BAE8A625}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ActorService", "examples\GeneratedActor\ActorService\ActorService.csproj", "{7C06FE2D-6C62-48F5-A505-F0D715C554DE}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ActorService", "examples\GeneratedActor\ActorService\ActorService.csproj", "{7C06FE2D-6C62-48F5-A505-F0D715C554DE}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dapr.Actors.Generators.Test", "test\Dapr.Actors.Generators.Test\Dapr.Actors.Generators.Test.csproj", "{AF89083D-4715-42E6-93E9-38497D12A8A6}"
|
||||
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("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dapr.E2E.Test.Actors.Generators", "test\Dapr.E2E.Test.Actors.Generators\Dapr.E2E.Test.Actors.Generators.csproj", "{B5CDB0DC-B26D-48F1-B934-FE5C1C991940}"
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cryptography", "examples\Client\Cryptography\Cryptography.csproj", "{C74FBA78-13E8-407F-A173-4555AEE41FF3}"
|
||||
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
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dapr.Common.Test", "test\Dapr.Common.Test\Dapr.Common.Test.csproj", "{CDB47863-BEBD-4841-A807-46D868962521}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dapr.AI", "src\Dapr.AI\Dapr.AI.csproj", "{273F2527-1658-4CCF-8DC6-600E921188C5}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dapr.AI.Test", "test\Dapr.AI.Test\Dapr.AI.Test.csproj", "{2F3700EF-1CDA-4C15-AC88-360230000ECD}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "AI", "AI", "{3046DBF4-C2FF-4F3A-9176-E1C01E0A90E5}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConversationalAI", "examples\AI\ConversationalAI\ConversationalAI.csproj", "{11011FF8-77EA-4B25-96C0-29D4D486EF1C}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkflowExternalInteraction", "examples\Workflow\WorkflowExternalInteraction\WorkflowExternalInteraction.csproj", "{43CB06A9-7E88-4C5F-BFB8-947E072CBC9F}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkflowMonitor", "examples\Workflow\WorkflowMonitor\WorkflowMonitor.csproj", "{7F73A3D8-FFC2-4E31-AA3D-A4840316A8C6}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkflowTaskChaining", "examples\Workflow\WorkflowTaskChaining\WorkflowTaskChaining.csproj", "{945DD3B7-94E5-435E-B3CB-796C20A652C7}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkflowSubworkflow", "examples\Workflow\WorkflowSubworkflow\WorkflowSubworkflow.csproj", "{FD3E9371-3134-4235-8E80-32226DFB4B1F}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkflowFanOutFanIn", "examples\Workflow\WorkflowFanOutFanIn\WorkflowFanOutFanIn.csproj", "{D83B27F3-4401-42F5-843E-147566B4999A}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkflowAsyncOperations", "examples\Workflow\WorkflowAsyncOperations\WorkflowAsyncOperations.csproj", "{00359961-0C50-4BB1-A794-8B06DE991639}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dapr.Messaging.Test", "test\Dapr.Messaging.Test\Dapr.Messaging.Test.csproj", "{4E04EB35-7FD2-4FDB-B09A-F75CE24053B9}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dapr.Messaging", "src\Dapr.Messaging\Dapr.Messaging.csproj", "{0EAE36A1-B578-4F13-A113-7A477ECA1BDA}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StreamingSubscriptionExample", "examples\Client\PublishSubscribe\StreamingSubscriptionExample\StreamingSubscriptionExample.csproj", "{290D1278-F613-4DF3-9DF5-F37E38CDC363}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dapr.Jobs", "src\Dapr.Jobs\Dapr.Jobs.csproj", "{C8BB6A85-A7EA-40C0-893D-F36F317829B3}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dapr.Jobs.Test", "test\Dapr.Jobs.Test\Dapr.Jobs.Test.csproj", "{BF9828E9-5597-4D42-AA6E-6E6C12214204}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Jobs", "Jobs", "{D9697361-232F-465D-A136-4561E0E88488}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JobsSample", "examples\Jobs\JobsSample\JobsSample.csproj", "{9CAF360E-5AD3-4C4F-89A0-327EEB70D673}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dapr.Workflow.Test", "test\Dapr.Workflow.Test\Dapr.Workflow.Test.csproj", "{E90114C6-86FC-43B8-AE5C-D9273CF21FE4}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
|
@ -291,10 +327,82 @@ Global
|
|||
{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
|
||||
{D1786E2B-CAA0-4B2D-A974-9845EB9E420F}.Debug|Any CPU.ActiveCfg = Debug
|
||||
{D1786E2B-CAA0-4B2D-A974-9845EB9E420F}.Debug|Any CPU.Build.0 = Debug
|
||||
{D1786E2B-CAA0-4B2D-A974-9845EB9E420F}.Release|Any CPU.ActiveCfg = Release
|
||||
{D1786E2B-CAA0-4B2D-A974-9845EB9E420F}.Release|Any CPU.Build.0 = Release
|
||||
{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
|
||||
{CDB47863-BEBD-4841-A807-46D868962521}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{CDB47863-BEBD-4841-A807-46D868962521}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{CDB47863-BEBD-4841-A807-46D868962521}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{CDB47863-BEBD-4841-A807-46D868962521}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{273F2527-1658-4CCF-8DC6-600E921188C5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{273F2527-1658-4CCF-8DC6-600E921188C5}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{273F2527-1658-4CCF-8DC6-600E921188C5}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{273F2527-1658-4CCF-8DC6-600E921188C5}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{2F3700EF-1CDA-4C15-AC88-360230000ECD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{2F3700EF-1CDA-4C15-AC88-360230000ECD}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{2F3700EF-1CDA-4C15-AC88-360230000ECD}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{2F3700EF-1CDA-4C15-AC88-360230000ECD}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{11011FF8-77EA-4B25-96C0-29D4D486EF1C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{11011FF8-77EA-4B25-96C0-29D4D486EF1C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{11011FF8-77EA-4B25-96C0-29D4D486EF1C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{11011FF8-77EA-4B25-96C0-29D4D486EF1C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{43CB06A9-7E88-4C5F-BFB8-947E072CBC9F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{43CB06A9-7E88-4C5F-BFB8-947E072CBC9F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{43CB06A9-7E88-4C5F-BFB8-947E072CBC9F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{43CB06A9-7E88-4C5F-BFB8-947E072CBC9F}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{7F73A3D8-FFC2-4E31-AA3D-A4840316A8C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{7F73A3D8-FFC2-4E31-AA3D-A4840316A8C6}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{7F73A3D8-FFC2-4E31-AA3D-A4840316A8C6}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{7F73A3D8-FFC2-4E31-AA3D-A4840316A8C6}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{945DD3B7-94E5-435E-B3CB-796C20A652C7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{945DD3B7-94E5-435E-B3CB-796C20A652C7}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{945DD3B7-94E5-435E-B3CB-796C20A652C7}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{945DD3B7-94E5-435E-B3CB-796C20A652C7}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{FD3E9371-3134-4235-8E80-32226DFB4B1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{FD3E9371-3134-4235-8E80-32226DFB4B1F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{FD3E9371-3134-4235-8E80-32226DFB4B1F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{FD3E9371-3134-4235-8E80-32226DFB4B1F}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{D83B27F3-4401-42F5-843E-147566B4999A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{D83B27F3-4401-42F5-843E-147566B4999A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{D83B27F3-4401-42F5-843E-147566B4999A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{D83B27F3-4401-42F5-843E-147566B4999A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{00359961-0C50-4BB1-A794-8B06DE991639}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{00359961-0C50-4BB1-A794-8B06DE991639}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{00359961-0C50-4BB1-A794-8B06DE991639}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{00359961-0C50-4BB1-A794-8B06DE991639}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{4E04EB35-7FD2-4FDB-B09A-F75CE24053B9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{4E04EB35-7FD2-4FDB-B09A-F75CE24053B9}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{4E04EB35-7FD2-4FDB-B09A-F75CE24053B9}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{4E04EB35-7FD2-4FDB-B09A-F75CE24053B9}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{0EAE36A1-B578-4F13-A113-7A477ECA1BDA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{0EAE36A1-B578-4F13-A113-7A477ECA1BDA}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{0EAE36A1-B578-4F13-A113-7A477ECA1BDA}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{0EAE36A1-B578-4F13-A113-7A477ECA1BDA}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{290D1278-F613-4DF3-9DF5-F37E38CDC363}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{290D1278-F613-4DF3-9DF5-F37E38CDC363}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{290D1278-F613-4DF3-9DF5-F37E38CDC363}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{290D1278-F613-4DF3-9DF5-F37E38CDC363}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{C8BB6A85-A7EA-40C0-893D-F36F317829B3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{C8BB6A85-A7EA-40C0-893D-F36F317829B3}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C8BB6A85-A7EA-40C0-893D-F36F317829B3}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{C8BB6A85-A7EA-40C0-893D-F36F317829B3}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{BF9828E9-5597-4D42-AA6E-6E6C12214204}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{BF9828E9-5597-4D42-AA6E-6E6C12214204}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{BF9828E9-5597-4D42-AA6E-6E6C12214204}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{BF9828E9-5597-4D42-AA6E-6E6C12214204}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{9CAF360E-5AD3-4C4F-89A0-327EEB70D673}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{9CAF360E-5AD3-4C4F-89A0-327EEB70D673}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{9CAF360E-5AD3-4C4F-89A0-327EEB70D673}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{9CAF360E-5AD3-4C4F-89A0-327EEB70D673}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{E90114C6-86FC-43B8-AE5C-D9273CF21FE4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{E90114C6-86FC-43B8-AE5C-D9273CF21FE4}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{E90114C6-86FC-43B8-AE5C-D9273CF21FE4}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{E90114C6-86FC-43B8-AE5C-D9273CF21FE4}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
@ -343,11 +451,32 @@ Global
|
|||
{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} = {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}
|
||||
{CDB47863-BEBD-4841-A807-46D868962521} = {DD020B34-460F-455F-8D17-CF4A949F100B}
|
||||
{273F2527-1658-4CCF-8DC6-600E921188C5} = {27C5D71D-0721-4221-9286-B94AB07B58CF}
|
||||
{2F3700EF-1CDA-4C15-AC88-360230000ECD} = {DD020B34-460F-455F-8D17-CF4A949F100B}
|
||||
{3046DBF4-C2FF-4F3A-9176-E1C01E0A90E5} = {D687DDC4-66C5-4667-9E3A-FD8B78ECAA78}
|
||||
{11011FF8-77EA-4B25-96C0-29D4D486EF1C} = {3046DBF4-C2FF-4F3A-9176-E1C01E0A90E5}
|
||||
{43CB06A9-7E88-4C5F-BFB8-947E072CBC9F} = {BF3ED6BF-ADF3-4D25-8E89-02FB8D945CA9}
|
||||
{7F73A3D8-FFC2-4E31-AA3D-A4840316A8C6} = {BF3ED6BF-ADF3-4D25-8E89-02FB8D945CA9}
|
||||
{945DD3B7-94E5-435E-B3CB-796C20A652C7} = {BF3ED6BF-ADF3-4D25-8E89-02FB8D945CA9}
|
||||
{FD3E9371-3134-4235-8E80-32226DFB4B1F} = {BF3ED6BF-ADF3-4D25-8E89-02FB8D945CA9}
|
||||
{D83B27F3-4401-42F5-843E-147566B4999A} = {BF3ED6BF-ADF3-4D25-8E89-02FB8D945CA9}
|
||||
{00359961-0C50-4BB1-A794-8B06DE991639} = {BF3ED6BF-ADF3-4D25-8E89-02FB8D945CA9}
|
||||
{4E04EB35-7FD2-4FDB-B09A-F75CE24053B9} = {DD020B34-460F-455F-8D17-CF4A949F100B}
|
||||
{0EAE36A1-B578-4F13-A113-7A477ECA1BDA} = {27C5D71D-0721-4221-9286-B94AB07B58CF}
|
||||
{290D1278-F613-4DF3-9DF5-F37E38CDC363} = {0EF6EA64-D7C3-420D-9890-EAE8D54A57E6}
|
||||
{C8BB6A85-A7EA-40C0-893D-F36F317829B3} = {27C5D71D-0721-4221-9286-B94AB07B58CF}
|
||||
{BF9828E9-5597-4D42-AA6E-6E6C12214204} = {DD020B34-460F-455F-8D17-CF4A949F100B}
|
||||
{D9697361-232F-465D-A136-4561E0E88488} = {D687DDC4-66C5-4667-9E3A-FD8B78ECAA78}
|
||||
{9CAF360E-5AD3-4C4F-89A0-327EEB70D673} = {D9697361-232F-465D-A136-4561E0E88488}
|
||||
{E90114C6-86FC-43B8-AE5C-D9273CF21FE4} = {DD020B34-460F-455F-8D17-CF4A949F100B}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {65220BF2-EAE1-4CB2-AA58-EBE80768CB40}
|
||||
|
|
|
@ -6,18 +6,112 @@ weight: 3000
|
|||
description: Guidelines for contributing to the Dapr .NET SDK
|
||||
---
|
||||
|
||||
When contributing to the [.NET SDK](https://github.com/dapr/dotnet-sdk) the following rules and best-practices should be followed.
|
||||
# Welcome!
|
||||
If you're reading this, you're likely interested in contributing to Dapr and/or the Dapr .NET SDK. Welcome to the project
|
||||
and thank you for your interest in contributing!
|
||||
|
||||
Please review the documentation, familiarize yourself with what Dapr is and what it's seeking to accomplish and reach
|
||||
out on [Discord](https://bit.ly/dapr-discord). Let us know how you'd like to contribute and we'd be happy to chime in
|
||||
with ideas and suggestions.
|
||||
|
||||
There are many ways to contribute to Dapr:
|
||||
- Submit bug reports for the [Dapr runtime](https://github.com/dapr/dapr/issues/new/choose) or the [Dapr .NET SDK](https://github.com/dapr/dotnet-sdk/issues/new/choose)
|
||||
- Propose new [runtime capabilities](https://github.com/dapr/proposals/issues/new/choose) or [SDK functionality](https://github.com/dapr/dotnet-sdk/issues/new/choose)
|
||||
- Improve the documentation in either the [larger Dapr project](https://github.com/dapr/docs) or the [Dapr .NET SDK specifically](https://github.com/dapr/dotnet-sdk/tree/master/daprdocs)
|
||||
- Add new or improve existing [components](https://github.com/dapr/components-contrib/) that implement the various building blocks
|
||||
- Augment the [.NET pluggable component SDK capabilities](https://github.com/dapr-sandbox/components-dotnet-sdk)
|
||||
- Improve the Dapr .NET SDK code base and/or fix a bug (detailed below)
|
||||
|
||||
If you're new to the code base, please feel encouraged to ask in the #dotnet-sdk channel in Discord about how
|
||||
to implement changes or generally ask questions. You are not required to seek permission to work on anything, but do
|
||||
note that if an issue is assigned to someone, it's an indication that someone might have already started work on it.
|
||||
Especially if it's been a while since the last activity on that issue, please feel free to reach out and see if it's
|
||||
still something they're interested in pursuing or whether you can take over, and open a pull request with your
|
||||
implementation.
|
||||
|
||||
If you'd like to assign yourself to an issue, respond to the conversation with "/assign" and the bot will assign you
|
||||
to it.
|
||||
|
||||
We have labeled some issues as `good-first-issue` or `help wanted` indicating that these are likely to be small,
|
||||
self-contained changes.
|
||||
|
||||
If you're not certain about your implementation, please create it as a draft pull request and solicit feedback
|
||||
from the [.NET maintainers](https://github.com/orgs/dapr/teams/maintainers-dotnet-sdk) by tagging
|
||||
`@dapr/maintainers-dotnet-sdk` and providing some context about what you need assistance with.
|
||||
|
||||
# Contribution Rules and Best Practices
|
||||
|
||||
When contributing to the [.NET SDK](https://github.com/dapr/dotnet-sdk) the following rules and best-practices should
|
||||
be followed.
|
||||
|
||||
## Pull Requests
|
||||
Pull requests that contain only formatting changes are generally discouraged. Pull requests should instead seek to
|
||||
fix a bug, add new functionality, or improve on existing capabilities.
|
||||
|
||||
Do aim to minimize the contents of your pull request to span only a single issue. Broad PRs that touch on a lot of files
|
||||
are not likely to be reviewed or accepted in a short timeframe. Accommodating many different issues in a single PR makes
|
||||
it hard to determine whether your code fully addresses the underlying issue(s) or not and complicates the code review.
|
||||
|
||||
## Tests
|
||||
All pull requests should include unit and/or integration tests that reflect the nature of what was added or changed
|
||||
so it's clear that the functionality works as intended. Avoid using auto-generated tests that duplicate testing the
|
||||
same functionality several times. Rather, seek to improve code coverage by validating each possible path of your
|
||||
changes so future contributors can more easily navigate the contours of your logic and more readily identify limitations.
|
||||
|
||||
## Examples
|
||||
|
||||
The `examples` directory contains code samples for users to run to try out specific functionality of the various .NET SDK packages and extensions. When writing new and updated samples keep in mind:
|
||||
The `examples` directory contains code samples for users to run to try out specific functionality of the various
|
||||
Dapr .NET SDK packages and extensions. When writing new and updated samples keep in mind:
|
||||
|
||||
- All examples should be runnable on Windows, Linux, and MacOS. While .NET Core code is consistent among operating systems, any pre/post example commands should provide options through [codetabs]({{< ref "contributing-docs.md#tabbed-content" >}})
|
||||
- Contain steps to download/install any required pre-requisites. Someone coming in with a fresh OS install should be able to start on the example and complete it without an error. Links to external download pages are fine.
|
||||
- All examples should be runnable on Windows, Linux, and MacOS. While .NET Core code is consistent among operating
|
||||
systems, any pre/post example commands should provide options through
|
||||
[codetabs]({{< ref "contributing-docs.md#tabbed-content" >}})
|
||||
- Contain steps to download/install any required pre-requisites. Someone coming in with a fresh OS install should be
|
||||
able to start on the example and complete it without an error. Links to external download pages are fine.
|
||||
|
||||
## Docs
|
||||
## Documentation
|
||||
|
||||
The `daprdocs` directory contains the markdown files that are rendered into the [Dapr Docs](https://docs.dapr.io) website. When the documentation website is built this repo is cloned and configured so that its contents are rendered with the docs content. When writing docs keep in mind:
|
||||
The `daprdocs` directory contains the markdown files that are rendered into the [Dapr Docs](https://docs.dapr.io) website. When the
|
||||
documentation website is built this repo is cloned and configured so that its contents are rendered with the docs
|
||||
content. When writing docs keep in mind:
|
||||
|
||||
- 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.
|
||||
- All files and directories should be prefixed with `dotnet-` to ensure all file/directory names are globally
|
||||
- unique across all Dapr documentation.
|
||||
|
||||
All pull requests should strive to include both XML documentation in the code clearly indicating what functionality
|
||||
does and why it's there as well as changes to the published documentation to clarify for other developers how your change
|
||||
improves the Dapr framework.
|
||||
|
||||
## 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.
|
||||
|
||||
## Commit Sign-offs
|
||||
All code submitted to the Dapr .NET SDK must be signed off by the developer authoring it. This means that every
|
||||
commit must end with the following:
|
||||
> Signed-off-by: First Last <flast@example.com>
|
||||
|
||||
The name and email address must match the registered GitHub name and email address of the user committing the changes.
|
||||
We use a bot to detect this in pull requests and we will be unable to merge the PR if this check fails to validate.
|
||||
|
||||
If you notice that a PR has failed to validate because of a failed DCO check early on in the PR history, please consider
|
||||
squashing the PR locally and resubmitting to ensure that the sign-off statement is included in the commit history.
|
||||
|
||||
# Languages, Tools and Processes
|
||||
All source code in the Dapr .NET SDK is written in C# and targets the latest language version available to the earliest
|
||||
supported .NET SDK. As of v1.15, this means that because .NET 6 is still supported, the latest language version available
|
||||
is [C# version 10](https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-version-history#c-version-10).
|
||||
|
||||
As of v1.15, the following versions of .NET are supported:
|
||||
|
||||
| Version | Notes |
|
||||
| --- |-----------------------------------------------------------------|
|
||||
| .NET 6 | Will be discontinued in v1.16 |
|
||||
| .NET 7 | Only supported in Dapr.Workflows, will be discontinued in v1.16 |
|
||||
| .NET 8 | Will continue to be supported in v1.16 |
|
||||
| .NET 9 | Will continue to be supported in v1.16 |
|
||||
|
||||
Contributors are welcome to use whatever IDE they're most comfortable developing in, but please do not submit
|
||||
IDE-specific preference files along with your contributions as these will be rejected.
|
|
@ -18,7 +18,15 @@ 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), [.NET 8](https://dotnet.microsoft.com/download) or [.NET 9](https://dotnet.microsoft.com/download) installed
|
||||
|
||||
{{% alert title="Note" color="primary" %}}
|
||||
|
||||
Note that while .NET 6 is generally supported as the minimum .NET requirement across the Dapr .NET SDK packages
|
||||
and .NET 7 is the minimally supported version of .NET by Dapr.Workflows in Dapr v1.15, only .NET 8 and .NET 9 will
|
||||
continue to be supported by Dapr in v1.16 and later.
|
||||
|
||||
{{% /alert %}}
|
||||
|
||||
## Installation
|
||||
|
||||
|
@ -69,6 +77,20 @@ Put the Dapr .NET SDK to the test. Walk through the .NET quickstarts and tutoria
|
|||
<a href="{{< ref dotnet-workflow >}}" class="stretched-link"></a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title"><b>Jobs</b></h5>
|
||||
<p class="card-text">Create and manage the scheduling and orchestration of jobs in .NET.</p>
|
||||
<a href="{{< ref dotnet-jobs >}}" class="stretched-link"></a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title"><b>AI</b></h5>
|
||||
<p class="card-text">Create and manage AI operations in .NET</p>
|
||||
<a href="{{< ref dotnet-ai >}}" class="stretched-link"></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
## More information
|
||||
|
|
|
@ -45,7 +45,15 @@ 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), [.NET 8](https://dotnet.microsoft.com/download) or [.NET 9](https://dotnet.microsoft.com/download) installed
|
||||
|
||||
{{% alert title="Note" color="primary" %}}
|
||||
|
||||
Note that while .NET 6 is generally supported as the minimum .NET requirement across the Dapr .NET SDK packages
|
||||
and .NET 7 is the minimally supported version of .NET by Dapr.Workflows in Dapr v1.15, only .NET 8 and .NET 9 will
|
||||
continue to be supported by Dapr in v1.16 and later.
|
||||
|
||||
{{% /alert %}}
|
||||
|
||||
## Step 0: Prepare
|
||||
|
||||
|
@ -319,11 +327,8 @@ namespace MyActorService
|
|||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,15 +5,263 @@ linkTitle: "Actor serialization"
|
|||
weight: 300000
|
||||
description: Necessary steps to serialize your types using remoted Actors in .NET
|
||||
---
|
||||
# Actor Serialization
|
||||
|
||||
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.
|
||||
The Dapr actor package enables you to use Dapr virtual actors within a .NET application with either a weakly- or strongly-typed client. Each utilizes a different serialization approach. This document will review the differences and convey a few key ground rules to understand in either scenario.
|
||||
|
||||
# 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.
|
||||
Please be advised that it is not a supported scenario to use the weakly- or strongly typed actor clients interchangeably because of these different serialization approaches. The data persisted using one Actor client will not be accessible using the other Actor client, so it is important to pick one and use it consistently throughout your application.
|
||||
|
||||
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).
|
||||
## Weakly-typed Dapr Actor client
|
||||
In this section, you will learn how to configure your C# types so they are properly serialized and deserialized at runtime when using a weakly-typed actor client. These clients use string-based names of methods with request and response payloads that are serialized using the System.Text.Json serializer. Please note that 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/tree/main/src/libraries/System.Text.Json).
|
||||
|
||||
## Serializable Types
|
||||
When using the weakly-typed Dapr Actor client to invoke methods from your various actors, it's not necessary to independently serialize or deserialize the method payloads as this will happen transparently on your behalf by the SDK.
|
||||
|
||||
The client will use the latest version of System.Text.Json available for the version of .NET you're building against and serialization is subject to all the inherent capabilities provided in the [associated .NET documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/overview).
|
||||
|
||||
The serializer will be configured to use the `JsonSerializerOptions.Web` [default options](https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/configure-options?pivots=dotnet-8-0#web-defaults-for-jsonserializeroptions) unless overridden with a custom options configuration which means the following are applied:
|
||||
- Deserialization of the property name is performed in a case-insensitive manner
|
||||
- Serialization of the property name is performed using [camel casing](https://en.wikipedia.org/wiki/Camel_case) unless the property is overridden with a `[JsonPropertyName]` attribute
|
||||
- Deserialization will read numeric values from number and/or string values
|
||||
|
||||
### Basic Serialization
|
||||
In the following example, we present a simple class named Doodad though it could just as well be a record as well.
|
||||
|
||||
```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:
|
||||
|
||||
```json
|
||||
{"id": "a06ced64-4f42-48ad-84dd-46ae6a7e333d", "name": "DoodadName", "count": 5}
|
||||
```
|
||||
|
||||
### Override Serialized Property Name
|
||||
The default property names can be overridden by applying the `[JsonPropertyName]` attribute to desired properties.
|
||||
|
||||
Generally, this isn't going to be necessary for types you're persisting to the actor state as you're not intended to read or write them independent of Dapr-associated functionality, but
|
||||
the following is provided just to clearly illustrate that it's possible.
|
||||
|
||||
#### Override Property Names on Classes
|
||||
Here's an example demonstrating the use of `JsonPropertyName` to change the name for the first property following serialization. Note that the last usage of `JsonPropertyName` on the `Count` property
|
||||
matches what it would be expected to serialize to. This is largely just to demonstrate that applying this attribute won't negatively impact anything - in fact, it might be preferable if you later
|
||||
decide to change the default serialization options but still need to consistently access the properties previously serialized before that change as `JsonPropertyName` will override those options.
|
||||
|
||||
```csharp
|
||||
public class Doodad
|
||||
{
|
||||
[JsonPropertyName("identifier")]
|
||||
public Guid Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
[JsonPropertyName("count")]
|
||||
public int Count { get; set; }
|
||||
}
|
||||
```
|
||||
|
||||
This would serialize to the following:
|
||||
|
||||
```json
|
||||
{"identifier": "a06ced64-4f42-48ad-84dd-46ae6a7e333d", "name": "DoodadName", "count": 5}
|
||||
```
|
||||
|
||||
#### Override Property Names on Records
|
||||
Let's try doing the same thing with a record from C# 12 or later:
|
||||
|
||||
```csharp
|
||||
public record Thingy(string Name, [JsonPropertyName("count")] int Count);
|
||||
```
|
||||
|
||||
Because the argument passed in a primary constructor (introduced in C# 12) can be applied to either a property or field within a record, using the `[JsonPropertyName]` attribute may
|
||||
require specifying that you intend the attribute to apply to a property and not a field in some ambiguous cases. Should this be necessary, you'd indicate as much in the primary constructor with:
|
||||
|
||||
```csharp
|
||||
public record Thingy(string Name, [property: JsonPropertyName("count")] int Count);
|
||||
```
|
||||
|
||||
If `[property: ]` is applied to the `[JsonPropertyName]` attribute where it's not necessary, it will not negatively impact serialization or deserialization as the operation will
|
||||
proceed normally as though it were a property (as it typically would if not marked as such).
|
||||
|
||||
### Enumeration types
|
||||
Enumerations, including flat enumerations are serializable to JSON, but the value persisted may surprise you. Again, it's not expected that the developer should ever engage
|
||||
with the serialized data independently of Dapr, but the following information may at least help in diagnosing why a seemingly mild version migration isn't working as expected.
|
||||
|
||||
Take the following `enum` type providing the various seasons in the year:
|
||||
|
||||
```csharp
|
||||
public enum Season
|
||||
{
|
||||
Spring,
|
||||
Summer,
|
||||
Fall,
|
||||
Winter
|
||||
}
|
||||
```
|
||||
|
||||
We'll go ahead and use a separate demonstration type that references our `Season` and simultaneously illustrate how this works with records:
|
||||
|
||||
```csharp
|
||||
public record Engagement(string Name, Season TimeOfYear);
|
||||
```
|
||||
|
||||
Given the following initialized instance:
|
||||
|
||||
```csharp
|
||||
var myEngagement = new Engagement("Ski Trip", Season.Winter);
|
||||
```
|
||||
|
||||
This would serialize to the following JSON:
|
||||
```json
|
||||
{"name": "Ski Trip", "season": 3}
|
||||
```
|
||||
|
||||
That might be unexpected that our `Season.Winter` value was represented as a `3`, but this is because the serializer is going to automatically use numeric representations
|
||||
of the enum values starting with zero for the first value and incrementing the numeric value for each additional value available. Again, if a migration were taking place and
|
||||
a developer had flipped the order of the enums, this would affect a breaking change in your solution as the serialized numeric values would point to different values when deserialized.
|
||||
|
||||
Rather, there is a `JsonConverter` available with `System.Text.Json` that will instead opt to use a string-based value instead of the numeric value. The `[JsonConverter]` attribute needs
|
||||
to be applied to be enum type itself to enable this, but will then be realized in any downstream serialization or deserialization operation that references the enum.
|
||||
|
||||
```csharp
|
||||
[JsonConverter(typeof(JsonStringEnumConverter<Season>))]
|
||||
public enum Season
|
||||
{
|
||||
Spring,
|
||||
Summer,
|
||||
Fall,
|
||||
Winter
|
||||
}
|
||||
```
|
||||
|
||||
Using the same values from our `myEngagement` instance above, this would produce the following JSON instead:
|
||||
|
||||
```json
|
||||
{"name": "Ski Trip", "season": "Winter"}
|
||||
```
|
||||
|
||||
As a result, the enum members can be shifted around without fear of introducing errors during deserialization.
|
||||
|
||||
#### Custom Enumeration Values
|
||||
|
||||
The System.Text.Json serialization platform doesn't, out of the box, support the use of `[EnumMember]` to allow you to change the value of enum that's used during serialization or deserialization, but
|
||||
there are scenarios where this could be useful. Again, assume that you're tasking with refactoring the solution to apply some better names to your various
|
||||
enums. You're using the `JsonStringEnumConverter<TType>` detailed above so you're saving the name of the enum to value instead of a numeric value, but if you change
|
||||
the enum name, that will introduce a breaking change as the name will no longer match what's in state.
|
||||
|
||||
Do note that if you opt into using this approach, you should decorate all your enum members with the `[EnumMeber]` attribute so that the values are consistently applied for each enum value instead
|
||||
of haphazardly. Nothing will validate this at build or runtime, but it is considered a best practice operation.
|
||||
|
||||
How can you specify the precise value persisted while still changing the name of the enum member in this scenario? Use a custom `JsonConverter` with an extension method that can pull the value
|
||||
out of the attached `[EnumMember]` attributes where provided. Add the following to your solution:
|
||||
|
||||
```csharp
|
||||
public sealed class EnumMemberJsonConverter<T> : JsonConverter<T> where T : struct, Enum
|
||||
{
|
||||
/// <summary>Reads and converts the JSON to type <typeparamref name="T" />.</summary>
|
||||
/// <param name="reader">The reader.</param>
|
||||
/// <param name="typeToConvert">The type to convert.</param>
|
||||
/// <param name="options">An object that specifies serialization options to use.</param>
|
||||
/// <returns>The converted value.</returns>
|
||||
public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
// Get the string value from the JSON reader
|
||||
var value = reader.GetString();
|
||||
|
||||
// Loop through all the enum values
|
||||
foreach (var enumValue in Enum.GetValues<T>())
|
||||
{
|
||||
// Get the value from the EnumMember attribute, if any
|
||||
var enumMemberValue = GetValueFromEnumMember(enumValue);
|
||||
|
||||
// If the values match, return the enum value
|
||||
if (value == enumMemberValue)
|
||||
{
|
||||
return enumValue;
|
||||
}
|
||||
}
|
||||
|
||||
// If no match found, throw an exception
|
||||
throw new JsonException($"Invalid value for {typeToConvert.Name}: {value}");
|
||||
}
|
||||
|
||||
/// <summary>Writes a specified value as JSON.</summary>
|
||||
/// <param name="writer">The writer to write to.</param>
|
||||
/// <param name="value">The value to convert to JSON.</param>
|
||||
/// <param name="options">An object that specifies serialization options to use.</param>
|
||||
public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options)
|
||||
{
|
||||
// Get the value from the EnumMember attribute, if any
|
||||
var enumMemberValue = GetValueFromEnumMember(value);
|
||||
|
||||
// Write the value to the JSON writer
|
||||
writer.WriteStringValue(enumMemberValue);
|
||||
}
|
||||
|
||||
private static string GetValueFromEnumMember(T value)
|
||||
{
|
||||
MemberInfo[] member = typeof(T).GetMember(value.ToString(), BindingFlags.DeclaredOnly | BindingFlags.Static | BindingFlags.Public);
|
||||
if (member.Length == 0)
|
||||
return value.ToString();
|
||||
object[] customAttributes = member.GetCustomAttributes(typeof(EnumMemberAttribute), false);
|
||||
if (customAttributes.Length != 0)
|
||||
{
|
||||
EnumMemberAttribute enumMemberAttribute = (EnumMemberAttribute)customAttributes;
|
||||
if (enumMemberAttribute != null && enumMemberAttribute.Value != null)
|
||||
return enumMemberAttribute.Value;
|
||||
}
|
||||
return value.ToString();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Now let's add a sample enumerator. We'll set a value that uses the lower-case version of each enum member to demonstrate this. Don't forget to decorate the enum with the `JsonConverter`
|
||||
attribute and reference our custom converter in place of the numeral-to-string converter used in the last section.
|
||||
|
||||
```csharp
|
||||
[JsonConverter(typeof(EnumMemberJsonConverter<Season>))]
|
||||
public enum Season
|
||||
{
|
||||
[EnumMember(Value="spring")]
|
||||
Spring,
|
||||
[EnumMember(Value="summer")]
|
||||
Summer,
|
||||
[EnumMember(Value="fall")]
|
||||
Fall,
|
||||
[EnumMember(Value="winter")]
|
||||
Winter
|
||||
}
|
||||
```
|
||||
|
||||
Let's use our sample record from before. We'll also add a `[JsonPropertyName]` attribute just to augment the demonstration:
|
||||
```csharp
|
||||
public record Engagement([property: JsonPropertyName("event")] string Name, Season TimeOfYear);
|
||||
```
|
||||
|
||||
And finally, let's initialize a new instance of this:
|
||||
|
||||
```csharp
|
||||
var myEngagement = new Engagement("Conference", Season.Fall);
|
||||
```
|
||||
|
||||
This time, serialization will take into account the values from the attached `[EnumMember]` attribute providing us a mechanism to refactor our application without necessitating
|
||||
a complex versioning scheme for our existing enum values in the state.
|
||||
|
||||
```json
|
||||
{"event": "Conference", "season": "fall"}
|
||||
```
|
||||
|
||||
## Strongly-typed Dapr Actor client
|
||||
In this section, you will learn how to configure your classes and records so they are properly serialized and deserialized at runtime when using a strongly-typed actor client. These clients are implemented using .NET interfaces and are <u>not</u> compatible with Dapr Actors written using other languages.
|
||||
|
||||
This actor client serializes data using an engine called the [Data Contract Serializer](https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/serializable-types) which converts your C# types to and from XML documents. 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).
|
||||
|
||||
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.
|
||||
|
||||
### 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
|
||||
|
@ -23,14 +271,14 @@ There are several important considerations to keep in mind when using the Data C
|
|||
- 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?
|
||||
### 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
|
||||
#### .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.
|
||||
|
@ -153,7 +401,7 @@ When this is serialized, because we're changing the names of the serialized memb
|
|||
</Doodad>
|
||||
```
|
||||
|
||||
#### Classes in C# 12 - Primary Constructors
|
||||
##### 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.
|
||||
|
@ -198,7 +446,7 @@ public class Doodad(ILogger<Doodad> _logger)
|
|||
}
|
||||
```
|
||||
|
||||
### .NET Structs
|
||||
#### .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
|
||||
|
@ -210,7 +458,7 @@ public struct Doodad
|
|||
}
|
||||
```
|
||||
|
||||
### .NET Records
|
||||
#### .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:
|
||||
|
@ -238,7 +486,7 @@ public record Doodad(
|
|||
[property: DataMember] int Count)
|
||||
```
|
||||
|
||||
### Supported Primitive Types
|
||||
#### 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)
|
||||
|
@ -267,7 +515,7 @@ There are additional types that aren't actually primitives but have similar buil
|
|||
|
||||
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
|
||||
#### 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.
|
||||
|
@ -283,15 +531,15 @@ public enum Colors
|
|||
}
|
||||
```
|
||||
|
||||
### Collection Types
|
||||
#### 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<TKey, TValue>](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
|
||||
#### 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
|
||||
#### 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.
|
||||
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
---
|
||||
type: docs
|
||||
title: "Dapr AI .NET SDK"
|
||||
linkTitle: "AI"
|
||||
weight: 50000
|
||||
description: Get up and running with the Dapr AI .NET SDK
|
||||
---
|
||||
|
||||
With the Dapr AI package, you can interact with the Dapr AI workloads from a .NET application.
|
||||
|
||||
Today, Dapr provides the Conversational API to engage with large language models. To get started with this workload,
|
||||
walk through the [Dapr Conversational AI]({{< ref dotnet-ai-conversation-howto.md >}}) how-to guide.
|
|
@ -0,0 +1,90 @@
|
|||
---
|
||||
type: docs
|
||||
title: "How to: Create and use Dapr AI Conversations in the .NET SDK"
|
||||
linkTitle: "How to: Use the AI Conversations client"
|
||||
weight: 500100
|
||||
description: Learn how to create and use the Dapr Conversational AI client using the .NET SDK
|
||||
---
|
||||
|
||||
## Prerequisites
|
||||
- [.NET 6](https://dotnet.microsoft.com/download/dotnet/6.0), [.NET 8](https://dotnet.microsoft.com/download/dotnet/8.0), or [.NET 9](https://dotnet.microsoft.com/download/dotnet/9.0) installed
|
||||
- [Dapr CLI](https://docs.dapr.io/getting-started/install-dapr-cli/)
|
||||
- [Initialized Dapr environment](https://docs.dapr.io/getting-started/install-dapr-selfhost)
|
||||
|
||||
{{% alert title="Note" color="primary" %}}
|
||||
|
||||
.NET 6 is supported as the minimum required for the Dapr .NET SDK packages in this release. Only .NET 8 and .NET 9
|
||||
will be supported in Dapr v1.16 and later releases.
|
||||
|
||||
{{% /alert %}}
|
||||
|
||||
## Installation
|
||||
|
||||
To get started with the Dapr AI .NET SDK client, install the [Dapr.AI package](https://www.nuget.org/packages/Dapr.AI) from NuGet:
|
||||
```sh
|
||||
dotnet add package Dapr.AI
|
||||
```
|
||||
|
||||
A `DaprConversationClient` maintains access to networking resources in the form of TCP sockets used to communicate with the Dapr sidecar.
|
||||
|
||||
### Dependency Injection
|
||||
|
||||
The `AddDaprAiConversation()` method will register the Dapr client ASP.NET Core dependency injection and is the recommended approach
|
||||
for using this package. This method accepts an optional options delegate for configuring the `DaprConversationClient` and a
|
||||
`ServiceLifetime` argument, allowing you to specify a different lifetime for the registered services instead of the default `Singleton`
|
||||
value.
|
||||
|
||||
The following example assumes all default values are acceptable and is sufficient to register the `DaprConversationClient`:
|
||||
|
||||
```csharp
|
||||
services.AddDaprAiConversation();
|
||||
```
|
||||
|
||||
The optional configuration delegate is used to configure the `DaprConversationClient` by specifying options on the
|
||||
`DaprConversationClientBuilder` as in the following example:
|
||||
```csharp
|
||||
services.AddSingleton<DefaultOptionsProvider>();
|
||||
services.AddDaprAiConversation((serviceProvider, clientBuilder) => {
|
||||
//Inject a service to source a value from
|
||||
var optionsProvider = serviceProvider.GetRequiredService<DefaultOptionsProvider>();
|
||||
var standardTimeout = optionsProvider.GetStandardTimeout();
|
||||
|
||||
//Configure the value on the client builder
|
||||
clientBuilder.UseTimeout(standardTimeout);
|
||||
});
|
||||
```
|
||||
|
||||
### Manual Instantiation
|
||||
Rather than using dependency injection, a `DaprConversationClient` can also be built using the static client builder.
|
||||
|
||||
For best performance, create a single long-lived instance of `DaprConversationClient` and provide access to that shared instance throughout
|
||||
your application. `DaprConversationClient` instances are thread-safe and intended to be shared.
|
||||
|
||||
Avoid creating a `DaprConversationClient` per-operation.
|
||||
|
||||
A `DaprConversationClient` can be configured by invoking methods on the `DaprConversationClientBuilder` class before calling `.Build()`
|
||||
to create the client. The settings for each `DaprConversationClient` are separate and cannot be changed after calling `.Build()`.
|
||||
|
||||
```csharp
|
||||
var daprConversationClient = new DaprConversationClientBuilder()
|
||||
.UseJsonSerializerSettings( ... ) //Configure JSON serializer
|
||||
.Build();
|
||||
```
|
||||
|
||||
See the .NET [documentation here]({{< ref dotnet-client >}}) for more information about the options available when configuring the Dapr client via the builder.
|
||||
|
||||
## Try it out
|
||||
Put the Dapr AI .NET SDK to the test. Walk through the samples to see Dapr in action:
|
||||
|
||||
| SDK Samples | Description |
|
||||
| ----------- | ----------- |
|
||||
| [SDK samples](https://github.com/dapr/dotnet-sdk/tree/master/examples) | Clone the SDK repo to try out some examples and get started. |
|
||||
|
||||
## Building Blocks
|
||||
|
||||
This part of the .NET SDK allows you to interface with the Conversations API to send and receive messages from
|
||||
large language models.
|
||||
|
||||
### Send messages
|
||||
|
||||
|
|
@ -0,0 +1,135 @@
|
|||
---
|
||||
type: docs
|
||||
title: "Dapr AI Client"
|
||||
linkTitle: "AI client"
|
||||
weight: 50005
|
||||
description: Learn how to create Dapr AI clients
|
||||
---
|
||||
|
||||
The Dapr AI client package allows you to interact with the AI capabilities provided by the Dapr sidecar.
|
||||
|
||||
## Lifetime management
|
||||
A `DaprConversationClient` is a version of the Dapr client that is dedicated to interacting with the Dapr Conversation
|
||||
API. It can be registered alongside a `DaprClient` and other Dapr clients without issue.
|
||||
|
||||
It maintains access to networking resources in the form of TCP sockets used to communicate with the Dapr sidecar.
|
||||
|
||||
For best performance, create a single long-lived instance of `DaprConversationClient` and provide access to that shared
|
||||
instance throughout your application. `DaprConversationClient` instances are thread-safe and intended to be shared.
|
||||
|
||||
This can be aided by utilizing the dependency injection functionality. The registration method supports registration using
|
||||
as a singleton, a scoped instance or as transient (meaning it's recreated every time it's injected), but also enables
|
||||
registration to utilize values from an `IConfiguration` or other injected service in a way that's impractical when
|
||||
creating the client from scratch in each of your classes.
|
||||
|
||||
Avoid creating a `DaprConversationClient` for each operation.
|
||||
|
||||
## Configuring DaprConversationClient via DaprConversationClientBuilder
|
||||
|
||||
A `DaprConversationClient` can be configured by invoking methods on the `DaprConversationClientBuilder` class before
|
||||
calling `.Build()` to create the client itself. The settings for each `DaprConversationClient` are separate
|
||||
and cannot be changed after calling `.Build()`.
|
||||
|
||||
```cs
|
||||
var daprConversationClient = new DaprConversationClientBuilder()
|
||||
.UseDaprApiToken("abc123") // Specify the API token used to authenticate to other Dapr sidecars
|
||||
.Build();
|
||||
```
|
||||
|
||||
The `DaprConversationClientBuilder` contains settings for:
|
||||
|
||||
- The HTTP endpoint of the Dapr sidecar
|
||||
- The gRPC endpoint of the Dapr sidecar
|
||||
- The `JsonSerializerOptions` object used to configure JSON serialization
|
||||
- The `GrpcChannelOptions` object used to configure gRPC
|
||||
- The API token used to authenticate requests to the sidecar
|
||||
- The factory method used to create the `HttpClient` instance used by the SDK
|
||||
- The timeout used for the `HttpClient` instance when making requests to the sidecar
|
||||
|
||||
The SDK will read the following environment variables to configure the default values:
|
||||
|
||||
- `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
|
||||
|
||||
Dapr's use of `CancellationToken` for cancellation relies on the configuration of the gRPC channel options. If you need
|
||||
to configure these options yourself, make sure to enable the [ThrowOperationCanceledOnCancellation setting](https://grpc.github.io/grpc/csharp-dotnet/api/Grpc.Net.Client.GrpcChannelOptions.html#Grpc_Net_Client_GrpcChannelOptions_ThrowOperationCanceledOnCancellation).
|
||||
|
||||
```cs
|
||||
var daprConversationClient = new DaprConversationClientBuilder()
|
||||
.UseGrpcChannelOptions(new GrpcChannelOptions { ... ThrowOperationCanceledOnCancellation = true })
|
||||
.Build();
|
||||
```
|
||||
|
||||
## Using cancellation with `DaprConversationClient`
|
||||
|
||||
The APIs on `DaprConversationClient` perform asynchronous operations and accept an optional `CancellationToken` parameter. This
|
||||
follows a standard .NET practice for cancellable operations. Note that when cancellation occurs, there is no guarantee that
|
||||
the remote endpoint stops processing the request, only that the client has stopped waiting for completion.
|
||||
|
||||
When an operation is cancelled, it will throw an `OperationCancelledException`.
|
||||
|
||||
## Configuring `DaprConversationClient` via dependency injection
|
||||
|
||||
Using the built-in extension methods for registering the `DaprConversationClient` in a dependency injection container can
|
||||
provide the benefit of registering the long-lived service a single time, centralize complex configuration and improve
|
||||
performance by ensuring similarly long-lived resources are re-purposed when possible (e.g. `HttpClient` instances).
|
||||
|
||||
There are three overloads available to give the developer the greatest flexibility in configuring the client for their
|
||||
scenario. Each of these will register the `IHttpClientFactory` on your behalf if not already registered, and configure
|
||||
the `DaprConversationClientBuilder` to use it when creating the `HttpClient` instance in order to re-use the same instance as
|
||||
much as possible and avoid socket exhaustion and other issues.
|
||||
|
||||
In the first approach, there's no configuration done by the developer and the `DaprConversationClient` is configured with the
|
||||
default settings.
|
||||
|
||||
```cs
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
builder.Services.AddDaprConversationClient(); //Registers the `DaprConversationClient` to be injected as needed
|
||||
var app = builder.Build();
|
||||
```
|
||||
|
||||
Sometimes the developer will need to configure the created client using the various configuration options detailed
|
||||
above. This is done through an overload that passes in the `DaprConversationClientBuiler` and exposes methods for configuring
|
||||
the necessary options.
|
||||
|
||||
```cs
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
builder.Services.AddDaprConversationClient((_, daprConversationClientBuilder) => {
|
||||
//Set the API token
|
||||
daprConversationClientBuilder.UseDaprApiToken("abc123");
|
||||
//Specify a non-standard HTTP endpoint
|
||||
daprConversationClientBuilder.UseHttpEndpoint("http://dapr.my-company.com");
|
||||
});
|
||||
|
||||
var app = builder.Build();
|
||||
```
|
||||
|
||||
Finally, it's possible that the developer may need to retrieve information from another service in order to populate
|
||||
these configuration values. That value may be provided from a `DaprClient` instance, a vendor-specific SDK or some
|
||||
local service, but as long as it's also registered in DI, it can be injected into this configuration operation via the
|
||||
last overload:
|
||||
|
||||
```cs
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
//Register a fictional service that retrieves secrets from somewhere
|
||||
builder.Services.AddSingleton<SecretService>();
|
||||
|
||||
builder.Services.AddDaprConversationClient((serviceProvider, daprConversationClientBuilder) => {
|
||||
//Retrieve an instance of the `SecretService` from the service provider
|
||||
var secretService = serviceProvider.GetRequiredService<SecretService>();
|
||||
var daprApiToken = secretService.GetSecret("DaprApiToken").Value;
|
||||
|
||||
//Configure the `DaprConversationClientBuilder`
|
||||
daprConversationClientBuilder.UseDaprApiToken(daprApiToken);
|
||||
});
|
||||
|
||||
var app = builder.Build();
|
||||
```
|
|
@ -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<Account>(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
|
||||
|
@ -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 %}}
|
||||
|
||||
|
@ -257,26 +282,6 @@ namespace LockService
|
|||
}
|
||||
```
|
||||
|
||||
### Manage workflow instances (Alpha)
|
||||
|
||||
```csharp
|
||||
var daprClient = new DaprClientBuilder().Build();
|
||||
|
||||
string instanceId = "MyWorkflowInstance1";
|
||||
string workflowComponentName = "dapr"; // alternatively, this could be the name of a workflow component defined in yaml
|
||||
string workflowName = "MyWorkflowDefinition";
|
||||
var input = new { name = "Billy", age = 30 }; // Any JSON-serializable value is OK
|
||||
|
||||
// Start workflow
|
||||
var startResponse = await daprClient.StartWorkflowAsync(instanceId, workflowComponentName, workflowName, input);
|
||||
|
||||
// Terminate workflow
|
||||
await daprClient.TerminateWorkflowAsync(instanceId, workflowComponentName);
|
||||
|
||||
// Get workflow metadata
|
||||
var getResponse = await daprClient.GetWorkflowAsync(instanceId, workflowComponentName, workflowName);
|
||||
```
|
||||
|
||||
## Sidecar APIs
|
||||
### Sidecar Health
|
||||
The .NET SDK provides a way to poll for the sidecar health, as well as a convenience method to wait for the sidecar to be ready.
|
||||
|
|
|
@ -10,6 +10,48 @@ description: Essential tips and advice for using DaprClient
|
|||
|
||||
A `DaprClient` holds access to networking resources in the form of TCP sockets used to communicate with the Dapr sidecar. `DaprClient` implements `IDisposable` to support eager cleanup of resources.
|
||||
|
||||
### Dependency Injection
|
||||
|
||||
The `AddDaprClient()` method will register the Dapr client with ASP.NET Core dependency injection. This method accepts an optional
|
||||
options delegate for configuring the `DaprClient` and an `ServiceLifetime` argument, allowing you to specify a different lifetime
|
||||
for the registered resources instead of the default `Singleton` value.
|
||||
|
||||
The following example assumes all default values are acceptable and is sufficient to register the `DaprClient`.
|
||||
|
||||
```csharp
|
||||
services.AddDaprClient();
|
||||
```
|
||||
|
||||
The optional configuration delegates are used to configure `DaprClient` by specifying options on the provided `DaprClientBuilder`
|
||||
as in the following example:
|
||||
|
||||
```csharp
|
||||
services.AddDaprClient(daprBuilder => {
|
||||
daprBuilder.UseJsonSerializerOptions(new JsonSerializerOptions {
|
||||
WriteIndented = true,
|
||||
MaxDepth = 8
|
||||
});
|
||||
daprBuilder.UseTimeout(TimeSpan.FromSeconds(30));
|
||||
});
|
||||
```
|
||||
|
||||
The another optional configuration delegate overload provides access to both the `DaprClientBuilder` as well as an `IServiceProvider`
|
||||
allowing for more advanced configurations that may require injecting services from the dependency injection container.
|
||||
|
||||
```csharp
|
||||
services.AddSingleton<SampleService>();
|
||||
services.AddDaprClient((serviceProvider, daprBuilder) => {
|
||||
var sampleService = serviceProvider.GetRequiredService<SampleService>();
|
||||
var timeoutValue = sampleService.TimeoutOptions;
|
||||
|
||||
daprBuilder.UseTimeout(timeoutValue);
|
||||
});
|
||||
```
|
||||
|
||||
### Manual Instantiation
|
||||
|
||||
Rather than using dependency injection, a `DaprClient` can also be built using the static client builder.
|
||||
|
||||
For best performance, create a single long-lived instance of `DaprClient` and provide access to that shared instance throughout your application. `DaprClient` instances are thread-safe and intended to be shared.
|
||||
|
||||
Avoid creating a `DaprClient` per-operation and disposing it when the operation is complete.
|
||||
|
@ -24,13 +66,41 @@ var daprClient = new DaprClientBuilder()
|
|||
.Build();
|
||||
```
|
||||
|
||||
The `DaprClientBuilder` contains settings for:
|
||||
By default, the `DaprClientBuilder` will prioritize the following locations, in the following order, to source the configuration
|
||||
values:
|
||||
|
||||
- The HTTP endpoint of the Dapr sidecar
|
||||
- The gRPC endpoint of the Dapr sidecar
|
||||
- The `JsonSerializerOptions` object used to configure JSON serialization
|
||||
- The `GrpcChannelOptions` object used to configure gRPC
|
||||
- The API Token used to authenticate requests to the sidecar
|
||||
- The value provided to a method on the `DaprClientBuilder` (e.g. `UseTimeout(TimeSpan.FromSeconds(30))`)
|
||||
- The value pulled from an optionally injected `IConfiguration` matching the name expected in the associated environment variable
|
||||
- The value pulled from the associated environment variable
|
||||
- Default values
|
||||
|
||||
### Configuring on `DaprClientBuilder`
|
||||
|
||||
The `DaprClientBuilder` contains the following methods to set configuration options:
|
||||
|
||||
- `UseHttpEndpoint(string)`: The HTTP endpoint of the Dapr sidecar
|
||||
- `UseGrpcEndpoint(string)`: Sets the gRPC endpoint of the Dapr sidecar
|
||||
- `UseGrpcChannelOptions(GrpcChannelOptions)`: Sets the gRPC channel options used to connect to the Dapr sidecar
|
||||
- `UseHttpClientFactory(IHttpClientFactory)`: Configures the DaprClient to use a registered `IHttpClientFactory` when building `HttpClient` instances
|
||||
- `UseJsonSerializationOptions(JsonSerializerOptions)`: Used to configure JSON serialization
|
||||
- `UseDaprApiToken(string)`: Adds the provided token to every request to authenticate to the Dapr sidecar
|
||||
- `UseTimeout(TimeSpan)`: Specifies a timeout value used by the `HttpClient` when communicating with the Dapr sidecar
|
||||
|
||||
### Configuring From `IConfiguration`
|
||||
Rather than rely on sourcing configuration values directly from environment variables or because the values are sourced
|
||||
from dependency injected services, another options is to make these values available on `IConfiguration`.
|
||||
|
||||
For example, you might be registering your application in a multi-tenant environment and need to prefix the environment
|
||||
variables used. The following example shows how these values can be sourced from the environment variables to your
|
||||
`IConfiguration` when their keys are prefixed with `test_`;
|
||||
|
||||
```csharp
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
builder.Configuration.AddEnvironmentVariables("test_"); //Retrieves all environment variables that start with "test_" and removes the prefix when sourced from IConfiguration
|
||||
builder.Services.AddDaprClient();
|
||||
```
|
||||
|
||||
### Configuring From Environment Variables
|
||||
|
||||
The SDK will read the following environment variables to configure the default values:
|
||||
|
||||
|
@ -40,9 +110,14 @@ The SDK will read the following environment variables to configure the default v
|
|||
- `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
|
||||
|
||||
{{% alert title="Note" color="primary" %}}
|
||||
If both `DAPR_HTTP_ENDPOINT` and `DAPR_HTTP_PORT` are specified, the port value from `DAPR_HTTP_PORT` will be ignored in favor of the port
|
||||
implicitly or explicitly defined on `DAPR_HTTP_ENDPOINT`. The same is true of both `DAPR_GRPC_ENDPOINT` and `DAPR_GRPC_PORT`.
|
||||
{{% /alert %}}
|
||||
|
||||
### Configuring gRPC channel options
|
||||
|
||||
Dapr's use of `CancellationToken` for cancellation relies on the configuration of the gRPC channel options. If you need to configure these options yourself, make sure to enable the [ThrowOperationCanceledOnCancellation setting](https://grpc.github.io/grpc/csharp-dotnet/api/Grpc.Net.Client.GrpcChannelOptions.html#Grpc_Net_Client_GrpcChannelOptions_ThrowOperationCanceledOnCancellation).
|
||||
Dapr's use of `CancellationToken` for cancellation relies on the configuration of the gRPC channel options and this is enabled by default. If you need to configure these options yourself, make sure to enable the [ThrowOperationCanceledOnCancellation setting](https://grpc.github.io/grpc/csharp-dotnet/api/Grpc.Net.Client.GrpcChannelOptions.html#Grpc_Net_Client_GrpcChannelOptions_ThrowOperationCanceledOnCancellation).
|
||||
|
||||
```C#
|
||||
var daprClient = new DaprClientBuilder()
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
type: docs
|
||||
title: "Developing applications with the Dapr .NET SDK"
|
||||
linkTitle: "Dev integrations"
|
||||
weight: 50000
|
||||
weight: 100000
|
||||
description: Learn about local development integration options for .NET Dapr applications
|
||||
---
|
||||
|
||||
|
|
|
@ -0,0 +1,138 @@
|
|||
---
|
||||
type: docs
|
||||
title: "Dapr .NET SDK Development with .NET Aspire"
|
||||
linkTitle: ".NET Aspire"
|
||||
weight: 40000
|
||||
description: Learn about local development with .NET Aspire
|
||||
---
|
||||
|
||||
# .NET Aspire
|
||||
|
||||
[.NET Aspire](https://learn.microsoft.com/en-us/dotnet/aspire/get-started/aspire-overview) is a development tool
|
||||
designed to make it easier to include external software into .NET applications by providing a framework that allows
|
||||
third-party services to be readily integrated, observed and provisioned alongside your own software.
|
||||
|
||||
Aspire simplifies local development by providing rich integration with popular IDEs including
|
||||
[Microsoft Visual Studio](https://visualstudio.microsoft.com/vs/),
|
||||
[Visual Studio Code](https://code.visualstudio.com/),
|
||||
[JetBrains Rider](https://blog.jetbrains.com/dotnet/2024/02/19/jetbrains-rider-and-the-net-aspire-plugin/) and others
|
||||
to launch your application with the debugger while automatically launching and provisioning access to other
|
||||
integrations as well, including Dapr.
|
||||
|
||||
While Aspire also assists with deployment of your application to various cloud hosts like Microsoft Azure and
|
||||
Amazon AWS, deployment is currently outside the scope of this guide. More information can be found in Aspire's
|
||||
documentation [here](https://learn.microsoft.com/en-us/dotnet/aspire/deployment/overview).
|
||||
|
||||
## Prerequisites
|
||||
- While the Dapr .NET SDK is compatible with [.NET 6](https://dotnet.microsoft.com/download/dotnet/6.0),
|
||||
[.NET 8](https://dotnet.microsoft.com/download/dotnet/8.0) or [.NET 9](https://dotnet.microsoft.com/download/dotnet/9.0),
|
||||
.NET Aspire is only compatible with [.NET 8](https://dotnet.microsoft.com/download/dotnet/8.0) or
|
||||
[.NET 9](https://dotnet.microsoft.com/download/dotnet/9.0).
|
||||
- An OCI compliant container runtime such as [Docker Desktop](https://www.docker.com/products/docker-desktop) or
|
||||
[Podman](https://podman.io/)
|
||||
- Install and initialize Dapr v1.13 or later
|
||||
|
||||
## Using .NET Aspire via CLI
|
||||
|
||||
We'll start by creating a brand new .NET application. Open your preferred CLI and navigate to the directory you wish
|
||||
to create your new .NET solution within. Start by using the following command to install a template that will create
|
||||
an empty Aspire application:
|
||||
|
||||
```sh
|
||||
dotnet new install Aspire.ProjectTemplates
|
||||
```
|
||||
|
||||
Once that's installed, proceed to create an empty .NET Aspire application in your current directory. The `-n` argument
|
||||
allows you to specify the name of the output solution. If it's excluded, the .NET CLI will instead use the name
|
||||
of the output directory, e.g. `C:\source\aspiredemo` will result in the solution being named `aspiredemo`. The rest
|
||||
of this tutorial will assume a solution named `aspiredemo`.
|
||||
|
||||
```sh
|
||||
dotnet new aspire -n aspiredemo
|
||||
```
|
||||
|
||||
This will create two Aspire-specific directories and one file in your directory:
|
||||
- `aspiredemo.AppHost/` contains the Aspire orchestration project that is used to configure each of the integrations
|
||||
used in your application(s).
|
||||
- `aspiredemo.ServiceDefaults/` contains a collection of extensions meant to be shared across your solution to aid in
|
||||
resilience, service discovery and telemetry capabilities offered by Aspire (these are distinct from the capabilities
|
||||
offered in Dapr itself).
|
||||
- `aspiredemo.sln` is the file that maintains the layout of your current solution
|
||||
|
||||
We'll next create a project that'll serve as our Dapr application. From the same directory, use the following
|
||||
to create an empty ASP.NET Core project called `MyApp`. This will be created relative to your current directory in
|
||||
`MyApp\MyApp.csproj`.
|
||||
|
||||
```sh
|
||||
dotnet new web MyApp
|
||||
```
|
||||
|
||||
Next we'll configure the AppHost project to add the necessary package to support local Dapr development. Navigate
|
||||
into the AppHost directory with the following and install the `Aspire.Hosting.Dapr` package from NuGet into the project.
|
||||
We'll also add a reference to our `MyApp` project so we can reference it during the registration process.
|
||||
|
||||
```sh
|
||||
cd aspiredemo.AppHost
|
||||
dotnet add package Aspire.Hosting.Dapr
|
||||
dotnet add reference ../MyApp/
|
||||
```
|
||||
|
||||
Next, we need to configure Dapr as a resource to be loaded alongside your project. Open the `Program.cs` file in that
|
||||
project within your preferred IDE. It should look similar to the following:
|
||||
|
||||
```csharp
|
||||
var builder = DistributedApplication.CreateBuilder(args);
|
||||
|
||||
builder.Build().Run();
|
||||
```
|
||||
|
||||
If you're familiar with the dependency injection approach used in ASP.NET Core projects or others utilizing the
|
||||
`Microsoft.Extensions.DependencyInjection` functionality, you'll find that this will be a familiar experience.
|
||||
|
||||
Because we've already added a project reference to `MyApp`, we need to start by adding a reference in this configuration
|
||||
as well. Add the following before the `builder.Build().Run()` line:
|
||||
|
||||
```csharp
|
||||
var myApp = builder
|
||||
.AddProject<Projects.MyApp>("myapp")
|
||||
.WithDaprSidecar();
|
||||
```
|
||||
|
||||
Because the project reference has been added to this solution, your project shows up as a type within the `Projects.`
|
||||
namespace for our purposes here. The name of the variable you assign the project to doesn't much matter in this tutorial
|
||||
but would be used if you wanted to create a reference between this project and another using Aspire's service discovery
|
||||
functionality.
|
||||
|
||||
Adding `.WithDaprSidecar()` configures Dapr as a .NET Aspire resource so that when the project runs, the sidecar will be
|
||||
deployed alongside your application. This accepts a number of different options and could optionally be configured as in
|
||||
the following example:
|
||||
|
||||
```csharp
|
||||
DaprSidecarOptions sidecarOptions = new()
|
||||
{
|
||||
AppId = "my-other-app",
|
||||
AppPort = 8080, //Note that this argument is required if you intend to configure pubsub, actors or workflows as of Aspire v9.0
|
||||
DaprGrpcPort = 50001,
|
||||
DaprHttpPort = 3500,
|
||||
MetricsPort = 9090
|
||||
};
|
||||
|
||||
builder
|
||||
.AddProject<Projects.MyOtherApp>("myotherapp")
|
||||
.WithReference(myApp)
|
||||
.WithDaprSidecar(sidecarOptions);
|
||||
```
|
||||
|
||||
{{% alert color="primary" %}}
|
||||
|
||||
As indicated in the example above, as of .NET Aspire 9.0, if you intend to use any functionality in which Dapr needs to
|
||||
call into your application such as pubsub, actors or workflows, you will need to specify your AppPort as
|
||||
a configured option as Aspire will not automatically pass it to Dapr at runtime. It's expected that this behavior will
|
||||
change in a future release as a fix has been merged and can be tracked [here](https://github.com/dotnet/aspire/pull/6362).
|
||||
|
||||
{{% /alert %}}
|
||||
|
||||
When you open the solution in your IDE, ensure that the `aspiredemo.AppHost` is configured as your startup project, but
|
||||
when you launch it in a debug configuration, you'll note that your integrated console should reflect your expected Dapr
|
||||
logs and it will be available to your application.
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
type: docs
|
||||
title: "Dapr .NET SDK Development with Docker-Compose"
|
||||
linkTitle: "Docker Compose"
|
||||
weight: 50000
|
||||
weight: 60000
|
||||
description: Learn about local development with Docker-Compose
|
||||
---
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
type: docs
|
||||
title: "Dapr .NET SDK Development with Project Tye"
|
||||
linkTitle: "Project Tye"
|
||||
weight: 40000
|
||||
weight: 50000
|
||||
description: Learn about local development with Project Tye
|
||||
---
|
||||
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
type: docs
|
||||
title: "Error Handling in the Dapr .NET SDK"
|
||||
linkTitle: "Error handling"
|
||||
weight: 90000
|
||||
description: Learn about error handling in the Dapr.NET SDK.
|
||||
---
|
|
@ -0,0 +1,140 @@
|
|||
---
|
||||
type: docs
|
||||
title: "Richer Error Model in the Dapr .NET SDK"
|
||||
linkTitle: "Richer error model"
|
||||
weight: 59000
|
||||
description: Learn how to use the richer error model in the .NET SDK.
|
||||
---
|
||||
|
||||
The Dapr .NET SDK supports the richer error model, implemented by the Dapr runtime. This model provides a way for applications to enrich their errors with added context,
|
||||
allowing consumers of the application to better understand the issue and resolve faster. You can read more about the richer error model [here](https://google.aip.dev/193), and you
|
||||
can find the Dapr proto file implementing these errors [here](https://github.com/googleapis/googleapis/blob/master/google/rpc/error_details.proto").
|
||||
|
||||
The Dapr .NET SDK implements all details supported by the Dapr runtime, implemented in the `Dapr.Common.Exceptions` namespace, and is accessible through
|
||||
the `DaprException` extension method `TryGetExtendedErrorInfo`. Currently this detail extraction is only supported for
|
||||
`RpcException`'s where the details are present.
|
||||
|
||||
```csharp
|
||||
// Example usage of ExtendedErrorInfo
|
||||
|
||||
try
|
||||
{
|
||||
// Perform some action with the Dapr client that throws a DaprException.
|
||||
}
|
||||
catch (DaprException daprEx)
|
||||
{
|
||||
if (daprEx.TryGetExtendedErrorInfo(out DaprExtendedErrorInfo errorInfo)
|
||||
{
|
||||
Console.WriteLine(errorInfo.Code);
|
||||
Console.WriteLine(errorInfo.Message);
|
||||
|
||||
foreach (DaprExtendedErrorDetail detail in errorInfo.Details)
|
||||
{
|
||||
Console.WriteLine(detail.ErrorType);
|
||||
switch (detail.ErrorType)
|
||||
case ExtendedErrorType.ErrorInfo:
|
||||
Console.WriteLine(detail.Reason);
|
||||
Console.WriteLine(detail.Domain);
|
||||
default:
|
||||
Console.WriteLine(detail.TypeUrl);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## DaprExtendedErrorInfo
|
||||
|
||||
Contains `Code` (the status code) and `Message` (the error message) associated with the error, parsed from an inner `RpcException`.
|
||||
Also contains a collection of `DaprExtendedErrorDetails` parsed from the details in the exception.
|
||||
|
||||
## DaprExtendedErrorDetail
|
||||
|
||||
All details implement the abstract `DaprExtendedErrorDetail` and have an associated `DaprExtendedErrorType`.
|
||||
|
||||
1. [RetryInfo](#retryinfo)
|
||||
|
||||
2. [DebugInfo](#debuginfo)
|
||||
|
||||
3. [QuotaFailure](#quotafailure)
|
||||
|
||||
4. [PreconditionFailure](#preconditionfailure)
|
||||
|
||||
5. [RequestInfo](#requestinfo)
|
||||
|
||||
6. [LocalizedMessage](#localizedmessage)
|
||||
|
||||
7. [BadRequest](#badrequest)
|
||||
|
||||
8. [ErrorInfo](#errorinfo)
|
||||
|
||||
9. [Help](#help)
|
||||
|
||||
10. [ResourceInfo](#resourceinfo)
|
||||
|
||||
11. [Unknown](#unknown)
|
||||
|
||||
## RetryInfo
|
||||
|
||||
Information telling the client how long to wait before they should retry. Provides a `DaprRetryDelay` with the properties
|
||||
`Second` (offset in seconds) and `Nano` (offset in nanoseconds).
|
||||
|
||||
## DebugInfo
|
||||
|
||||
Debugging information offered by the server. Contains `StackEntries` (a collection of strings containing the stack trace), and
|
||||
`Detail` (further debugging information).
|
||||
|
||||
## QuotaFailure
|
||||
|
||||
Information relating to some quota that may have been reached, such as a daily usage limit on an API. It has one property `Violations`,
|
||||
a collection of `DaprQuotaFailureViolation`, which each contain a `Subject` (the subject of the request) and `Description` (further information regarding the failure).
|
||||
|
||||
## PreconditionFailure
|
||||
|
||||
Information informing the client that some required precondition was not met. Has one property `Violations`, a collection of
|
||||
`DaprPreconditionFailureViolation`, which each has `Subject` (subject where the precondition failure occured e.g. "Azure"), `Type` (representation of the precondition type e.g. "TermsOfService"), and `Description` (further description e.g. "ToS must be accepted.").
|
||||
|
||||
## RequestInfo
|
||||
|
||||
Information returned by the server that can be used by the server to identify the clients request. Contains
|
||||
`RequestId` and `ServingData` properties, `RequestId` being some string (such as a UID) the server can interpret,
|
||||
and `ServingData` being some arbitrary data that made up part of the request.
|
||||
|
||||
## LocalizedMessage
|
||||
|
||||
Contains a localized message, along with the locale of the message. Contains `Locale` (the locale e.g. "en-US") and `Message` (the localized message).
|
||||
|
||||
## BadRequest
|
||||
|
||||
Describes a bad request field. Contains collection of `DaprBadRequestDetailFieldViolation`, which each has `Field` (the offending field in request e.g. 'first_name') and
|
||||
`Description` (further information detailing the reason e.g. "first_name cannot contain special characters").
|
||||
|
||||
## ErrorInfo
|
||||
|
||||
Details the cause of an error. Contains three properties, `Reason` (the reason for the error, which should take the form of UPPER_SNAKE_CASE e.g. DAPR_INVALID_KEY),
|
||||
`Domain` (domain the error belongs to e.g. 'dapr.io'), and `Metadata`, a key value based collection of futher information.
|
||||
|
||||
## Help
|
||||
|
||||
Provides resources for the client to perform further research into the issue. Contains a collection of `DaprHelpDetailLink`,
|
||||
which provides `Url` (a url to help or documentation), and `Description` (a description of what the link provides).
|
||||
|
||||
## ResourceInfo
|
||||
|
||||
Provides information relating to an accessed resource. Provides three properties `ResourceType` (type of the resource being access e.g. "Azure service bus"),
|
||||
`ResourceName` (The name of the resource e.g. "my-configured-service-bus"), `Owner` (the owner of the resource e.g. "subscriptionowner@dapr.io"),
|
||||
and `Description` (further information on the resource relating to the error e.g. "missing permissions to use this resource").
|
||||
|
||||
## Unknown
|
||||
|
||||
Returned when the detail type url cannot be mapped to the correct `DaprExtendedErrorDetail` implementation.
|
||||
Provides one property `TypeUrl` (the type url that could not be parsed e.g. "type.googleapis.com/Google.rpc.UnrecognizedType").
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
---
|
||||
type: docs
|
||||
title: "Dapr Jobs .NET SDK"
|
||||
linkTitle: "Jobs"
|
||||
weight: 50000
|
||||
description: Get up and running with Dapr Jobs and the Dapr .NET SDK
|
||||
---
|
||||
|
||||
With the Dapr Job package, you can interact with the Dapr Job APIs from a .NET application to trigger future operations
|
||||
to run according to a predefined schedule with an optional payload.
|
||||
|
||||
To get started, walk through the [Dapr Jobs]({{< ref dotnet-jobs-howto.md >}}) how-to guide and refer to
|
||||
[best practices documentation]({{< ref dotnet-jobsclient-usage.md >}}) for additional guidance.
|
|
@ -0,0 +1,394 @@
|
|||
---
|
||||
type: docs
|
||||
title: "How to: Author and manage Dapr Jobs in the .NET SDK"
|
||||
linkTitle: "How to: Author & manage jobs"
|
||||
weight: 51000
|
||||
description: Learn how to author and manage Dapr Jobs using the .NET SDK
|
||||
---
|
||||
|
||||
Let's create an endpoint that will be invoked by Dapr Jobs when it triggers, then schedule the job in the same app. We'll use the [simple example provided here](https://github.com/dapr/dotnet-sdk/tree/master/examples/Jobs), for the following demonstration and walk through it as an explainer of how you can schedule one-time or recurring jobs using either an interval or Cron expression yourself. In this guide,
|
||||
you will:
|
||||
|
||||
- Deploy a .NET Web API application ([JobsSample](https://github.com/dapr/dotnet-sdk/tree/master/examples/Jobs/JobsSample))
|
||||
- Utilize the Dapr .NET Jobs SDK to schedule a job invocation and set up the endpoint to be triggered
|
||||
|
||||
In the .NET example project:
|
||||
- The main [`Program.cs`](https://github.com/dapr/dotnet-sdk/tree/master/examples/Jobs/JobsSample/Program.cs) file comprises the entirety of this demonstration.
|
||||
|
||||
## Prerequisites
|
||||
- [Dapr CLI](https://docs.dapr.io/getting-started/install-dapr-cli/)
|
||||
- [Initialized Dapr environment](https://docs.dapr.io/getting-started/install-dapr-selfhost)
|
||||
- [.NET 6](https://dotnet.microsoft.com/download/dotnet/6.0), [.NET 8](https://dotnet.microsoft.com/download/dotnet/8.0) or [.NET 9](https://dotnet.microsoft.com/download/dotnet/9.0) installed
|
||||
- [Dapr.Jobs](https://www.nuget.org/packages/Dapr.Jobs) NuGet package installed to your project
|
||||
|
||||
{{% alert title="Note" color="primary" %}}
|
||||
|
||||
Note that while .NET 6 is the minimum support version of .NET in Dapr v1.15, only .NET 8 and .NET 9 will continue to be supported by Dapr in v1.16 and later.
|
||||
|
||||
{{% /alert %}}
|
||||
|
||||
## Set up the environment
|
||||
Clone the [.NET SDK repo](https://github.com/dapr/dotnet-sdk).
|
||||
|
||||
```sh
|
||||
git clone https://github.com/dapr/dotnet-sdk.git
|
||||
```
|
||||
|
||||
From the .NET SDK root directory, navigate to the Dapr Jobs example.
|
||||
|
||||
```sh
|
||||
cd examples/Jobs
|
||||
```
|
||||
|
||||
## Run the application locally
|
||||
|
||||
To run the Dapr application, you need to start the .NET program and a Dapr sidecar. Navigate to the `JobsSample` directory.
|
||||
|
||||
```sh
|
||||
cd JobsSample
|
||||
```
|
||||
|
||||
We'll run a command that starts both the Dapr sidecar and the .NET program at the same time.
|
||||
|
||||
```sh
|
||||
dapr run --app-id jobsapp --dapr-grpc-port 4001 --dapr-http-port 3500 -- dotnet run
|
||||
```
|
||||
|
||||
> Dapr listens for HTTP requests at `http://localhost:3500` and internal Jobs gRPC requests at `http://localhost:4001`.
|
||||
|
||||
## Register the Dapr Jobs client with dependency injection
|
||||
The Dapr Jobs SDK provides an extension method to simplify the registration of the Dapr Jobs client. Before completing
|
||||
the dependency injection registration in `Program.cs`, add the following line:
|
||||
|
||||
```cs
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
//Add anywhere between these two lines
|
||||
builder.Services.AddDaprJobsClient();
|
||||
|
||||
var app = builder.Build();
|
||||
```
|
||||
|
||||
> Note that in today's implementation of the Jobs API, the app that schedules the job will also be the app that receives the trigger notification. In other words, you cannot schedule a trigger to run in another application. As a result, while you don't explicitly need the Dapr Jobs client to be registered in your application to schedule a trigger invocation endpoint, your endpoint will never be invoked without the same app also scheduling the job somehow (whether via this Dapr Jobs .NET SDK or an HTTP call to the sidecar).
|
||||
|
||||
It's possible that you may want to provide some configuration options to the Dapr Jobs client that
|
||||
should be present with each call to the sidecar such as a Dapr API token, or you want to use a non-standard
|
||||
HTTP or gRPC endpoint. This is possible through use of an overload of the registration method that allows configuration of a
|
||||
`DaprJobsClientBuilder` instance:
|
||||
|
||||
```cs
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
builder.Services.AddDaprJobsClient((_, daprJobsClientBuilder) =>
|
||||
{
|
||||
daprJobsClientBuilder.UseDaprApiToken("abc123");
|
||||
daprJobsClientBuilder.UseHttpEndpoint("http://localhost:8512"); //Non-standard sidecar HTTP endpoint
|
||||
});
|
||||
|
||||
var app = builder.Build();
|
||||
```
|
||||
|
||||
Still, it's possible that whatever values you wish to inject need to be retrieved from some other source, itself registered as a dependency. There's one more overload you can use to inject an `IServiceProvider` into the configuration action method. In the following example, we register a fictional singleton that can retrieve secrets from somewhere and pass it into the configuration method for `AddDaprJobClient` so
|
||||
we can retrieve our Dapr API token from somewhere else for registration here:
|
||||
|
||||
```cs
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
builder.Services.AddSingleton<SecretRetriever>();
|
||||
builder.Services.AddDaprJobsClient((serviceProvider, daprJobsClientBuilder) =>
|
||||
{
|
||||
var secretRetriever = serviceProvider.GetRequiredService<SecretRetriever>();
|
||||
var daprApiToken = secretRetriever.GetSecret("DaprApiToken").Value;
|
||||
daprJobsClientBuilder.UseDaprApiToken(daprApiToken);
|
||||
|
||||
daprJobsClientBuilder.UseHttpEndpoint("http://localhost:8512");
|
||||
});
|
||||
|
||||
var app = builder.Build();
|
||||
```
|
||||
|
||||
## Use the Dapr Jobs client using IConfiguration
|
||||
It's possible to configure the Dapr Jobs client using the values in your registered `IConfiguration` as well without
|
||||
explicitly specifying each of the value overrides using the `DaprJobsClientBuilder` as demonstrated in the previous
|
||||
section. Rather, by populating an `IConfiguration` made available through dependency injection the `AddDaprJobsClient()`
|
||||
registration will automatically use these values over their respective defaults.
|
||||
|
||||
Start by populating the values in your configuration. This can be done in several different ways as demonstrated below.
|
||||
|
||||
### Configuration via `ConfigurationBuilder`
|
||||
Application settings can be configured without using a configuration source and by instead populating the value in-memory
|
||||
using a `ConfigurationBuilder` instance:
|
||||
|
||||
```csharp
|
||||
var builder = WebApplication.CreateBuilder();
|
||||
|
||||
//Create the configuration
|
||||
var configuration = new ConfigurationBuilder()
|
||||
.AddInMemoryCollection(new Dictionary<string, string> {
|
||||
{ "DAPR_HTTP_ENDPOINT", "http://localhost:54321" },
|
||||
{ "DAPR_API_TOKEN", "abc123" }
|
||||
})
|
||||
.Build();
|
||||
|
||||
builder.Configuration.AddConfiguration(configuration);
|
||||
builder.Services.AddDaprJobsClient(); //This will automatically populate the HTTP endpoint and API token values from the IConfiguration
|
||||
```
|
||||
|
||||
### Configuration via Environment Variables
|
||||
Application settings can be accessed from environment variables available to your application.
|
||||
|
||||
The following environment variables will be used to populate both the HTTP endpoint and API token used to register the
|
||||
Dapr Jobs client.
|
||||
|
||||
| Key | Value |
|
||||
| --- | --- |
|
||||
| DAPR_HTTP_ENDPOINT | http://localhost:54321 |
|
||||
| DAPR_API_TOKEN | abc123 |
|
||||
|
||||
```csharp
|
||||
var builder = WebApplication.CreateBuilder();
|
||||
|
||||
builder.Configuration.AddEnvironmentVariables();
|
||||
builder.Services.AddDaprJobsClient();
|
||||
```
|
||||
|
||||
The Dapr Jobs client will be configured to use both the HTTP endpoint `http://localhost:54321` and populate all outbound
|
||||
requests with the API token header `abc123`.
|
||||
|
||||
### Configuration via prefixed Environment Variables
|
||||
|
||||
However, in shared-host scenarios where there are multiple applications all running on the same machine without using
|
||||
containers or in development environments, it's not uncommon to prefix environment variables. The following example
|
||||
assumes that both the HTTP endpoint and the API token will be pulled from environment variables prefixed with the
|
||||
value "myapp_". The two environment variables used in this scenario are as follows:
|
||||
|
||||
| Key | Value |
|
||||
| --- | --- |
|
||||
| myapp_DAPR_HTTP_ENDPOINT | http://localhost:54321 |
|
||||
| myapp_DAPR_API_TOKEN | abc123 |
|
||||
|
||||
These environment variables will be loaded into the registered configuration in the following example and made available
|
||||
without the prefix attached.
|
||||
|
||||
```csharp
|
||||
var builder = WebApplication.CreateBuilder();
|
||||
|
||||
builder.Configuration.AddEnvironmentVariables(prefix: "myapp_");
|
||||
builder.Services.AddDaprJobsClient();
|
||||
```
|
||||
|
||||
The Dapr Jobs client will be configured to use both the HTTP endpoint `http://localhost:54321` and populate all outbound
|
||||
requests with the API token header `abc123`.
|
||||
|
||||
## Use the Dapr Jobs client without relying on dependency injection
|
||||
While the use of dependency injection simplifies the use of complex types in .NET and makes it easier to
|
||||
deal with complicated configurations, you're not required to register the `DaprJobsClient` in this way. Rather, you can also elect to create an instance of it from a `DaprJobsClientBuilder` instance as demonstrated below:
|
||||
|
||||
```cs
|
||||
|
||||
public class MySampleClass
|
||||
{
|
||||
public void DoSomething()
|
||||
{
|
||||
var daprJobsClientBuilder = new DaprJobsClientBuilder();
|
||||
var daprJobsClient = daprJobsClientBuilder.Build();
|
||||
|
||||
//Do something with the `daprJobsClient`
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Set up a endpoint to be invoked when the job is triggered
|
||||
|
||||
It's easy to set up a jobs endpoint if you're at all familiar with [minimal APIs in ASP.NET Core](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/minimal-apis/overview) as the syntax is the same between the two.
|
||||
|
||||
Once dependency injection registration has been completed, configure the application the same way you would to handle mapping an HTTP request via the minimal API functionality in ASP.NET Core. Implemented as an extension method,
|
||||
pass the name of the job it should be responsive to and a delegate. Services can be injected into the delegate's arguments as you wish and the job payload can be accessed from the `ReadOnlyMemory<byte>` originally provided to the
|
||||
job registration.
|
||||
|
||||
There are two delegates you can use here. One provides an `IServiceProvider` in case you need to inject other services into the handler:
|
||||
|
||||
```cs
|
||||
//We have this from the example above
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
builder.Services.AddDaprJobsClient();
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
//Add our endpoint registration
|
||||
app.MapDaprScheduledJob("myJob", (IServiceProvider serviceProvider, string jobName, ReadOnlyMemory<byte> jobPayload) => {
|
||||
var logger = serviceProvider.GetService<ILogger>();
|
||||
logger?.LogInformation("Received trigger invocation for '{jobName}'", "myJob");
|
||||
|
||||
//Do something...
|
||||
});
|
||||
|
||||
app.Run();
|
||||
```
|
||||
|
||||
The other overload of the delegate doesn't require an `IServiceProvider` if not necessary:
|
||||
|
||||
```cs
|
||||
//We have this from the example above
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
builder.Services.AddDaprJobsClient();
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
//Add our endpoint registration
|
||||
app.MapDaprScheduledJob("myJob", (string jobName, ReadOnlyMemory<byte> jobPayload) => {
|
||||
//Do something...
|
||||
});
|
||||
|
||||
app.Run();
|
||||
```
|
||||
|
||||
## Support cancellation tokens when processing mapped invocations
|
||||
You may want to ensure that timeouts are handled on job invocations so that they don't indefinitely hang and use system resources. When setting up the job mapping, there's an optional `TimeSpan` parameter that can be
|
||||
provided as the last argument to specify a timeout for the request. Every time the job mapping invocation is triggered, a new `CancellationTokenSource` will be created using this timeout parameter and a `CancellationToken`
|
||||
will be created from it to put an upper bound on the processing of the request. If a timeout isn't provided, this defaults to `CancellationToken.None` and a timeout will not be automatically applied to the mapping.
|
||||
|
||||
```cs
|
||||
//We have this from the example above
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
builder.Services.AddDaprJobsClient();
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
//Add our endpoint registration
|
||||
app.MapDaprScheduledJob("myJob", (string jobName, ReadOnlyMemory<byte> jobPayload) => {
|
||||
//Do something...
|
||||
}, TimeSpan.FromSeconds(15)); //Assigns a maximum timeout of 15 seconds for handling the invocation request
|
||||
|
||||
app.Run();
|
||||
```
|
||||
|
||||
## Register the job
|
||||
|
||||
Finally, we have to register the job we want scheduled. Note that from here, all SDK methods have cancellation token support and use a default token if not otherwise set.
|
||||
|
||||
There are three different ways to set up a job that vary based on how you want to configure the schedule:
|
||||
|
||||
### One-time job
|
||||
A one-time job is exactly that; it will run at a single point in time and will not repeat. This approach requires that you select a job name and specify a time it should be triggered.
|
||||
|
||||
| Argument Name | Type | Description | Required |
|
||||
|---|---|---|---|
|
||||
| jobName | string | The name of the job being scheduled. | Yes |
|
||||
| scheduledTime | DateTime | The point in time when the job should be run. | Yes |
|
||||
| payload | ReadOnlyMemory<byte> | Job data provided to the invocation endpoint when triggered. | No |
|
||||
| cancellationToken | CancellationToken | Used to cancel out of the operation early, e.g. because of an operation timeout. | No |
|
||||
|
||||
One-time jobs can be scheduled from the Dapr Jobs client as in the following example:
|
||||
|
||||
```cs
|
||||
public class MyOperation(DaprJobsClient daprJobsClient)
|
||||
{
|
||||
public async Task ScheduleOneTimeJobAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var today = DateTime.UtcNow;
|
||||
var threeDaysFromNow = today.AddDays(3);
|
||||
|
||||
await daprJobsClient.ScheduleOneTimeJobAsync("myJobName", threeDaysFromNow, cancellationToken: cancellationToken);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Interval-based job
|
||||
An interval-based job is one that runs on a recurring loop configured as a fixed amount of time, not unlike how [reminders](https://docs.dapr.io/developing-applications/building-blocks/actors/actors-timers-reminders/#actor-reminders) work in the Actors building block today. These jobs can be scheduled with a number of optional arguments as well:
|
||||
|
||||
| Argument Name | Type | Description | Required |
|
||||
|---|---|---|---|
|
||||
| jobName | string | The name of the job being scheduled. | Yes |
|
||||
| interval | TimeSpan | The interval at which the job should be triggered. | Yes |
|
||||
| startingFrom | DateTime | The point in time from which the job schedule should start. | No |
|
||||
| repeats | int | The maximum number of times the job should be triggered. | No |
|
||||
| ttl | When the job should expires and no longer trigger. | No |
|
||||
| payload | ReadOnlyMemory<byte> | Job data provided to the invocation endpoint when triggered. | No |
|
||||
| cancellationToken | CancellationToken | Used to cancel out of the operation early, e.g. because of an operation timeout. | No |
|
||||
|
||||
Interval-based jobs can be scheduled from the Dapr Jobs client as in the following example:
|
||||
|
||||
```cs
|
||||
public class MyOperation(DaprJobsClient daprJobsClient)
|
||||
{
|
||||
|
||||
public async Task ScheduleIntervalJobAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var hourlyInterval = TimeSpan.FromHours(1);
|
||||
|
||||
//Trigger the job hourly, but a maximum of 5 times
|
||||
await daprJobsClient.ScheduleIntervalJobAsync("myJobName", hourlyInterval, repeats: 5), cancellationToken: cancellationToken;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Cron-based job
|
||||
A Cron-based job is scheduled using a Cron expression. This gives more calendar-based control over when the job is triggered as it can used calendar-based values in the expression. Like the other options, these jobs can be scheduled with a number of optional arguments as well:
|
||||
|
||||
| Argument Name | Type | Description | Required |
|
||||
|---|---|---|---|
|
||||
| jobName | string | The name of the job being scheduled. | Yes |
|
||||
| cronExpression | string | The systemd Cron-like expression indicating when the job should be triggered. | Yes |
|
||||
| startingFrom | DateTime | The point in time from which the job schedule should start. | No |
|
||||
| repeats | int | The maximum number of times the job should be triggered. | No |
|
||||
| ttl | When the job should expires and no longer trigger. | No |
|
||||
| payload | ReadOnlyMemory<byte> | Job data provided to the invocation endpoint when triggered. | No |
|
||||
| cancellationToken | CancellationToken | Used to cancel out of the operation early, e.g. because of an operation timeout. | No |
|
||||
|
||||
A Cron-based job can be scheduled from the Dapr Jobs client as follows:
|
||||
|
||||
```cs
|
||||
public class MyOperation(DaprJobsClient daprJobsClient)
|
||||
{
|
||||
public async Task ScheduleCronJobAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
//At the top of every other hour on the fifth day of the month
|
||||
const string cronSchedule = "0 */2 5 * *";
|
||||
|
||||
//Don't start this until next month
|
||||
var now = DateTime.UtcNow;
|
||||
var oneMonthFromNow = now.AddMonths(1);
|
||||
var firstOfNextMonth = new DateTime(oneMonthFromNow.Year, oneMonthFromNow.Month, 1, 0, 0, 0);
|
||||
|
||||
//Trigger the job hourly, but a maximum of 5 times
|
||||
await daprJobsClient.ScheduleCronJobAsync("myJobName", cronSchedule, dueTime: firstOfNextMonth, cancellationToken: cancellationToken);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Get details of already-scheduled job
|
||||
If you know the name of an already-scheduled job, you can retrieve its metadata without waiting for it to
|
||||
be triggered. The returned `JobDetails` exposes a few helpful properties for consuming the information from the Dapr Jobs API:
|
||||
|
||||
- If the `Schedule` property contains a Cron expression, the `IsCronExpression` property will be true and the expression will also be available in the `CronExpression` property.
|
||||
- If the `Schedule` property contains a duration value, the `IsIntervalExpression` property will instead be true and the value will be converted to a `TimeSpan` value accessible from the `Interval` property.
|
||||
|
||||
This can be done by using the following:
|
||||
|
||||
```cs
|
||||
public class MyOperation(DaprJobsClient daprJobsClient)
|
||||
{
|
||||
public async Task<JobDetails> GetJobDetailsAsync(string jobName, CancellationToken cancellationToken)
|
||||
{
|
||||
var jobDetails = await daprJobsClient.GetJobAsync(jobName, canecllationToken);
|
||||
return jobDetails;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Delete a scheduled job
|
||||
To delete a scheduled job, you'll need to know its name. From there, it's as simple as calling the `DeleteJobAsync` method on the Dapr Jobs client:
|
||||
|
||||
```cs
|
||||
public class MyOperation(DaprJobsClient daprJobsClient)
|
||||
{
|
||||
public async Task DeleteJobAsync(string jobName, CancellationToken cancellationToken)
|
||||
{
|
||||
await daprJobsClient.DeleteJobAsync(jobName, cancellationToken);
|
||||
}
|
||||
}
|
||||
```
|
|
@ -0,0 +1,197 @@
|
|||
---
|
||||
type: docs
|
||||
title: "DaprJobsClient usage"
|
||||
linkTitle: "DaprJobsClient usage"
|
||||
weight: 59000
|
||||
description: Essential tips and advice for using DaprJobsClient
|
||||
---
|
||||
|
||||
## Lifetime management
|
||||
|
||||
A `DaprJobsClient` is a version of the Dapr client that is dedicated to interacting with the Dapr Jobs API. It can be
|
||||
registered alongside a `DaprClient` and other Dapr clients without issue.
|
||||
|
||||
It maintains access to networking resources in the form of TCP sockets used to communicate with the Dapr sidecar and
|
||||
implements `IDisposable` to support the eager cleanup of resources.
|
||||
|
||||
For best performance, create a single long-lived instance of `DaprJobsClient` and provide access to that shared instance
|
||||
throughout your application. `DaprJobsClient` instances are thread-safe and intended to be shared.
|
||||
|
||||
This can be aided by utilizing the dependency injection functionality. The registration method supports registration using
|
||||
as a singleton, a scoped instance or as transient (meaning it's recreated every time it's injected), but also enables
|
||||
registration to utilize values from an `IConfiguration` or other injected service in a way that's impractical when
|
||||
creating the client from scratch in each of your classes.
|
||||
|
||||
Avoid creating a `DaprJobsClient` for each operation and disposing it when the operation is complete.
|
||||
|
||||
## Configuring DaprJobsClient via the DaprJobsClientBuilder
|
||||
|
||||
A `DaprJobsClient` can be configured by invoking methods on the `DaprJobsClientBuilder` class before calling `.Build()`
|
||||
to create the client itself. The settings for each `DaprJobsClient` are separate
|
||||
and cannot be changed after calling `.Build()`.
|
||||
|
||||
```cs
|
||||
var daprJobsClient = new DaprJobsClientBuilder()
|
||||
.UseDaprApiToken("abc123") // Specify the API token used to authenticate to other Dapr sidecars
|
||||
.Build();
|
||||
```
|
||||
|
||||
The `DaprJobsClientBuilder` contains settings for:
|
||||
|
||||
- The HTTP endpoint of the Dapr sidecar
|
||||
- The gRPC endpoint of the Dapr sidecar
|
||||
- The `JsonSerializerOptions` object used to configure JSON serialization
|
||||
- The `GrpcChannelOptions` object used to configure gRPC
|
||||
- The API token used to authenticate requests to the sidecar
|
||||
- The factory method used to create the `HttpClient` instance used by the SDK
|
||||
- The timeout used for the `HttpClient` instance when making requests to the sidecar
|
||||
|
||||
The SDK will read the following environment variables to configure the default values:
|
||||
|
||||
- `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
|
||||
|
||||
Dapr's use of `CancellationToken` for cancellation relies on the configuration of the gRPC channel options. If you need
|
||||
to configure these options yourself, make sure to enable the [ThrowOperationCanceledOnCancellation setting](https://grpc.github.io/grpc/csharp-dotnet/api/Grpc.Net.Client.GrpcChannelOptions.html#Grpc_Net_Client_GrpcChannelOptions_ThrowOperationCanceledOnCancellation).
|
||||
|
||||
```cs
|
||||
var daprJobsClient = new DaprJobsClientBuilder()
|
||||
.UseGrpcChannelOptions(new GrpcChannelOptions { ... ThrowOperationCanceledOnCancellation = true })
|
||||
.Build();
|
||||
```
|
||||
|
||||
## Using cancellation with `DaprJobsClient`
|
||||
|
||||
The APIs on `DaprJobsClient` perform asynchronous operations and accept an optional `CancellationToken` parameter. This
|
||||
follows a standard .NET practice for cancellable operations. Note that when cancellation occurs, there is no guarantee that
|
||||
the remote endpoint stops processing the request, only that the client has stopped waiting for completion.
|
||||
|
||||
When an operation is cancelled, it will throw an `OperationCancelledException`.
|
||||
|
||||
## Configuring `DaprJobsClient` via dependency injection
|
||||
|
||||
Using the built-in extension methods for registering the `DaprJobsClient` in a dependency injection container can
|
||||
provide the benefit of registering the long-lived service a single time, centralize complex configuration and improve
|
||||
performance by ensuring similarly long-lived resources are re-purposed when possible (e.g. `HttpClient` instances).
|
||||
|
||||
There are three overloads available to give the developer the greatest flexibility in configuring the client for their
|
||||
scenario. Each of these will register the `IHttpClientFactory` on your behalf if not already registered, and configure
|
||||
the `DaprJobsClientBuilder` to use it when creating the `HttpClient` instance in order to re-use the same instance as
|
||||
much as possible and avoid socket exhaustion and other issues.
|
||||
|
||||
In the first approach, there's no configuration done by the developer and the `DaprJobsClient` is configured with the
|
||||
default settings.
|
||||
|
||||
```cs
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
builder.Services.AddDaprJobsClient(); //Registers the `DaprJobsClient` to be injected as needed
|
||||
var app = builder.Build();
|
||||
```
|
||||
|
||||
Sometimes the developer will need to configure the created client using the various configuration options detailed
|
||||
above. This is done through an overload that passes in the `DaprJobsClientBuiler` and exposes methods for configuring
|
||||
the necessary options.
|
||||
|
||||
```cs
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
builder.Services.AddDaprJobsClient((_, daprJobsClientBuilder) => {
|
||||
//Set the API token
|
||||
daprJobsClientBuilder.UseDaprApiToken("abc123");
|
||||
//Specify a non-standard HTTP endpoint
|
||||
daprJobsClientBuilder.UseHttpEndpoint("http://dapr.my-company.com");
|
||||
});
|
||||
|
||||
var app = builder.Build();
|
||||
```
|
||||
|
||||
Finally, it's possible that the developer may need to retrieve information from another service in order to populate
|
||||
these configuration values. That value may be provided from a `DaprClient` instance, a vendor-specific SDK or some
|
||||
local service, but as long as it's also registered in DI, it can be injected into this configuration operation via the
|
||||
last overload:
|
||||
|
||||
```cs
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
//Register a fictional service that retrieves secrets from somewhere
|
||||
builder.Services.AddSingleton<SecretService>();
|
||||
|
||||
builder.Services.AddDaprJobsClient((serviceProvider, daprJobsClientBuilder) => {
|
||||
//Retrieve an instance of the `SecretService` from the service provider
|
||||
var secretService = serviceProvider.GetRequiredService<SecretService>();
|
||||
var daprApiToken = secretService.GetSecret("DaprApiToken").Value;
|
||||
|
||||
//Configure the `DaprJobsClientBuilder`
|
||||
daprJobsClientBuilder.UseDaprApiToken(daprApiToken);
|
||||
});
|
||||
|
||||
var app = builder.Build();
|
||||
```
|
||||
|
||||
## Understanding payload serialization on DaprJobsClient
|
||||
|
||||
While there are many methods on the `DaprClient` that automatically serialize and deserialize data using the
|
||||
`System.Text.Json` serializer, this SDK takes a different philosophy. Instead, the relevant methods accept an optional
|
||||
payload of `ReadOnlyMemory<byte>` meaning that serialization is an exercise left to the developer and is not
|
||||
generally handled by the SDK.
|
||||
|
||||
That said, there are some helper extension methods available for each of the scheduling methods. If you know that you
|
||||
want to use a type that's JSON-serializable, you can use the `Schedule*WithPayloadAsync` method for each scheduling
|
||||
type that accepts an `object` as a payload and an optional `JsonSerializerOptions` to use when serializing the value.
|
||||
This will convert the value to UTF-8 encoded bytes for you as a convenience. Here's an example of what this might
|
||||
look like when scheduling a Cron expression:
|
||||
|
||||
```cs
|
||||
public sealed record Doodad (string Name, int Value);
|
||||
|
||||
//...
|
||||
var doodad = new Doodad("Thing", 100);
|
||||
await daprJobsClient.ScheduleCronJobWithPayloadAsync("myJob", "5 * * * *", doodad);
|
||||
```
|
||||
|
||||
In the same vein, if you have a plain string value, you can use an overload of the same method to serialize a
|
||||
string-typed payload and the JSON serialization step will be skipped and it'll only be encoded to an array of
|
||||
UTF-8 encoded bytes. Here's an example of what this might look like when scheduling a one-time job:
|
||||
|
||||
```cs
|
||||
var now = DateTime.UtcNow;
|
||||
var oneWeekFromNow = now.AddDays(7);
|
||||
await daprJobsClient.ScheduleOneTimeJobWithPayloadAsync("myOtherJob", oneWeekFromNow, "This is a test!");
|
||||
```
|
||||
|
||||
The delegate handling the job invocation expects at least two arguments to be present:
|
||||
- A `string` that is populated with the `jobName`, providing the name of the invoked job
|
||||
- A `ReadOnlyMemory<byte>` that is populated with the bytes originally provided during the job registration.
|
||||
|
||||
Because the payload is stored as a `ReadOnlyMemory<byte>`, the developer has the freedom to serialize and deserialize
|
||||
as they wish, but there are again two helper extensions included that can deserialize this to either a JSON-compatible
|
||||
type or a string. Both methods assume that the developer encoded the originally scheduled job (perhaps using the
|
||||
helper serialization methods) as these methods will not force the bytes to represent something they're not.
|
||||
|
||||
To deserialize the bytes to a string, the following helper method can be used:
|
||||
```cs
|
||||
var payloadAsString = Encoding.UTF8.GetString(jobPayload.Span); //If successful, returns a string with the value
|
||||
```
|
||||
|
||||
## Error handling
|
||||
|
||||
Methods on `DaprJobsClient` will throw a `DaprJobsServiceException` if an issue is encountered between the SDK
|
||||
and the Jobs API service running on the Dapr sidecar. If a failure is encountered because of a poorly formatted
|
||||
request made to the Jobs API service through this SDK, a `DaprMalformedJobException` will be thrown. In case of
|
||||
illegal argument values, the appropriate standard exception will be thrown (e.g. `ArgumentOutOfRangeException`
|
||||
or `ArgumentNullException`) with the name of the offending argument. And for anything else, a `DaprException`
|
||||
will be thrown.
|
||||
|
||||
The most common cases of failure will be related to:
|
||||
|
||||
- Incorrect argument formatting while engaging with the Jobs API
|
||||
- Transient failures such as a networking problem
|
||||
- Invalid data, such as a failure to deserialize a value into a type it wasn't originally serialized from
|
||||
|
||||
In any of these cases, you can examine more exception details through the `.InnerException` property.
|
|
@ -0,0 +1,17 @@
|
|||
---
|
||||
type: docs
|
||||
title: "Dapr Messaging .NET SDK"
|
||||
linkTitle: "Messaging"
|
||||
weight: 60000
|
||||
description: Get up and running with the Dapr Messaging .NET SDK
|
||||
---
|
||||
|
||||
With the Dapr Messaging package, you can interact with the Dapr messaging APIs from a .NET application. In the
|
||||
v1.15 release, this package only contains the functionality corresponding to the
|
||||
[streaming PubSub capability](https://docs.dapr.io/developing-applications/building-blocks/pubsub/howto-publish-subscribe/#subscribe-to-topics).
|
||||
|
||||
Future Dapr .NET SDK releases will migrate existing messaging capabilities out from Dapr.Client to this
|
||||
Dapr.Messaging package. This will be documented in the release notes, documentation and obsolete attributes in advance.
|
||||
|
||||
To get started, walk through the [Dapr Messaging]({{< ref dotnet-messaging-pubsub-howto.md >}}) how-to guide and
|
||||
refer to [best practices documentation]({{< ref dotnet-messaging-pubsub-usage.md >}}) for additional guidance.
|
|
@ -0,0 +1,268 @@
|
|||
---
|
||||
type: docs
|
||||
title: "How to: Author and manage Dapr streaming subscriptions in the .NET SDK"
|
||||
linkTitle: "How to: Author & manage streaming subscriptions"
|
||||
weight: 61000
|
||||
description: Learn how to author and manage Dapr streaming subscriptions using the .NET SDK
|
||||
---
|
||||
|
||||
Let's create a subscription to a pub/sub topic or queue at using the streaming capability. We'll use the
|
||||
[simple example provided here](https://github.com/dapr/dotnet-sdk/tree/master/examples/Client/PublishSubscribe/StreamingSubscriptionExample),
|
||||
for the following demonstration and walk through it as an explainer of how you can configure message handlers at
|
||||
runtime and which do not require an endpoint to be pre-configured. In this guide, you will:
|
||||
|
||||
- Deploy a .NET Web API application ([StreamingSubscriptionExample](https://github.com/dapr/dotnet-sdk/tree/master/examples/Client/PublishSubscribe/StreamingSubscriptionExample))
|
||||
- Utilize the Dapr .NET Messaging SDK to subscribe dynamically to a pub/sub topic.
|
||||
|
||||
## Prerequisites
|
||||
- [Dapr CLI](https://docs.dapr.io/getting-started/install-dapr-cli/)
|
||||
- [Initialized Dapr environment](https://docs.dapr.io/getting-started/install-dapr-selfhost)
|
||||
- [.NET 6](https://dotnet.microsoft.com/download/dotnet/6.0), [.NET 8](https://dotnet.microsoft.com/download/dotnet/8.0) or [.NET 9](https://dotnet.microsoft.com/download/dotnet/9.0) installed
|
||||
- [Dapr.Messaging](https://www.nuget.org/packages/Dapr.Messaging) NuGet package installed to your project
|
||||
|
||||
{{% alert title="Note" color="primary" %}}
|
||||
|
||||
Note that while .NET 6 is the minimum support version of .NET in Dapr v1.15, only .NET 8 and .NET 9 will continue to be supported by Dapr in v1.16 and later.
|
||||
|
||||
{{% /alert %}}
|
||||
|
||||
## Set up the environment
|
||||
Clone the [.NET SDK repo](https://github.com/dapr/dotnet-sdk).
|
||||
|
||||
```sh
|
||||
git clone https://github.com/dapr/dotnet-sdk.git
|
||||
```
|
||||
|
||||
From the .NET SDK root directory, navigate to the Dapr streaming PubSub example.
|
||||
|
||||
```sh
|
||||
cd examples/Client/PublishSubscribe
|
||||
```
|
||||
|
||||
## Run the application locally
|
||||
|
||||
To run the Dapr application, you need to start the .NET program and a Dapr sidecar. Navigate to the `StreamingSubscriptionExample` directory.
|
||||
|
||||
```sh
|
||||
cd StreamingSubscriptionExample
|
||||
```
|
||||
|
||||
We'll run a command that starts both the Dapr sidecar and the .NET program at the same time.
|
||||
|
||||
```sh
|
||||
dapr run --app-id pubsubapp --dapr-grpc-port 4001 --dapr-http-port 3500 -- dotnet run
|
||||
```
|
||||
> Dapr listens for HTTP requests at `http://localhost:3500` and internal Jobs gRPC requests at `http://localhost:4001`.
|
||||
|
||||
## Register the Dapr PubSub client with dependency injection
|
||||
The Dapr Messaging SDK provides an extension method to simplify the registration of the Dapr PubSub client. Before
|
||||
completing the dependency injection registration in `Program.cs`, add the following line:
|
||||
|
||||
```csharp
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
//Add anywhere between these two
|
||||
builder.Services.AddDaprPubSubClient(); //That's it
|
||||
|
||||
var app = builder.Build();
|
||||
```
|
||||
|
||||
It's possible that you may want to provide some configuration options to the Dapr PubSub client that
|
||||
should be present with each call to the sidecar such as a Dapr API token, or you want to use a non-standard
|
||||
HTTP or gRPC endpoint. This be possible through use of an overload of the registration method that allows configuration
|
||||
of a `DaprPublishSubscribeClientBuilder` instance:
|
||||
|
||||
```csharp
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
builder.Services.AddDaprPubSubClient((_, daprPubSubClientBuilder) => {
|
||||
daprPubSubClientBuilder.UseDaprApiToken("abc123");
|
||||
daprPubSubClientBuilder.UseHttpEndpoint("http://localhost:8512"); //Non-standard sidecar HTTP endpoint
|
||||
});
|
||||
|
||||
var app = builder.Build();
|
||||
```
|
||||
|
||||
Still, it's possible that whatever values you wish to inject need to be retrieved from some other source, itself registered as a dependency. There's one more overload you can use to inject an `IServiceProvider` into the configuration action method. In the following example, we register a fictional singleton that can retrieve secrets from somewhere and pass it into the configuration method for `AddDaprJobClient` so
|
||||
we can retrieve our Dapr API token from somewhere else for registration here:
|
||||
|
||||
```csharp
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
builder.Services.AddSingleton<SecretRetriever>();
|
||||
builder.Services.AddDaprPubSubClient((serviceProvider, daprPubSubClientBuilder) => {
|
||||
var secretRetriever = serviceProvider.GetRequiredService<SecretRetriever>();
|
||||
var daprApiToken = secretRetriever.GetSecret("DaprApiToken").Value;
|
||||
daprPubSubClientBuilder.UseDaprApiToken(daprApiToken);
|
||||
|
||||
daprPubSubClientBuilder.UseHttpEndpoint("http://localhost:8512");
|
||||
});
|
||||
|
||||
var app = builder.Build();
|
||||
```
|
||||
|
||||
## Use the Dapr PubSub client using IConfiguration
|
||||
It's possible to configure the Dapr PubSub client using the values in your registered `IConfiguration` as well without
|
||||
explicitly specifying each of the value overrides using the `DaprPublishSubscribeClientBuilder` as demonstrated in the previous
|
||||
section. Rather, by populating an `IConfiguration` made available through dependency injection the `AddDaprPubSubClient()`
|
||||
registration will automatically use these values over their respective defaults.
|
||||
|
||||
Start by populating the values in your configuration. This can be done in several different ways as demonstrated below.
|
||||
|
||||
### Configuration via `ConfigurationBuilder`
|
||||
Application settings can be configured without using a configuration source and by instead populating the value in-memory
|
||||
using a `ConfigurationBuilder` instance:
|
||||
|
||||
```csharp
|
||||
var builder = WebApplication.CreateBuilder();
|
||||
|
||||
//Create the configuration
|
||||
var configuration = new ConfigurationBuilder()
|
||||
.AddInMemoryCollection(new Dictionary<string, string> {
|
||||
{ "DAPR_HTTP_ENDPOINT", "http://localhost:54321" },
|
||||
{ "DAPR_API_TOKEN", "abc123" }
|
||||
})
|
||||
.Build();
|
||||
|
||||
builder.Configuration.AddConfiguration(configuration);
|
||||
builder.Services.AddDaprPubSubClient(); //This will automatically populate the HTTP endpoint and API token values from the IConfiguration
|
||||
```
|
||||
|
||||
### Configuration via Environment Variables
|
||||
Application settings can be accessed from environment variables available to your application.
|
||||
|
||||
The following environment variables will be used to populate both the HTTP endpoint and API token used to register the
|
||||
Dapr PubSub client.
|
||||
|
||||
| Key | Value |
|
||||
|--------------------|------------------------|
|
||||
| DAPR_HTTP_ENDPOINT | http://localhost:54321 |
|
||||
| DAPR_API_TOKEN | abc123 |
|
||||
|
||||
```csharp
|
||||
var builder = WebApplication.CreateBuilder();
|
||||
|
||||
builder.Configuration.AddEnvironmentVariables();
|
||||
builder.Services.AddDaprPubSubClient();
|
||||
```
|
||||
|
||||
The Dapr PubSub client will be configured to use both the HTTP endpoint `http://localhost:54321` and populate all outbound
|
||||
requests with the API token header `abc123`.
|
||||
|
||||
### Configuration via prefixed Environment Variables
|
||||
However, in shared-host scenarios where there are multiple applications all running on the same machine without using
|
||||
containers or in development environments, it's not uncommon to prefix environment variables. The following example
|
||||
assumes that both the HTTP endpoint and the API token will be pulled from environment variables prefixed with the
|
||||
value "myapp_". The two environment variables used in this scenario are as follows:
|
||||
|
||||
| Key | Value |
|
||||
|--------------------------|------------------------|
|
||||
| myapp_DAPR_HTTP_ENDPOINT | http://localhost:54321 |
|
||||
| myapp_DAPR_API_TOKEN | abc123 |
|
||||
|
||||
These environment variables will be loaded into the registered configuration in the following example and made available
|
||||
without the prefix attached.
|
||||
|
||||
```csharp
|
||||
var builder = WebApplication.CreateBuilder();
|
||||
|
||||
builder.Configuration.AddEnvironmentVariables(prefix: "myapp_");
|
||||
builder.Services.AddDaprPubSubClient();
|
||||
```
|
||||
|
||||
The Dapr PubSub client will be configured to use both the HTTP endpoint `http://localhost:54321` and populate all outbound
|
||||
requests with the API token header `abc123`.
|
||||
|
||||
## Use the Dapr PubSub client without relying on dependency injection
|
||||
While the use of dependency injection simplifies the use of complex types in .NET and makes it easier to
|
||||
deal with complicated configurations, you're not required to register the `DaprPublishSubscribeClient` in this way.
|
||||
Rather, you can also elect to create an instance of it from a `DaprPublishSubscribeClientBuilder` instance as
|
||||
demonstrated below:
|
||||
|
||||
```cs
|
||||
|
||||
public class MySampleClass
|
||||
{
|
||||
public void DoSomething()
|
||||
{
|
||||
var daprPubSubClientBuilder = new DaprPublishSubscribeClientBuilder();
|
||||
var daprPubSubClient = daprPubSubClientBuilder.Build();
|
||||
|
||||
//Do something with the `daprPubSubClient`
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Set up message handler
|
||||
The streaming subscription implementation in Dapr gives you greater control over handling backpressure from events by
|
||||
leaving the messages in the Dapr runtime until your application is ready to accept them. The .NET SDK supports a
|
||||
high-performance queue for maintaining a local cache of these messages in your application while processing is pending.
|
||||
These messages will persist in the queue until processing either times out for each one or a response action is taken
|
||||
for each (typically after processing succeeds or fails). Until this response action is received by the Dapr runtime,
|
||||
the messages will be persisted by Dapr and made available in case of a service failure.
|
||||
|
||||
The various response actions available are as follows:
|
||||
| Response Action | Description |
|
||||
| --- | --- |
|
||||
| Retry | The event should be delivered again in the future. |
|
||||
| Drop | The event should be deleted (or forwarded to a dead letter queue, if configured) and not attempted again. |
|
||||
| Success | The event should be deleted as it was successfully processed. |
|
||||
|
||||
The handler will receive only one message at a time and if a cancellation token is provided to the subscription,
|
||||
this token will be provided during the handler invocation.
|
||||
|
||||
The handler must be configured to return a `Task<TopicResponseAction>` indicating one of these operations, even if from
|
||||
a try/catch block. If an exception is not caught by your handler, the subscription will use the response action configured
|
||||
in the options during subscription registration.
|
||||
|
||||
The following demonstrates the sample message handler provided in the example:
|
||||
|
||||
```csharp
|
||||
Task<TopicResponseAction> HandleMessageAsync(TopicMessage message, CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
//Do something with the message
|
||||
Console.WriteLine(Encoding.UTF8.GetString(message.Data.Span));
|
||||
return Task.FromResult(TopicResponseAction.Success);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return Task.FromResult(TopicResponseAction.Retry);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Configure and subscribe to the PubSub topic
|
||||
Configuration of the streaming subscription requires the name of the PubSub component registered with Dapr, the name
|
||||
of the topic or queue being subscribed to, the `DaprSubscriptionOptions` providing the configuration for the subscription,
|
||||
the message handler and an optional cancellation token. The only required argument to the `DaprSubscriptionOptions` is
|
||||
the default `MessageHandlingPolicy` which consists of a per-event timeout and the `TopicResponseAction` to take when
|
||||
that timeout occurs.
|
||||
|
||||
Other options are as follows:
|
||||
|
||||
| Property Name | Description |
|
||||
|-----------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------|
|
||||
| Metadata | Additional subscription metadata |
|
||||
| DeadLetterTopic | The optional name of the dead-letter topic to send dropped messages to. |
|
||||
| MaximumQueuedMessages | By default, there is no maximum boundary enforced for the internal queue, but setting this |
|
||||
| property would impose an upper limit. | |
|
||||
| MaximumCleanupTimeout | When the subscription is disposed of or the token flags a cancellation request, this specifies |
|
||||
| the maximum amount of time available to process the remaining messages in the internal queue. | |
|
||||
|
||||
Subscription is then configured as in the following example:
|
||||
```csharp
|
||||
var messagingClient = app.Services.GetRequiredService<DaprPublishSubscribeClient>();
|
||||
|
||||
var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(60)); //Override the default of 30 seconds
|
||||
var options = new DaprSubscriptionOptions(new MessageHandlingPolicy(TimeSpan.FromSeconds(10), TopicResponseAction.Retry));
|
||||
var subscription = await messagingClient.SubscribeAsync("pubsub", "mytopic", options, HandleMessageAsync, cancellationTokenSource.Token);
|
||||
```
|
||||
|
||||
## Terminate and clean up subscription
|
||||
When you've finished with your subscription and wish to stop receiving new events, simply await a call to
|
||||
`DisposeAsync()` on your subscription instance. This will cause the client to unregister from additional events and
|
||||
proceed to finish processing all the events still leftover in the backpressure queue, if any, before disposing of any
|
||||
internal resources. This cleanup will be limited to the timeout interval provided in the `DaprSubscriptionOptions` when
|
||||
the subscription was registered and by default, this is set to 30 seconds.
|
|
@ -0,0 +1,130 @@
|
|||
---
|
||||
type: docs
|
||||
title: "DaprPublishSubscribeClient usage"
|
||||
linkTitle: "DaprPublishSubscribeClient usage"
|
||||
weight: 69000
|
||||
description: Essential tips and advice for using DaprPublishSubscribeClient
|
||||
---
|
||||
|
||||
## Lifetime management
|
||||
|
||||
A `DaprPublishSubscribeClient` is a version of the Dapr client that is dedicated to interacting with the Dapr Messaging API.
|
||||
It can be registered alongside a `DaprClient` and other Dapr clients without issue.
|
||||
|
||||
It maintains access to networking resources in the form of TCP sockets used to communicate with the Dapr sidecar and implements
|
||||
`IAsyncDisposable` to support the eager cleanup of resources.
|
||||
|
||||
For best performance, create a single long-lived instance of `DaprPublishSubscribeClient` and provide access to that shared
|
||||
instance throughout your application. `DaprPublishSubscribeClient` instances are thread-safe and intended to be shared.
|
||||
|
||||
This can be aided by utilizing the dependency injection functionality. The registration method supports registration using
|
||||
as a singleton, a scoped instance or as transient (meaning it's recreated every time it's injected), but also enables
|
||||
registration to utilize values from an `IConfiguration` or other injected service in a way that's impractical when
|
||||
creating the client from scratch in each of your classes.
|
||||
|
||||
Avoid creating a `DaprPublishSubscribeClient` for each operation and disposing it when the operation is complete. It's
|
||||
intended that the `DaprPublishSubscribeClient` should only be disposed when you no longer wish to receive events on the
|
||||
subscription as disposing it will cancel the ongoing receipt of new events.
|
||||
|
||||
## Configuring DaprPublishSubscribeClient via the DaprPublishSubscribeClientBuilder
|
||||
A `DaprPublishSubscribeClient` can be configured by invoking methods on the `DaprPublishSubscribeClientBuilder` class
|
||||
before calling `.Build()` to create the client itself. The settings for each `DaprPublishSubscribeClient` are separate
|
||||
and cannot be changed after calling `.Build()`.
|
||||
|
||||
```cs
|
||||
var daprPubsubClient = new DaprPublishSubscribeClientBuilder()
|
||||
.UseDaprApiToken("abc123") // Specify the API token used to authenticate to other Dapr sidecars
|
||||
.Build();
|
||||
```
|
||||
|
||||
The `DaprPublishSubscribeClientBuilder` contains settings for:
|
||||
|
||||
- The HTTP endpoint of the Dapr sidecar
|
||||
- The gRPC endpoint of the Dapr sidecar
|
||||
- The `JsonSerializerOptions` object used to configure JSON serialization
|
||||
- The `GrpcChannelOptions` object used to configure gRPC
|
||||
- The API token used to authenticate requests to the sidecar
|
||||
- The factory method used to create the `HttpClient` instance used by the SDK
|
||||
- The timeout used for the `HttpClient` instance when making requests to the sidecar
|
||||
|
||||
The SDK will read the following environment variables to configure the default values:
|
||||
|
||||
- `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
|
||||
Dapr's use of `CancellationToken` for cancellation relies on the configuration of the gRPC channel options. If you
|
||||
need to configure these options yourself, make sure to enable the [ThrowOperationCanceledOnCancellation setting](https://grpc.github.io/grpc/csharp-dotnet/api/Grpc.Net.Client.GrpcChannelOptions.html#Grpc_Net_Client_GrpcChannelOptions_ThrowOperationCanceledOnCancellation).
|
||||
|
||||
```cs
|
||||
var daprPubsubClient = new DaprPublishSubscribeClientBuilder()
|
||||
.UseGrpcChannelOptions(new GrpcChannelOptions { ... ThrowOperationCanceledOnCancellation = true })
|
||||
.Build();
|
||||
```
|
||||
|
||||
## Using cancellation with `DaprPublishSubscribeClient`
|
||||
|
||||
The APIs on `DaprPublishSubscribeClient` perform asynchronous operations and accept an optional `CancellationToken`
|
||||
parameter. This follows a standard .NET practice for cancellable operations. Note that when cancellation occurs, there is
|
||||
no guarantee that the remote endpoint stops processing the request, only that the client has stopped waiting for completion.
|
||||
|
||||
When an operation is cancelled, it will throw an `OperationCancelledException`.
|
||||
|
||||
## Configuring `DaprPublishSubscribeClient` via dependency injection
|
||||
|
||||
Using the built-in extension methods for registering the `DaprPublishSubscribeClient` in a dependency injection container
|
||||
can provide the benefit of registering the long-lived service a single time, centralize complex configuration and improve
|
||||
performance by ensuring similarly long-lived resources are re-purposed when possible (e.g. `HttpClient` instances).
|
||||
|
||||
There are three overloads available to give the developer the greatest flexibility in configuring the client for their
|
||||
scenario. Each of these will register the `IHttpClientFactory` on your behalf if not already registered, and configure
|
||||
the `DaprPublishSubscribeClientBuilder` to use it when creating the `HttpClient` instance in order to re-use the same
|
||||
instance as much as possible and avoid socket exhaustion and other issues.
|
||||
|
||||
In the first approach, there's no configuration done by the developer and the `DaprPublishSubscribeClient` is configured with
|
||||
the default settings.
|
||||
|
||||
```cs
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
builder.Services.DaprPublishSubscribeClient(); //Registers the `DaprPublishSubscribeClient` to be injected as needed
|
||||
var app = builder.Build();
|
||||
```
|
||||
|
||||
Sometimes the developer will need to configure the created client using the various configuration options detailed above. This is done through an overload that passes in the `DaprJobsClientBuiler` and exposes methods for configuring the necessary options.
|
||||
|
||||
```cs
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
builder.Services.AddDaprJobsClient((_, daprPubSubClientBuilder) => {
|
||||
//Set the API token
|
||||
daprPubSubClientBuilder.UseDaprApiToken("abc123");
|
||||
//Specify a non-standard HTTP endpoint
|
||||
daprPubSubClientBuilder.UseHttpEndpoint("http://dapr.my-company.com");
|
||||
});
|
||||
|
||||
var app = builder.Build();
|
||||
```
|
||||
|
||||
Finally, it's possible that the developer may need to retrieve information from another service in order to populate these configuration values. That value may be provided from a `DaprClient` instance, a vendor-specific SDK or some local service, but as long as it's also registered in DI, it can be injected into this configuration operation via the last overload:
|
||||
|
||||
```cs
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
//Register a fictional service that retrieves secrets from somewhere
|
||||
builder.Services.AddSingleton<SecretService>();
|
||||
|
||||
builder.Services.AddDaprPublishSubscribeClient((serviceProvider, daprPubSubClientBuilder) => {
|
||||
//Retrieve an instance of the `SecretService` from the service provider
|
||||
var secretService = serviceProvider.GetRequiredService<SecretService>();
|
||||
var daprApiToken = secretService.GetSecret("DaprApiToken").Value;
|
||||
|
||||
//Configure the `DaprPublishSubscribeClientBuilder`
|
||||
daprPubSubClientBuilder.UseDaprApiToken(daprApiToken);
|
||||
});
|
||||
|
||||
var app = builder.Build();
|
||||
```
|
|
@ -2,6 +2,6 @@
|
|||
type: docs
|
||||
title: "How to troubleshoot and debug with the Dapr .NET SDK"
|
||||
linkTitle: "Troubleshooting"
|
||||
weight: 100000
|
||||
weight: 120000
|
||||
description: Tips, tricks, and guides for troubleshooting and debugging with the Dapr .NET SDKs
|
||||
---
|
|
@ -18,11 +18,16 @@ In the .NET example project:
|
|||
|
||||
## 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://github.com/dapr/dotnet-sdk/)
|
||||
- [.NET 7](https://dotnet.microsoft.com/download/dotnet/7.0), [.NET 8](https://dotnet.microsoft.com/download/dotnet/8.0) or [.NET 9](https://dotnet.microsoft.com/download/dotnet/9.0) installed
|
||||
|
||||
{{% alert title="Note" color="primary" %}}
|
||||
|
||||
Dapr.Workflows supports .NET 7 or newer in v1.15. However, following the release of Dapr v1.16, only
|
||||
.NET 8 and .NET 9 will be supported.
|
||||
|
||||
{{% /alert %}}
|
||||
|
||||
## Set up the environment
|
||||
|
||||
|
@ -83,7 +88,7 @@ Run the following command to start a workflow.
|
|||
{{% codetab %}}
|
||||
|
||||
```bash
|
||||
curl -i -X POST http://localhost:3500/v1.0-beta1/workflows/dapr/OrderProcessingWorkflow/start?instanceID=12345678 \
|
||||
curl -i -X POST http://localhost:3500/v1.0/workflows/dapr/OrderProcessingWorkflow/start?instanceID=12345678 \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"Name": "Paperclips", "TotalCost": 99.95, "Quantity": 1}'
|
||||
```
|
||||
|
@ -93,7 +98,7 @@ curl -i -X POST http://localhost:3500/v1.0-beta1/workflows/dapr/OrderProcessingW
|
|||
{{% codetab %}}
|
||||
|
||||
```powershell
|
||||
curl -i -X POST http://localhost:3500/v1.0-beta1/workflows/dapr/OrderProcessingWorkflow/start?instanceID=12345678 `
|
||||
curl -i -X POST http://localhost:3500/v1.0/workflows/dapr/OrderProcessingWorkflow/start?instanceID=12345678 `
|
||||
-H "Content-Type: application/json" `
|
||||
-d '{"Name": "Paperclips", "TotalCost": 99.95, "Quantity": 1}'
|
||||
```
|
||||
|
@ -111,7 +116,7 @@ If successful, you should see a response like the following:
|
|||
Send an HTTP request to get the status of the workflow that was started:
|
||||
|
||||
```bash
|
||||
curl -i -X GET http://localhost:3500/v1.0-beta1/workflows/dapr/12345678
|
||||
curl -i -X GET http://localhost:3500/v1.0/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`:
|
||||
|
|
|
@ -0,0 +1,137 @@
|
|||
---
|
||||
type: docs
|
||||
title: "DaprWorkflowClient usage"
|
||||
linkTitle: "DaprWorkflowClient usage"
|
||||
weight: 100000
|
||||
description: Essential tips and advice for using DaprWorkflowClient
|
||||
---
|
||||
|
||||
## Lifetime management
|
||||
|
||||
A `DaprWorkflowClient` holds access to networking resources in the form of TCP sockets used to communicate with the Dapr sidecar as well
|
||||
as other types used in the management and operation of Workflows. `DaprWorkflowClient` implements `IAsyncDisposable` to support eager
|
||||
cleanup of resources.
|
||||
|
||||
## Dependency Injection
|
||||
|
||||
The `AddDaprWorkflow()` method will register the Dapr workflow services with ASP.NET Core dependency injection. This method
|
||||
requires an options delegate that defines each of the workflows and activities you wish to register and use in your application.
|
||||
|
||||
{{% alert title="Note" color="primary" %}}
|
||||
|
||||
This method will attempt to register a `DaprClient` instance, but this will only work if it hasn't already been registered with another
|
||||
lifetime. For example, an earlier call to `AddDaprClient()` with a singleton lifetime will always use a singleton regardless of the
|
||||
lifetime chose for the workflow client. The `DaprClient` instance will be used to communicate with the Dapr sidecar and if it's not
|
||||
yet registered, the lifetime provided during the `AddDaprWorkflow()` registration will be used to register the `DaprWorkflowClient`
|
||||
as well as its own dependencies.
|
||||
|
||||
{{% /alert %}}
|
||||
|
||||
### Singleton Registration
|
||||
By default, the `AddDaprWorkflow` method will register the `DaprWorkflowClient` and associated services using a singleton lifetime. This means
|
||||
that the services will be instantiated only a single time.
|
||||
|
||||
The following is an example of how registration of the `DaprWorkflowClient` as it would appear in a typical `Program.cs` file:
|
||||
|
||||
```csharp
|
||||
builder.Services.AddDaprWorkflow(options => {
|
||||
options.RegisterWorkflow<YourWorkflow>();
|
||||
options.RegisterActivity<YourActivity>();
|
||||
});
|
||||
|
||||
var app = builder.Build();
|
||||
await app.RunAsync();
|
||||
```
|
||||
|
||||
### Scoped Registration
|
||||
|
||||
While this may generally be acceptable in your use case, you may instead wish to override the lifetime specified. This is done by passing a `ServiceLifetime`
|
||||
argument in `AddDaprWorkflow`. For example, you may wish to inject another scoped service into your ASP.NET Core processing pipeline
|
||||
that needs context used by the `DaprClient` that wouldn't be available if the former service were registered as a singleton.
|
||||
|
||||
This is demonstrated in the following example:
|
||||
|
||||
```csharp
|
||||
builder.Services.AddDaprWorkflow(options => {
|
||||
options.RegisterWorkflow<YourWorkflow>();
|
||||
options.RegisterActivity<YourActivity>();
|
||||
}, ServiceLifecycle.Scoped);
|
||||
|
||||
var app = builder.Build();
|
||||
await app.RunAsync();
|
||||
```
|
||||
|
||||
### Transient Registration
|
||||
|
||||
Finally, Dapr services can also be registered using a transient lifetime meaning that they will be initialized every time they're injected. This
|
||||
is demonstrated in the following example:
|
||||
|
||||
```csharp
|
||||
builder.Services.AddDaprWorkflow(options => {
|
||||
options.RegisterWorkflow<YourWorkflow>();
|
||||
options.RegisterActivity<YourActivity>();
|
||||
}, ServiceLifecycle.Transient);
|
||||
|
||||
var app = builder.Build();
|
||||
await app.RunAsync();
|
||||
```
|
||||
|
||||
## Injecting Services into Workflow Activities
|
||||
Workflow activities support the same dependency injection that developers have come to expect of modern C# applications. Assuming a proper
|
||||
registration at startup, any such type can be injected into the constructor of the workflow activity and available to utilize during
|
||||
the execution of the workflow. This makes it simple to add logging via an injected `ILogger` or access to other Dapr
|
||||
building blocks by injecting `DaprClient` or `DaprJobsClient`, for example.
|
||||
|
||||
```csharp
|
||||
internal sealed class SquareNumberActivity : WorkflowActivity<int, int>
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
|
||||
public MyActivity(ILogger logger)
|
||||
{
|
||||
this._logger = logger;
|
||||
}
|
||||
|
||||
public override Task<int> RunAsync(WorkflowActivityContext context, int input)
|
||||
{
|
||||
this._logger.LogInformation("Squaring the value {number}", input);
|
||||
var result = input * input;
|
||||
this._logger.LogInformation("Got a result of {squareResult}", result);
|
||||
|
||||
return Task.FromResult(result);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Using ILogger in Workflow
|
||||
Because workflows must be deterministic, it is not possible to inject arbitrary services into them. For example,
|
||||
if you were able to inject a standard `ILogger` into a workflow and it needed to be replayed because of an error,
|
||||
subsequent replay from the event source log would result in the log recording additional operations that didn't actually
|
||||
take place a second or third time because their results were sourced from the log. This has the potential to introduce
|
||||
a significant amount of confusion. Rather, a replay-safe logger is made available for use within workflows. It will only
|
||||
log events the first time the workflow runs and will not log anything whenever the workflow is being replaced.
|
||||
|
||||
This logger can be retrieved from a method present on the `WorkflowContext` available on your workflow instance and
|
||||
otherwise used precisely as you might otherwise use an `ILogger` instance.
|
||||
|
||||
An end-to-end sample demonstrating this can be seen in the
|
||||
[.NET SDK repository](https://github.com/dapr/dotnet-sdk/blob/master/examples/Workflow/WorkflowConsoleApp/Workflows/OrderProcessingWorkflow.cs)
|
||||
but a brief extraction of this sample is available below.
|
||||
|
||||
```csharp
|
||||
public class OrderProcessingWorkflow : Workflow<OrderPayload, OrderResult>
|
||||
{
|
||||
public override async Task<OrderResult> RunAsync(WorkflowContext context, OrderPayload order)
|
||||
{
|
||||
string orderId = context.InstanceId;
|
||||
var logger = context.CreateReplaySafeLogger<OrderProcessingWorkflow>(); //Use this method to access the logger instance
|
||||
|
||||
logger.LogInformation("Received order {orderId} for {quantity} {name} at ${totalCost}", orderId, order.Quantity, order.Name, order.TotalCost);
|
||||
|
||||
//...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
# Dapr .NET SDK Release Process
|
||||
|
||||
> This information is intended for SDK maintainers. SDK users can ignore this document.
|
||||
|
||||
## Publish a SDK Release Candidate (RC)
|
||||
|
||||
RC release versions canonically use the form `<version>-rc<iteration>` where `<version>` represents the overall release version (e.g. `1.0`) and `<iteration>` represents a specific iteration of RC release (e.g. `01`, `02`, ..., `0n`).
|
||||
|
||||
Assume we intend to release `<version>` (e.g. `1.0-rc01`) of the SDK.
|
||||
|
||||
1. Create a release branch (if not already done) from `master`
|
||||
|
||||
```bash
|
||||
git checkout -b release-<version>
|
||||
```
|
||||
|
||||
1. Push the release branch to the `dotnet-sdk` repo (i.e. typically `origin`)
|
||||
|
||||
```bash
|
||||
git push origin v<version>
|
||||
```
|
||||
|
||||
1. Create a tag on the release branch for the RC version
|
||||
|
||||
```bash
|
||||
git tag v<version>-rc<iteration>
|
||||
```
|
||||
|
||||
1. Push the tag to the `dotnet-sdk` repo (i.e. typically `origin`)
|
||||
|
||||
```bash
|
||||
git push origin v<version>-rc<iteration>
|
||||
```
|
||||
|
||||
> This final step will generate a build and automatically publish the resulting packages to NuGet.
|
||||
|
||||
## Publish a SDK Release
|
||||
|
||||
Official (i.e. supported) release versions canonically use the form `<version>` where `<version>` represents the overall release version (e.g. `1.0`).
|
||||
|
||||
1. Create a release branch (if not already done) from `master`
|
||||
|
||||
```bash
|
||||
git checkout -b release-<version>
|
||||
```
|
||||
|
||||
1. Push the release branch to the `dotnet-sdk` repo (i.e. typically `origin`)
|
||||
|
||||
```bash
|
||||
git push origin v<version>
|
||||
```
|
||||
|
||||
1. Create a tag on the release branch for the release
|
||||
|
||||
```bash
|
||||
git tag v<version>
|
||||
```
|
||||
|
||||
1. Push the tag to the `dotnet-sdk` repo (i.e. typically `origin`)
|
||||
|
||||
```bash
|
||||
git push origin v<version>
|
||||
```
|
||||
|
||||
> This final step will generate a build and automatically publish the resulting packages to NuGet.
|
||||
|
||||
## NuGet Package Publishing
|
||||
|
||||
Publishing to NuGet requires keys generated by a member of the Dapr organization. Such keys are added as a [GitHub Action secret](https://github.com/dapr/dotnet-sdk/settings/secrets/actions) with the name `NUGETORG_DAPR_API_KEY` These keys expire and therefore must be maintained and the GitHub Actions secret updated periodically.
|
|
@ -0,0 +1,13 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\src\Dapr.AI\Dapr.AI.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -0,0 +1,23 @@
|
|||
using Dapr.AI.Conversation;
|
||||
using Dapr.AI.Conversation.Extensions;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
builder.Services.AddDaprConversationClient();
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
var conversationClient = app.Services.GetRequiredService<DaprConversationClient>();
|
||||
var response = await conversationClient.ConverseAsync("conversation",
|
||||
new List<DaprConversationInput>
|
||||
{
|
||||
new DaprConversationInput(
|
||||
"Please write a witty haiku about the Dapr distributed programming framework at dapr.io",
|
||||
DaprConversationRole.Generic)
|
||||
});
|
||||
|
||||
Console.WriteLine("Received the following from the LLM:");
|
||||
foreach (var resp in response.Outputs)
|
||||
{
|
||||
Console.WriteLine($"\t{resp.Result}");
|
||||
}
|
|
@ -11,6 +11,9 @@
|
|||
// limitations under the License.
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
using Dapr.Actors.Communication;
|
||||
using IDemoActor;
|
||||
|
||||
namespace ActorClient
|
||||
{
|
||||
using System;
|
||||
|
@ -18,7 +21,6 @@ namespace ActorClient
|
|||
using System.Threading.Tasks;
|
||||
using Dapr.Actors;
|
||||
using Dapr.Actors.Client;
|
||||
using IDemoActorInterface;
|
||||
|
||||
/// <summary>
|
||||
/// Actor Client class.
|
||||
|
@ -43,7 +45,7 @@ namespace ActorClient
|
|||
|
||||
// Make strongly typed Actor calls with Remoting.
|
||||
// DemoActor is the type registered with Dapr runtime in the service.
|
||||
var proxy = ActorProxy.Create<IDemoActor>(actorId, "DemoActor");
|
||||
var proxy = ActorProxy.Create<IDemoActor.IDemoActor>(actorId, "DemoActor");
|
||||
|
||||
Console.WriteLine("Making call using actor proxy to save data.");
|
||||
await proxy.SaveData(data, TimeSpan.FromMinutes(10));
|
||||
|
@ -83,7 +85,7 @@ namespace ActorClient
|
|||
var nonRemotingProxy = ActorProxy.Create(actorId, "DemoActor");
|
||||
await nonRemotingProxy.InvokeMethodAsync("TestNoArgumentNoReturnType");
|
||||
await nonRemotingProxy.InvokeMethodAsync("SaveData", data);
|
||||
var res = await nonRemotingProxy.InvokeMethodAsync<MyData>("GetData");
|
||||
await nonRemotingProxy.InvokeMethodAsync<MyData>("GetData");
|
||||
|
||||
Console.WriteLine("Registering the timer and reminder");
|
||||
await proxy.RegisterTimer();
|
||||
|
|
|
@ -11,9 +11,9 @@
|
|||
// limitations under the License.
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
using IDemoActorInterface;
|
||||
using IDemoActor;
|
||||
|
||||
namespace DaprDemoActor
|
||||
namespace DemoActor
|
||||
{
|
||||
public class BankService
|
||||
{
|
||||
|
|
|
@ -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";
|
||||
|
||||
|
|
|
@ -1,13 +1,24 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6</TargetFramework>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\src\Dapr.Actors.AspNetCore\Dapr.Actors.AspNetCore.csproj" />
|
||||
<ProjectReference Include="..\..\..\src\Dapr.Actors\Dapr.Actors.csproj" />
|
||||
<ProjectReference Include="..\IDemoActor\IDemoActor.csproj" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup>
|
||||
<IsPublishable>true</IsPublishable>
|
||||
<EnableSdkContainerSupport>true</EnableSdkContainerSupport>
|
||||
<ContainerRepository>demo-actor</ContainerRepository>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ContainerEnvironmentVariable Include="ASPNETCORE_URLS" Value="http://+:8080" />
|
||||
<ContainerPort Include="8080" Type="tcp" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\src\Dapr.Actors.AspNetCore\Dapr.Actors.AspNetCore.csproj" />
|
||||
<ProjectReference Include="..\..\..\src\Dapr.Actors\Dapr.Actors.csproj" />
|
||||
<ProjectReference Include="..\IDemoActor\IDemoActor.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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: <your-docker-registry>/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
|
|
@ -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<AccountBalance> GetAccountBalance();
|
||||
|
|
|
@ -11,13 +11,12 @@
|
|||
// limitations under the License.
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
namespace IDemoActorInterface
|
||||
{
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Dapr.Actors;
|
||||
using Dapr.Actors.Runtime;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Dapr.Actors;
|
||||
|
||||
namespace IDemoActor
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface for Actor method.
|
||||
/// </summary>
|
||||
|
|
|
@ -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 <your-docker-registry> with your Docker registry
|
||||
docker tag demo-actor:latest <your-docker-registry>/demo-actor:latest
|
||||
|
||||
# Push the image to your Docker registry
|
||||
docker push <your-docker-registry>/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: <your-docker-registry>/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
|
||||
```
|
|
@ -11,6 +11,8 @@
|
|||
// limitations under the License.
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
using System.Linq;
|
||||
|
||||
namespace ControllerSample.Controllers
|
||||
{
|
||||
using System;
|
||||
|
@ -43,6 +45,7 @@ namespace ControllerSample.Controllers
|
|||
/// State store name.
|
||||
/// </summary>
|
||||
public const string StoreName = "statestore";
|
||||
|
||||
private readonly ILogger<SampleController> logger;
|
||||
|
||||
/// <summary>
|
||||
|
@ -72,6 +75,11 @@ namespace ControllerSample.Controllers
|
|||
[HttpPost("deposit")]
|
||||
public async Task<ActionResult<Account>> 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<Account>(StoreName, transaction.Id);
|
||||
state.Value ??= new Account() { Id = transaction.Id, };
|
||||
|
@ -83,7 +91,7 @@ namespace ControllerSample.Controllers
|
|||
}
|
||||
|
||||
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 @@ namespace ControllerSample.Controllers
|
|||
[Topic("pubsub", "multideposit", "amountDeadLetterTopic", false)]
|
||||
[BulkSubscribe("multideposit", 500, 2000)]
|
||||
[HttpPost("multideposit")]
|
||||
public async Task<ActionResult<BulkSubscribeAppResponse>> MultiDeposit([FromBody] BulkSubscribeMessage<BulkMessageModel<Transaction>>
|
||||
bulkMessage, [FromServices] DaprClient daprClient)
|
||||
public async Task<ActionResult<BulkSubscribeAppResponse>> MultiDeposit([FromBody]
|
||||
BulkSubscribeMessage<BulkMessageModel<Transaction>>
|
||||
bulkMessage, [FromServices] DaprClient daprClient)
|
||||
{
|
||||
logger.LogInformation("Enter bulk deposit");
|
||||
|
||||
|
||||
List<BulkSubscribeAppResponseEntry> entries = new List<BulkSubscribeAppResponseEntry>();
|
||||
|
||||
foreach (var entry in bulkMessage.Entries)
|
||||
{
|
||||
{
|
||||
try
|
||||
{
|
||||
var transaction = entry.Event.Data;
|
||||
|
||||
var state = await daprClient.GetStateEntryAsync<Account>(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 @@ namespace ControllerSample.Controllers
|
|||
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 @@ namespace ControllerSample.Controllers
|
|||
{
|
||||
return this.NotFound();
|
||||
}
|
||||
|
||||
if (transaction.Amount < 0m)
|
||||
{
|
||||
return BadRequest(new { statusCode = 400, message = "bad request" });
|
||||
|
@ -185,7 +199,8 @@ namespace ControllerSample.Controllers
|
|||
/// "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<ActionResult<Account>> WithdrawV2(TransactionV2 transaction, [FromServices] DaprClient daprClient)
|
||||
public async Task<ActionResult<Account>> WithdrawV2(TransactionV2 transaction,
|
||||
[FromServices] DaprClient daprClient)
|
||||
{
|
||||
logger.LogInformation("Enter withdraw.v2");
|
||||
if (transaction.Channel == "mobile" && transaction.Amount > 10000)
|
||||
|
@ -214,12 +229,15 @@ namespace ControllerSample.Controllers
|
|||
/// "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<ActionResult<Account>> RawDeposit([FromBody] JsonDocument rawTransaction, [FromServices] DaprClient daprClient)
|
||||
public async Task<ActionResult<Account>> 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<JsonDocument>(Convert.FromBase64String(transactionString));
|
||||
var transaction = JsonSerializer.Deserialize<Transaction>(transactionJson.RootElement.GetProperty("data").GetRawText());
|
||||
var transaction =
|
||||
JsonSerializer.Deserialize<Transaction>(transactionJson.RootElement.GetProperty("data").GetRawText());
|
||||
var state = await daprClient.GetStateEntryAsync<Account>(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 @@ namespace ControllerSample.Controllers
|
|||
/// Method for returning a BadRequest result which will cause Dapr sidecar to throw an RpcException
|
||||
/// </summary>
|
||||
[HttpPost("throwException")]
|
||||
public async Task<ActionResult<Account>> ThrowException(Transaction transaction, [FromServices] DaprClient daprClient)
|
||||
public async Task<ActionResult<Account>> ThrowException(Transaction transaction,
|
||||
[FromServices] DaprClient daprClient)
|
||||
{
|
||||
logger.LogInformation("Enter ThrowException");
|
||||
var task = Task.Delay(10);
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
// limitations under the License.
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
using Dapr.AspNetCore;
|
||||
|
||||
namespace ControllerSample
|
||||
{
|
||||
using System;
|
||||
|
|
|
@ -11,6 +11,11 @@
|
|||
// limitations under the License.
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
|
||||
using Dapr;
|
||||
using Dapr.AspNetCore;
|
||||
|
||||
|
||||
namespace ControllerSample
|
||||
{
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
|
@ -61,7 +66,10 @@ namespace ControllerSample
|
|||
|
||||
app.UseRouting();
|
||||
|
||||
app.UseCloudEvents();
|
||||
app.UseCloudEvents(new CloudEventsMiddlewareOptions
|
||||
{
|
||||
ForwardCloudEventPropertiesAsHeaders = true
|
||||
});
|
||||
|
||||
app.UseAuthorization();
|
||||
|
||||
|
|
|
@ -10,15 +10,16 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Google.Protobuf" Version="3.19.4" />
|
||||
<PackageReference Include="Grpc.AspNetCore" Version="2.47.0" />
|
||||
<PackageReference Include="Grpc.Net.Client" Version="2.52.0" />
|
||||
<PackageReference Include="Grpc.Tools" Version="2.47.0" PrivateAssets="All" />
|
||||
<PackageReference Include="Google.Api.CommonProtos" Version="2.2.0" />
|
||||
<PackageReference Include="Google.Protobuf" />
|
||||
<PackageReference Include="Grpc.AspNetCore" />
|
||||
<PackageReference Include="Grpc.Net.Client" />
|
||||
<PackageReference Include="Grpc.Tools" PrivateAssets="All" />
|
||||
<PackageReference Include="Google.Api.CommonProtos" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\src\Dapr.AspNetCore\Dapr.AspNetCore.csproj" />
|
||||
<ProjectReference Include="..\..\..\src\Dapr.Protos\Dapr.Protos.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
@ -11,11 +11,6 @@
|
|||
// limitations under the License.
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
|
|
|
@ -22,7 +22,7 @@ using Grpc.Core;
|
|||
using GrpcServiceSample.Generated;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace GrpcServiceSample
|
||||
namespace GrpcServiceSample.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// BankAccount gRPC service
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -221,7 +221,6 @@ namespace RoutingSample
|
|||
|
||||
async Task ViewErrorMessage(HttpContext context)
|
||||
{
|
||||
var client = context.RequestServices.GetRequiredService<DaprClient>();
|
||||
var transaction = await JsonSerializer.DeserializeAsync<Transaction>(context.Request.Body, serializerOptions);
|
||||
|
||||
logger.LogInformation("The amount cannot be negative: {0}", transaction.Amount);
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
using Dapr.Client;
|
||||
using Dapr.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -20,7 +20,7 @@ To load secrets into configuration call the _AddDaprSecretStore_ extension metho
|
|||
Use Dapr to run the application:
|
||||
|
||||
```shell
|
||||
dapr run --app-id SecretStoreConfigurationProviderSample --components-path ./components/ -- dotnet run
|
||||
dapr run --app-id SecretStoreConfigurationProviderSample --resources-path ./components/ -- dotnet run
|
||||
```
|
||||
|
||||
### 2. Test the application
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\src\Dapr.Client\Dapr.Client.csproj" />
|
||||
<ProjectReference Include="..\..\..\src\Dapr.AspNetCore\Dapr.AspNetCore.csproj" />
|
||||
<ProjectReference Include="..\..\..\src\Dapr.Common\Dapr.Common.csproj" />
|
||||
<ProjectReference Include="..\..\..\src\Dapr.Extensions.Configuration\Dapr.Extensions.Configuration.csproj" />
|
||||
<ProjectReference Include="..\..\AspNetCore\ControllerSample\ControllerSample.csproj" />
|
||||
</ItemGroup>
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using ControllerSample;
|
||||
using Dapr;
|
||||
|
|
|
@ -4,7 +4,6 @@ using Microsoft.Extensions.Hosting;
|
|||
using Dapr.Client;
|
||||
using Dapr.Extensions.Configuration;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
|
||||
namespace ConfigurationApi
|
||||
{
|
||||
|
|
|
@ -147,7 +147,7 @@ cd examples/Client/ConfigurationApi
|
|||
To run the `ConfigurationExample`, execute the following command:
|
||||
|
||||
```bash
|
||||
dapr run --app-id configexample --components-path ./Components -- dotnet run
|
||||
dapr run --app-id configexample --resources-path ./Components -- dotnet run
|
||||
```
|
||||
|
||||
### Get Configuration
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
using System;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
|
||||
namespace ConfigurationApi
|
||||
{
|
||||
|
|
|
@ -1,25 +0,0 @@
|
|||
apiVersion: dapr.io/v1alpha1
|
||||
kind: Component
|
||||
metadata:
|
||||
name: azurekeyvault
|
||||
spec:
|
||||
type: crypto.azure.keyvault
|
||||
metadata:
|
||||
- name: vaultName
|
||||
value: "<changeMe>"
|
||||
- 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
|
|
@ -1,7 +0,0 @@
|
|||
apiVersion: dapr.io/v1alpha1
|
||||
kind: Component
|
||||
metadata:
|
||||
name: envvar-secret-store
|
||||
spec:
|
||||
type: secretstores.local.env
|
||||
version: v1
|
|
@ -0,0 +1,11 @@
|
|||
apiVersion: dapr.io/v1alpha1
|
||||
kind: Component
|
||||
metadata:
|
||||
name: localstorage
|
||||
spec:
|
||||
type: crypto.dapr.localstorage
|
||||
version: v1
|
||||
metadata:
|
||||
- name: path
|
||||
# Path is relative to the folder where the example is located
|
||||
value: ./keys
|
|
@ -17,16 +17,13 @@ using Dapr.Client;
|
|||
|
||||
namespace Cryptography.Examples
|
||||
{
|
||||
internal class EncryptDecryptFileStreamExample : Example
|
||||
internal class EncryptDecryptFileStreamExample(string componentName, string keyName) : 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";
|
||||
|
||||
|
@ -35,21 +32,19 @@ namespace Cryptography.Examples
|
|||
{
|
||||
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<byte>();
|
||||
await foreach (var bytes in (await client.EncryptAsync(componentName, encryptFs, keyName,
|
||||
new EncryptionOptions(KeyWrapAlgorithm.Rsa), cancellationToken))
|
||||
.WithCancellation(cancellationToken))
|
||||
await foreach (var bytes in (client.EncryptAsync(componentName, encryptFs, keyName,
|
||||
new EncryptionOptions(KeyWrapAlgorithm.Rsa), cancellationToken)))
|
||||
{
|
||||
bufferedEncryptedBytes.Write(bytes.Span);
|
||||
}
|
||||
|
||||
Console.WriteLine($"Encrypted bytes: {Convert.ToBase64String(bufferedEncryptedBytes.GetSpan())}");
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("Encrypted bytes:");
|
||||
Console.WriteLine(Convert.ToBase64String(bufferedEncryptedBytes.WrittenMemory.ToArray()));
|
||||
|
||||
//We'll write to a temporary file via a FileStream
|
||||
var tempDecryptedFile = Path.GetTempFileName();
|
||||
|
@ -57,8 +52,8 @@ namespace Cryptography.Examples
|
|||
|
||||
//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))
|
||||
await foreach (var result in (client.DecryptAsync(componentName, encryptedMs, keyName,
|
||||
cancellationToken)))
|
||||
{
|
||||
decryptFs.Write(result.Span);
|
||||
}
|
||||
|
@ -67,7 +62,7 @@ namespace Cryptography.Examples
|
|||
|
||||
//Let's confirm the value as written to the file
|
||||
var decryptedValue = await File.ReadAllTextAsync(tempDecryptedFile, cancellationToken);
|
||||
Console.WriteLine($"Decrypted value: ");
|
||||
Console.WriteLine("Decrypted value: ");
|
||||
Console.WriteLine(decryptedValue);
|
||||
|
||||
//And some cleanup to delete our temp file
|
||||
|
|
|
@ -17,17 +17,13 @@ using Dapr.Client;
|
|||
|
||||
namespace Cryptography.Examples
|
||||
{
|
||||
internal class EncryptDecryptStringExample : Example
|
||||
internal class EncryptDecryptStringExample(string componentName, string keyName) : 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}'");
|
||||
|
@ -40,7 +36,7 @@ namespace Cryptography.Examples
|
|||
Console.WriteLine($"Encrypted bytes: '{Convert.ToBase64String(encryptedBytesResult.Span)}'");
|
||||
|
||||
//Decrypt the string
|
||||
var decryptedBytes = await client.DecryptAsync(componentName, encryptedBytesResult, keyName, new DecryptionOptions(), cancellationToken);
|
||||
var decryptedBytes = await client.DecryptAsync(componentName, encryptedBytesResult, keyName, cancellationToken);
|
||||
Console.WriteLine($"Decrypted string: '{Encoding.UTF8.GetString(decryptedBytes.ToArray())}'");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,17 +11,19 @@
|
|||
// limitations under the License.
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
using Cryptography;
|
||||
using Cryptography.Examples;
|
||||
|
||||
namespace Samples.Client
|
||||
namespace Cryptography
|
||||
{
|
||||
class Program
|
||||
{
|
||||
private const string ComponentName = "localstorage";
|
||||
private const string KeyName = "rsa-private-key.pem"; //This should match the name of your generated key - this sample expects an RSA symmetrical key.
|
||||
|
||||
private static readonly Example[] Examples = new Example[]
|
||||
{
|
||||
new EncryptDecryptStringExample(),
|
||||
new EncryptDecryptFileStreamExample()
|
||||
new EncryptDecryptStringExample(ComponentName, KeyName),
|
||||
new EncryptDecryptFileStreamExample(ComponentName, KeyName)
|
||||
};
|
||||
|
||||
static async Task<int> Main(string[] args)
|
||||
|
@ -35,7 +37,7 @@ namespace Samples.Client
|
|||
return 0;
|
||||
}
|
||||
|
||||
Console.WriteLine("Hello, please choose a sample to run:");
|
||||
Console.WriteLine("Hello, please choose a sample to run by passing your selection's number into the arguments, e.g. 'dotnet run 0':");
|
||||
for (var i = 0; i < Examples.Length; i++)
|
||||
{
|
||||
Console.WriteLine($"{i}: {Examples[i].DisplayName}");
|
||||
|
|
|
@ -50,6 +50,21 @@ button. Ensuring that the "User, group or service principal" option is selected,
|
|||
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.
|
||||
|
||||
## Generating the Keys
|
||||
This sample requires a private RSA key to be generated and placed in the `/keys` directory within the project.
|
||||
If you have OpenSSL installed on your machine, you can generate the key by navigating first
|
||||
into the project directory and then running the following command:
|
||||
|
||||
```bash
|
||||
# Generates a private RSA 40960-bit key named 'rsa-private-key.pem'
|
||||
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:4096 -out keys/rsa-private-key.pem
|
||||
```
|
||||
|
||||
> **WARNING: This RSA key is included in this project strictly for demonstration and testing purposes.**
|
||||
> - Do **NOT** use this key in any production environment or for any real-world applications.
|
||||
> - This key is publicly available and should be considered compromised.
|
||||
> - Generating and using your own secure keys is essential for maintaining security in your projects.
|
||||
|
||||
## Running the example
|
||||
|
||||
To run the sample locally, run this command in the DaprClient directory:
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
-----BEGIN PRIVATE KEY-----
|
||||
MIIJQQIBADANBgkqhkiG9w0BAQEFAASCCSswggknAgEAAoICAQC0URLpxZCqDv7S
|
||||
WfROh2Kei4VCEayNu/TK3NaD/QlIpip1rrsPKgTfTOZoRmkmG0Qj59srEJi2GEhL
|
||||
xpjvRQpA/C/OS+KELU8AeGrqHw7uN/a99NkoAr+zYDCyY9yckPeC5wGxc0/Q6HQT
|
||||
mWp+YcpR9wFO0PmTVlObssibagjjRNX7z/ZosecOOqjnAqlnYoHMavvoCD5fxM7y
|
||||
cm7so0JWooXwVaZKgehBEBg1W5F0q5e9ssAQk3lY6IUd5sOskiylTNf/+3r1JU0j
|
||||
YM8ik3a1/dyDALVXpLSfz7FM9VEj4QjiPF4UuXeBHPDFFiKWbiKfbjqvZ2Sz7Gl7
|
||||
c5rTk1Fozpr70E/wihrrv22Mxs0sEPdtemQgHXroQfRW8K4FhI0WHs7tR2gVxLHu
|
||||
OAU9LzCngz4yITh1eixVDmm/B5ZtNVrTQmaY84vGqhrFp+asyFNiXbhUAcT7D/q6
|
||||
w/c4aQ635ntCFSPYpWvhKqrqVDsoanD/5AWfc3+6Ek2/GVMyEQq+9tnCMM10EVSX
|
||||
8PsoAWHESDFude5zkHzn7IKy8mh6lfheEbBI5zN9z7WGexyiBgljmyUHXx6Pd8Uc
|
||||
yxpLRm94kynkDXD9SapQLzXmz+D+X/OYeADMIDWlbdXiIb1+2Q62H1lo6n10KVP7
|
||||
oEr8BHvcMFY89kwK4lKscUupn8xkzwIDAQABAoICACDuu78Rc8Hzeivt/PZIuMTP
|
||||
I5f1BWhffy571fwGP2dS3edfcc+rs3cbIuvBjFvG2BOcuYUsg0+isLWSQIVWvTAw
|
||||
PwT1DBpq8gZad+Bpqr7sXrbD3NN3aQ64TzyNi5HW0jXIviDsOBQmGGkp+G67qol8
|
||||
zPLZrPNxbVS++u+Tlqr3fAOBMHZfo50QLp/+dvUoYx90HKz8sHOqTMewCb1Tdf6/
|
||||
sSm7YuMxxbr4VwuLvU2rN0wQtQ5x+NQ5p3JWHr/KdLf+CGc6xXK3jNaczEf62dAU
|
||||
XO1aOESZEtorQy0Ukuy0IXy8XMx5MS/WGs1MJSYHWHB43+QARL6tu3guHYVt3wyv
|
||||
W6YTglQsSKc6uuK4JTZOx1VYZjjnSdeY/xiUmZGYp4ZiC9p8b9NvXmZT2EwqhCVt
|
||||
4OTcX4lkwGAsKcoEdLHi0K5CbBfYJsRgVVheDjP0xUFjCJCYqfqo2rE5YMXMTeY7
|
||||
clYEOXKGxwuy1Iu8nKqtWAV5r/eSmXBdxBqEBW9oxJfnnwNPG+yOk0Qkd1vaRj00
|
||||
mdKCOjgB2fOuPX2JRZ2z41Cem3gqhH0NQGrx3APV4egGrYAMClasgtZkUeUOIgK5
|
||||
xLlC/6svuHNyKXAKFpOubEy1FM8jz7111eNHxHRDP3+vH3u4CfAD2Sl+VDZdg51i
|
||||
WmVpT+B/DrnlHVSP2/XNAoIBAQD7F49oSdveKuO/lAyqkE9iF61i09G0b0ouDGUI
|
||||
qx+pd5/8vUcqi4upCxz+3AqMPWZRIqOyo8EUP7f4rSJrXn8U2SwnFfi4k2jiqmEA
|
||||
Wr0b8z5P1q5MH6BtVDa0Sr1R8xI9s3UgIs4pUKgBoQu9+U4Du4NSucQFcea8nIVY
|
||||
lLCqQcRhz8bCJPCNuHay5c77kK3Te197KPMasNurTNMOJcPMG95CZLB8Clf4A+pw
|
||||
fixvA1/fE4mFo1L7Ymxoz5lFYVWOTY9hh50Kqz57wxw4laU4ii+MaJj+YHuNR83N
|
||||
cO6FztUYKMR8BPgtl3/POTHTofSg7eIOiUYwcfRr6jbMWlsDAoIBAQC311xiMpho
|
||||
Hvdcvp3/urrIp2QhdD05n6TnZOPkpnd9kwGku2RA+occDQOg/BzADVwJaR/aE97F
|
||||
jbfRlfBesTZlUec0EwjKIFbeYh+QS/RmjQe9zpPQWMo1M7y0fMWU+yXRUcNBpcuy
|
||||
R6KlphK0k4xFkIAdC3QHmJQ0XvOpqvrhFy3i/Prc5Wlg29FYBBTAF0WZCZ4uCG34
|
||||
D0eG0CNaf8w9g9ClbU6nGLBCMcgjEOPYfyrJaedM+jXennLDPG6ySytrGwnwLAQc
|
||||
Okx+SrIiNHUpQGKteT88Kdpgo3F4KUX/pm84uGdxrOpDS7L0T9/G4CbjzCe1nHeS
|
||||
fJJsw5JN+Z9FAoIBAGn5S6FsasudtnnI9n+WYKq564fmdn986QX+XTYHY1mXD4MQ
|
||||
L9UZCFzUP+yg2iLOVzyvLf/bdUYijnb6O6itPV2DO0tTzqG4NXBVEJOhuGbvhsET
|
||||
joS6ZG9AN8ZoNPc9a9l2wFxL1E9Dp2Ton5gSfIa+wXJMzRqvM/8u4Gi+eMGi+Et/
|
||||
8hdGl/B4hkCDFZS/P14el/HXGqONOWlXB0zVS4n9yRSkgogXpYEbxfqshfxkpDX2
|
||||
fPhWMlO++ppR5BKQPhfNTFKRdgpms/xwIJ0RK6ZtTBwqmUfjWMIMKCQpIcJ/xRhp
|
||||
PGRLhKNZaawAK7Nyi1jQjbQs497WeZ6CP5aIHBkCggEALHyl83FQ5ilQLJZH/6E9
|
||||
H9854MqTIkWajxAgAa2yzqVrSWS7XuoBFe2kSimX/3V8Jx7UQV57kwy3RbVl5FQ3
|
||||
2I7YRwawItFulAPkpXNr4gEQtYKuzEUgMX2ilX54BZQ804lYmaM4Rp0FI9arQh1O
|
||||
XWsZRW4HFut6Oa4cgptIeH22ce5L+nZdaL3oy8a5Cr7W7bChIXySt+tioKHvXC/+
|
||||
yYgDTnTECrVzuaD4UFv+9t3XCcRh34PQ010+YjZWhzifehyh7AeKuxX0er8ymgpd
|
||||
q6zT9CyZ+8IZATer9qruMG4jDfO5vI1eZwiDdpF5klOdtZQqq80ANmeEu2McHVhh
|
||||
jQKCAQBbohPxMb3QYdukGp8IsIF04GfnTgaDbRgl4KeUyzdBN3nzvCKK0HDluptR
|
||||
4Ua64JksGG24gsTBy6yuQoGRCG0LJe0Ty3TRRnvZ8MpADoNMObspMSC8n8kk6ps+
|
||||
SoG1U9t6HYlIgQagvTc7mTmCmwYX1zlCoZp24yz5pDkKxqoPFDtrGlXxeUgOhpDT
|
||||
Mzi+DNTz9sH9vod4ibQiOseUxITwQpXHTJVrtNfvva6xjlhq+GGCuKIUwkUKOvBC
|
||||
ds7SR9demn69aWCyzXqD1cTnmxtn6bNPukwowg7a07ieUyKftcJ1icOWQ/bdQkEf
|
||||
dV1dhNiQEnqs4vDBVn40dnTKSSG2
|
||||
-----END PRIVATE KEY-----
|
|
@ -1,6 +1,5 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using Dapr;
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\src\Dapr.AspNetCore\Dapr.AspNetCore.csproj" />
|
||||
<ProjectReference Include="..\..\..\src\Dapr.Client\Dapr.Client.csproj" />
|
||||
<ProjectReference Include="..\..\..\src\Dapr.Common\Dapr.Common.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
using System;
|
||||
namespace DistributedLock.Model
|
||||
namespace DistributedLock.Model
|
||||
{
|
||||
#nullable enable
|
||||
public class StateData
|
||||
|
|
|
@ -1,11 +1,6 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace DistributedLock
|
||||
{
|
||||
|
|
|
@ -24,7 +24,7 @@ cd examples/Client/DistributedLock
|
|||
In order to run the application that generates data for the workers to process, simply run the following command:
|
||||
|
||||
```bash
|
||||
dapr run --components-path ./Components --app-id generator -- dotnet run
|
||||
dapr run --resources-path ./Components --app-id generator -- dotnet run
|
||||
```
|
||||
|
||||
This application will create a new file to process once every 10 seconds. The files are stored in `DistributedLock/tmp`.
|
||||
|
@ -33,8 +33,8 @@ This application will create a new file to process once every 10 seconds. The fi
|
|||
In order to properly demonstrate locking, this application will be run more than once with the same App ID. However, the applications do need different ports in order to properly receive bindings. Run them with the command below:
|
||||
|
||||
```bash
|
||||
dapr run --components-path ./Components --app-id worker --app-port 5000 -- dotnet run
|
||||
dapr run --components-path ./Components --app-id worker --app-port 5001 -- dotnet run
|
||||
dapr run --resources-path ./Components --app-id worker --app-port 5000 -- dotnet run
|
||||
dapr run --resources-path ./Components --app-id worker --app-port 5001 -- dotnet run
|
||||
```
|
||||
|
||||
After running the applications, they will attempt to process files. You should see output such as:
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -16,9 +16,9 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Google.Protobuf" Version="3.15.0" />
|
||||
<PackageReference Include="Google.Api.CommonProtos" Version="2.2.0" />
|
||||
<PackageReference Include="Grpc.Tools" Version="2.47.0" PrivateAssets="All" />
|
||||
<PackageReference Include="Google.Protobuf" />
|
||||
<PackageReference Include="Google.Api.CommonProtos" />
|
||||
<PackageReference Include="Grpc.Tools" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
@ -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/)
|
||||
|
|
|
@ -16,9 +16,9 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Google.Protobuf" Version="3.15.0" />
|
||||
<PackageReference Include="Google.Api.CommonProtos" Version="2.2.0" />
|
||||
<PackageReference Include="Grpc.Tools" Version="2.47.0" PrivateAssets="All" />
|
||||
<PackageReference Include="Google.Protobuf" />
|
||||
<PackageReference Include="Google.Api.CommonProtos" />
|
||||
<PackageReference Include="Grpc.Tools" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
@ -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/)
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
using System.Text;
|
||||
using Dapr.Messaging.PublishSubscribe;
|
||||
using Dapr.Messaging.PublishSubscribe.Extensions;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
builder.Services.AddDaprPubSubClient();
|
||||
var app = builder.Build();
|
||||
|
||||
//Process each message returned from the subscription
|
||||
Task<TopicResponseAction> HandleMessageAsync(TopicMessage message, CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
//Do something with the message
|
||||
Console.WriteLine(Encoding.UTF8.GetString(message.Data.Span));
|
||||
return Task.FromResult(TopicResponseAction.Success);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return Task.FromResult(TopicResponseAction.Retry);
|
||||
}
|
||||
}
|
||||
|
||||
var messagingClient = app.Services.GetRequiredService<DaprPublishSubscribeClient>();
|
||||
|
||||
//Create a dynamic streaming subscription and subscribe with a timeout of 30 seconds and 10 seconds for message handling
|
||||
var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(30));
|
||||
var subscription = await messagingClient.SubscribeAsync("pubsub", "myTopic",
|
||||
new DaprSubscriptionOptions(new MessageHandlingPolicy(TimeSpan.FromSeconds(10), TopicResponseAction.Retry)),
|
||||
HandleMessageAsync, cancellationTokenSource.Token);
|
||||
|
||||
await Task.Delay(TimeSpan.FromMinutes(1));
|
||||
|
||||
//When you're done with the subscription, simply dispose of it
|
||||
await subscription.DisposeAsync();
|
|
@ -0,0 +1,14 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\..\src\Dapr.Messaging\Dapr.Messaging.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -13,7 +13,6 @@
|
|||
|
||||
using System;
|
||||
using System.Net.Http.Json;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Dapr.Client;
|
||||
|
|
|
@ -16,9 +16,9 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Google.Protobuf" Version="3.15.0" />
|
||||
<PackageReference Include="Google.Api.CommonProtos" Version="2.2.0" />
|
||||
<PackageReference Include="Grpc.Tools" Version="2.47.0" PrivateAssets="All" />
|
||||
<PackageReference Include="Google.Protobuf" />
|
||||
<PackageReference Include="Google.Api.CommonProtos" />
|
||||
<PackageReference Include="Grpc.Tools" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
@ -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.
|
||||
|
@ -24,7 +24,8 @@ namespace Samples.Client
|
|||
new StateStoreExample(),
|
||||
new StateStoreTransactionsExample(),
|
||||
new StateStoreETagsExample(),
|
||||
new BulkStateExample()
|
||||
new BulkStateExample(),
|
||||
new StateStoreBinaryExample()
|
||||
};
|
||||
|
||||
static async Task<int> Main(string[] args)
|
||||
|
|
|
@ -16,9 +16,9 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Google.Protobuf" Version="3.15.0" />
|
||||
<PackageReference Include="Google.Api.CommonProtos" Version="2.2.0" />
|
||||
<PackageReference Include="Grpc.Tools" Version="2.47.0" PrivateAssets="All" />
|
||||
<PackageReference Include="Google.Protobuf" />
|
||||
<PackageReference Include="Google.Api.CommonProtos" />
|
||||
<PackageReference Include="Grpc.Tools" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using Dapr.Client;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading;
|
||||
using Google.Protobuf;
|
||||
|
||||
namespace Samples.Client
|
||||
{
|
||||
public class StateStoreBinaryExample : Example
|
||||
{
|
||||
|
||||
private static readonly string stateKeyName = "binarydata";
|
||||
private static readonly string storeName = "statestore";
|
||||
|
||||
public override string DisplayName => "Using the State Store with binary data";
|
||||
|
||||
public override async Task RunAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
using var client = new DaprClientBuilder().Build();
|
||||
|
||||
var state = "Test Binary Data";
|
||||
// convert variable in to byte array
|
||||
var stateBytes = Encoding.UTF8.GetBytes(state);
|
||||
await client.SaveByteStateAsync(storeName, stateKeyName, stateBytes.AsMemory(), cancellationToken: cancellationToken);
|
||||
Console.WriteLine("Saved State!");
|
||||
|
||||
var responseBytes = await client.GetByteStateAsync(storeName, stateKeyName, cancellationToken: cancellationToken);
|
||||
var savedState = Encoding.UTF8.GetString(ByteString.CopyFrom(responseBytes.Span).ToByteArray());
|
||||
|
||||
if (savedState == null)
|
||||
{
|
||||
Console.WriteLine("State not found in store");
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine($"Got State: {savedState}");
|
||||
}
|
||||
|
||||
await client.DeleteStateAsync(storeName, stateKeyName, cancellationToken: cancellationToken);
|
||||
Console.WriteLine("Deleted State!");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -1,22 +1,29 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net6</TargetFramework>
|
||||
<LangVersion>10.0</LangVersion>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net6</TargetFramework>
|
||||
<LangVersion>10.0</LangVersion>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\ActorCommon\ActorCommon.csproj" />
|
||||
<ProjectReference Include="..\..\..\src\Dapr.Actors\Dapr.Actors.csproj" />
|
||||
</ItemGroup>
|
||||
<!-- Persist the source generator (and other) files to disk -->
|
||||
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
|
||||
<!-- 👇 The "base" path for the source generators -->
|
||||
<!--<GeneratedFolder>Generated</GeneratedFolder>-->
|
||||
<!-- 👇 Write the output for each target framework to a different sub-folder -->
|
||||
<!--<CompilerGeneratedFilesOutputPath>$(GeneratedFolder)\$(TargetFramework)</CompilerGeneratedFilesOutputPath>-->
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\src\Dapr.Actors.Generators\Dapr.Actors.Generators.csproj"
|
||||
OutputItemType="Analyzer"
|
||||
ReferenceOutputAssembly="false" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\ActorCommon\ActorCommon.csproj" />
|
||||
<ProjectReference Include="..\..\..\src\Dapr.Actors\Dapr.Actors.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\src\Dapr.Actors.Generators\Dapr.Actors.Generators.csproj"
|
||||
OutputItemType="Analyzer"
|
||||
ReferenceOutputAssembly="false" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// ------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------
|
||||
// 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.
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
// ------------------------------------------------------------------------
|
||||
// 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
|
||||
{
|
||||
[GenerateActorClient]
|
||||
internal interface IGenericClientActor<TGenericType1, TGenericType2>
|
||||
{
|
||||
[ActorMethod(Name = "GetState")]
|
||||
Task<TGenericType1> GetStateAsync(CancellationToken cancellationToken = default);
|
||||
|
||||
[ActorMethod(Name = "SetState")]
|
||||
Task SetStateAsync(TGenericType2 state, CancellationToken cancellationToken = default);
|
||||
}
|
||||
}
|
|
@ -23,7 +23,7 @@ using var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromSec
|
|||
|
||||
var client = new ClientActorClient(proxy);
|
||||
|
||||
var state = await client.GetStateAsync(cancellationTokenSource.Token);
|
||||
await client.GetStateAsync(cancellationTokenSource.Token);
|
||||
|
||||
await client.SetStateAsync(new ClientState("Hello, World!"), cancellationTokenSource.Token);
|
||||
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\src\Dapr.Jobs\Dapr.Jobs.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -0,0 +1,51 @@
|
|||
// ------------------------------------------------------------------------
|
||||
// Copyright 2024 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.
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
using System.Text;
|
||||
using Dapr.Jobs;
|
||||
using Dapr.Jobs.Extensions;
|
||||
using Dapr.Jobs.Models;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
builder.Services.AddDaprJobsClient();
|
||||
builder.Logging.ClearProviders();
|
||||
builder.Logging.AddConsole();
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
//Set a handler to deal with incoming jobs
|
||||
app.MapDaprScheduledJobHandler(async (string jobName, ReadOnlyMemory<byte> jobPayload, ILogger? logger, CancellationToken cancellationToken) =>
|
||||
{
|
||||
logger?.LogInformation("Received trigger invocation for job '{jobName}'", jobName);
|
||||
|
||||
var deserializedPayload = Encoding.UTF8.GetString(jobPayload.Span);
|
||||
logger?.LogInformation("Received invocation for the job '{jobName}' with payload '{deserializedPayload}'",
|
||||
jobName, deserializedPayload);
|
||||
await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken);
|
||||
|
||||
return Task.CompletedTask;
|
||||
}, TimeSpan.FromSeconds(5));
|
||||
|
||||
using var scope = app.Services.CreateScope();
|
||||
var logger = scope.ServiceProvider.GetRequiredService<ILogger<Program>>();
|
||||
var daprJobsClient = scope.ServiceProvider.GetRequiredService<DaprJobsClient>();
|
||||
|
||||
logger.LogInformation("Scheduling one-time job 'myJob' to execute 10 seconds from now");
|
||||
await daprJobsClient.ScheduleJobAsync("myJob", DaprJobSchedule.FromDuration(TimeSpan.FromSeconds(2)),
|
||||
Encoding.UTF8.GetBytes("This is a test"), repeats: 10);
|
||||
logger.LogInformation("Scheduled one-time job 'myJob'");
|
||||
|
||||
app.Run();
|
||||
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
|
@ -0,0 +1,38 @@
|
|||
{
|
||||
"$schema": "http://json.schemastore.org/launchsettings.json",
|
||||
"iisSettings": {
|
||||
"windowsAuthentication": false,
|
||||
"anonymousAuthentication": true,
|
||||
"iisExpress": {
|
||||
"applicationUrl": "http://localhost:6382",
|
||||
"sslPort": 44324
|
||||
}
|
||||
},
|
||||
"profiles": {
|
||||
"http": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": true,
|
||||
"applicationUrl": "http://localhost:5140",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
},
|
||||
"https": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": true,
|
||||
"applicationUrl": "https://localhost:7241;http://localhost:5140",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
},
|
||||
"IIS Express": {
|
||||
"commandName": "IISExpress",
|
||||
"launchBrowser": true,
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
// ------------------------------------------------------------------------
|
||||
// Copyright 2024 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.Workflow;
|
||||
using WorkflowAsyncOperations.Models;
|
||||
|
||||
namespace WorkflowAsyncOperations.Activities;
|
||||
|
||||
internal sealed class NotifyWarehouseActivity : WorkflowActivity<Transaction, object?>
|
||||
{
|
||||
/// <summary>
|
||||
/// Override to implement async (non-blocking) workflow activity logic.
|
||||
/// </summary>
|
||||
/// <param name="context">Provides access to additional context for the current activity execution.</param>
|
||||
/// <param name="input">The deserialized activity input.</param>
|
||||
/// <returns>The output of the activity as a task.</returns>
|
||||
public override async Task<object?> RunAsync(WorkflowActivityContext context, Transaction input)
|
||||
{
|
||||
//Contact the warehouse to ship the product
|
||||
await Task.Delay(TimeSpan.FromSeconds(8));
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
// ------------------------------------------------------------------------
|
||||
// Copyright 2024 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.Workflow;
|
||||
using WorkflowAsyncOperations.Models;
|
||||
|
||||
namespace WorkflowAsyncOperations.Activities;
|
||||
|
||||
internal sealed class ProcessPaymentActivity : WorkflowActivity<Transaction, object?>
|
||||
{
|
||||
/// <summary>
|
||||
/// Override to implement async (non-blocking) workflow activity logic.
|
||||
/// </summary>
|
||||
/// <param name="context">Provides access to additional context for the current activity execution.</param>
|
||||
/// <param name="input">The deserialized activity input.</param>
|
||||
/// <returns>The output of the activity as a task.</returns>
|
||||
public override async Task<object?> RunAsync(WorkflowActivityContext context, Transaction input)
|
||||
{
|
||||
//Confirm payment with processor
|
||||
await Task.Delay(TimeSpan.FromSeconds(10));
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
// ------------------------------------------------------------------------
|
||||
// Copyright 2024 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 WorkflowAsyncOperations.Models;
|
||||
|
||||
internal sealed record Transaction(decimal Value)
|
||||
{
|
||||
public Guid CustomerId { get; init; } = Guid.NewGuid();
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
// ------------------------------------------------------------------------
|
||||
// Copyright 2024 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.Workflow;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using WorkflowAsyncOperations.Activities;
|
||||
using WorkflowAsyncOperations.Models;
|
||||
using WorkflowAsyncOperations.Workflows;
|
||||
|
||||
var builder = Host.CreateDefaultBuilder(args).ConfigureServices(services =>
|
||||
{
|
||||
services.AddDaprWorkflow(options =>
|
||||
{
|
||||
options.RegisterWorkflow<DemoWorkflow>();
|
||||
options.RegisterActivity<ProcessPaymentActivity>();
|
||||
options.RegisterActivity<NotifyWarehouseActivity>();
|
||||
});
|
||||
});
|
||||
|
||||
var host = await builder.StartAsync();
|
||||
|
||||
await using var scope = host.Services.CreateAsyncScope();
|
||||
var daprWorkflowClient = scope.ServiceProvider.GetRequiredService<DaprWorkflowClient>();
|
||||
|
||||
var instanceId = $"demo-workflow-{Guid.NewGuid().ToString()[..8]}";
|
||||
var transaction = new Transaction(16.58m);
|
||||
await daprWorkflowClient.ScheduleNewWorkflowAsync(nameof(DemoWorkflow), instanceId, transaction);
|
||||
|
||||
//Poll for status updates every second
|
||||
var status = await daprWorkflowClient.GetWorkflowStateAsync(instanceId);
|
||||
do
|
||||
{
|
||||
Console.WriteLine($"Current status: {status.RuntimeStatus}, step: {status.ReadCustomStatusAs<string>()}");
|
||||
status = await daprWorkflowClient.GetWorkflowStateAsync(instanceId);
|
||||
} while (!status.IsWorkflowCompleted);
|
||||
|
||||
Console.WriteLine($"Workflow completed - {status.ReadCustomStatusAs<string>()}");
|
|
@ -0,0 +1,18 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\src\Dapr.Workflow\Dapr.Workflow.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -0,0 +1,52 @@
|
|||
// ------------------------------------------------------------------------
|
||||
// Copyright 2024 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.Workflow;
|
||||
using WorkflowAsyncOperations.Activities;
|
||||
using WorkflowAsyncOperations.Models;
|
||||
|
||||
namespace WorkflowAsyncOperations.Workflows;
|
||||
|
||||
internal sealed class DemoWorkflow : Workflow<Transaction, bool>
|
||||
{
|
||||
/// <summary>
|
||||
/// Override to implement workflow logic.
|
||||
/// </summary>
|
||||
/// <param name="context">The workflow context.</param>
|
||||
/// <param name="input">The deserialized workflow input.</param>
|
||||
/// <returns>The output of the workflow as a task.</returns>
|
||||
public override async Task<bool> RunAsync(WorkflowContext context, Transaction input)
|
||||
{
|
||||
try
|
||||
{
|
||||
//Submit the transaction to the payment processor
|
||||
context.SetCustomStatus("Processing payment...");
|
||||
await context.CallActivityAsync(nameof(ProcessPaymentActivity), input);
|
||||
|
||||
|
||||
//Send the transaction details to the warehouse
|
||||
context.SetCustomStatus("Contacting warehouse...");
|
||||
await context.CallActivityAsync(nameof(NotifyWarehouseActivity), input);
|
||||
|
||||
context.SetCustomStatus("Success!");
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
//If anything goes wrong, return false
|
||||
context.SetCustomStatus("Something went wrong");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,5 @@
|
|||
using Dapr.Client;
|
||||
using Dapr.Workflow;
|
||||
using Dapr.Workflow;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using WorkflowConsoleApp.Models;
|
||||
|
||||
namespace WorkflowConsoleApp.Activities
|
||||
{
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue