diff --git a/daprdocs/README.md b/daprdocs/README.md deleted file mode 100644 index ce83c6b66..000000000 --- a/daprdocs/README.md +++ /dev/null @@ -1,25 +0,0 @@ -# Dapr .NET SDK documentation - -This page covers how the documentation is structured for the Dapr .NET SDK. - -## Dapr Docs - -All Dapr documentation is hosted at [docs.dapr.io](https://docs.dapr.io), including the docs for the [.NET SDK](https://docs.dapr.io/developing-applications/sdks/dotnet/). Head over there if you want to read the docs. - -### .NET SDK docs source - -Although the docs site code and content is in the [docs repo](https://github.com/dapr/docs), the .NET SDK content and images are within the `content` and `static` directories, respectively. - -This allows separation of roles and expertise between maintainers, and makes it easy to find the docs files you are looking for. - -## Writing .NET SDK docs - -To get up and running to write .NET SDK docs, visit the [docs repo](https://github.com/dapr/docs) to initialize your environment. It will clone both the docs repo and this repo, so you can make changes and see it rendered within the site instantly, as well as commit and PR into this repo. - -Make sure to read the [docs contributing guide](https://docs.dapr.io/contributing/contributing-docs/) for information on style/semantics/etc. - -## Docs architecture - -The docs site is built on [Hugo](https://gohugo.io), which lives in the docs repo. This repo is setup as a git submodule so that when the repo is cloned and initialized, the dotnet-sdk repo, along with the docs, are cloned as well. - -Then, in the Hugo configuration file, the `daprdocs/content` and `daprdocs/static` directories are redirected to the `daprdocs/developing-applications/sdks/dotnet` and `static/dotnet` directories, respectively. Thus, all the content within this repo is folded into the main docs site. \ No newline at end of file diff --git a/daprdocs/content/en/dotnet-sdk-contributing/dotnet-contributing.md b/daprdocs/content/en/dotnet-sdk-contributing/dotnet-contributing.md deleted file mode 100644 index 9e75a092b..000000000 --- a/daprdocs/content/en/dotnet-sdk-contributing/dotnet-contributing.md +++ /dev/null @@ -1,108 +0,0 @@ ---- -type: docs -title: "Contributing to the .NET SDK" -linkTitle: ".NET SDK" -weight: 3000 -description: Guidelines for contributing to the Dapr .NET SDK ---- - -# 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 -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 -[tabpane]({{% 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. - -## 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: - - - 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 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 - -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.16, this means that both .NET 8 and .NET 9 are supported. The latest language version available -is [C# version 12](https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-version-history#c-version-12) - -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. \ No newline at end of file diff --git a/daprdocs/content/en/dotnet-sdk-docs/_index.md b/daprdocs/content/en/dotnet-sdk-docs/_index.md deleted file mode 100644 index cf836dfd2..000000000 --- a/daprdocs/content/en/dotnet-sdk-docs/_index.md +++ /dev/null @@ -1,91 +0,0 @@ ---- -type: docs -title: "Dapr .NET SDK" -linkTitle: ".NET" -weight: 1000 -description: .NET SDK packages for developing Dapr applications -no_list: true -cascade: - github_repo: https://github.com/dapr/dotnet-sdk - github_subdir: daprdocs/content/en/dotnet-sdk-docs - path_base_for_github_subdir: content/en/developing-applications/sdks/dotnet/ - github_branch: master ---- - -Dapr offers a variety of packages to help with the development of .NET applications. Using them you can create .NET clients, servers, and virtual actors with Dapr. - -## Prerequisites -- [Dapr CLI]({{< ref install-dapr-cli.md >}}) installed -- Initialized [Dapr environment]({{< ref install-dapr-selfhost.md >}}) -- [.NET 8](https://dotnet.microsoft.com/download) or [.NET 9](https://dotnet.microsoft.com/download) installed - -## Installation - -To get started with the Client .NET SDK, install the Dapr .NET SDK package: - -```sh -dotnet add package Dapr.Client -``` - -## Try it out - -Put the Dapr .NET SDK to the test. Walk through the .NET quickstarts and tutorials to see Dapr in action: - -| SDK samples | Description | -| ----------- | ----------- | -| [Quickstarts]({{% ref quickstarts %}}) | Experience Dapr's API building blocks in just a few minutes using the .NET SDK. | -| [SDK samples](https://github.com/dapr/dotnet-sdk/tree/master/examples) | Clone the SDK repo to try out some examples and get started. | -| [Pub/sub tutorial](https://github.com/dapr/quickstarts/tree/master/tutorials/pub-sub) | See how Dapr .NET SDK works alongside other Dapr SDKs to enable pub/sub applications. | - -## Available packages - -| Package Name | Documentation Link | Description | -|-----------------------------------------------------------------------------------------------------------|---------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------| -| [Dapr.Client](https://www.nuget.org/packages/Dapr.Client) | [Documentation]({{% ref dotnet-client %}}) | Create .NET clients that interact with a Dapr sidecar and other Dapr applications. | -| [Dapr.AI](https://www.nuget.org/packages/Dapr.AI) | [Documentation]({{% ref dotnet-ai %}}) | Create and manage AI operations in .NET. | -| [Dapr.AI.A2a](https://www.nuget.org/packages/Dapr.AI.A2a) | | Dapr SDK for implementing agent-to-agent operations using the [A2A](https://github.com/a2aproject/a2a-dotnet) framework. | -| [Dapr.AI.Microsoft.Extensions](https://www.nuget.org/packages/Dapr.AI.Microsoft.Extensions) | [Documentation]({{% ref dotnet-ai-extensions-howto %}}) | Easily interact with LLMs conversationally and using tooling via the Dapr Conversation building block. | -| [Dapr.AspNetCore](https://www.nuget.org/packages/Dapr.AspNetCore) | [Documentation]({{% ref dotnet-client %}}) | Write servers and services in .NET using the Dapr SDK. Includes support and utilities providing richer integration with ASP.NET Core. | -| [Dapr.Actors](https://www.nuget.org/packages/Dapr.Actors) | [Documentation]({{% ref dotnet-actors %}}) | Create virtual actors with state, reminders/timers, and methods. | -| [Dapr.Actors.AspNetCore](https://www.nuget.org/packages/Dapr.Actors) | [Documentation]({{% ref dotnet-actors %}}) | Create virtual actors with state, reminders/timers, and methods with rich integration with ASP.NET Core. | -| [Dapr.Actors.Analyzers](https://www.nuget.org/packages/Dapr.Actors.Analyzers) | [Documentation]({{% ref dotnet-guidance-source-generators %}}) | A collection of Roslyn source generators and analyzers for enabling better practices and preventing common errors when using Dapr Actors in .NET. | -| [Dapr.Cryptography](https://www.nuget.org/packages/Dapr.Cryptography) | [Documentation]({{% ref dotnet-cryptography %}}) | Encrypt and decrypt streaming state of any size using Dapr's cryptography building block. | -| [Dapr.Jobs](https://www.nuget.org/packages/Dapr.Jobs) | [Documentation]({{% ref dotnet-jobs %}}) | Create and manage the scheduling and orchestration of jobs. | -| [Dapr.Jobs.Analyzers](https://www.nuget.org/packages/Dapr.Jobs.Analyzers) | [Documentation]({{% ref dotnet-guidance-source-generators %}}) | A collection of Roslyn source generators and analyzers for enabling better practices and preventing common errors when using Dapr Jobs in .NET. | -| [Dapr.DistributedLocks](https://www.nuget.org/packages/Dapr.DistributedLocks) | [Documentation]({{% ref dotnet-distributed-lock %}}) | Create and manage distributed locks for managing exclusive resource access. | -| [Dapr.Extensions.Configuration](https://www.nuget.org/packages/Dapr.Extensions.Configuration) | | Dapr secret store configuration provider implementation for `Microsoft.Extensions.Configuration`. | -| [Dapr.PluggableComponents](https://www.nuget.org/packages/Dapr.PluggableComponents) | | Used to implement pluggable components with Dapr using .NET. | -| [Dapr.PluggableComponents.AspNetCore](https://www.nuget.org/packages/Dapr.PluggableComponents.AspNetCore) | | Implement pluggable components with Dapr using .NET with rich ASP.NET Core support. | -| [Dapr.PluggableComponents.Protos](https://www.nuget.org/packages/Dapr.PluggableComponents.Protos) | | **Note:** Developers needn't install this package directly in their applications. | -| [Dapr.Messaging](https://www.nuget.org/packages/Dapr.Messaging) | [Documentation]({{% ref dotnet-messaging %}}) | Build distributed applications using the Dapr Messaging SDK that utilize messaging components like streaming pub/sub subscriptions. | -| [Dapr.Workflow](https://www.nuget.org/packages/Dapr.Workflow) | [Documentation]({{% ref dotnet-workflow %}}) | Create and manage workflows that work with other Dapr APIs. | -| [Dapr.Workflow.Analyzers](https://www.nuget.org/packages/Dapr.Workflow.Analyzers) | [Documentation]({{% ref dotnet-guidance-source-generators %}}) | A collection of Roslyn source generators and analyzers for enabling better practices and preventing common errors when using Dapr Workflows in .NET | - -## More information - -Learn more about local development options, best practices, or browse NuGet packages to add to your existing .NET -applications. - -
-
-
-
Development
-

Learn about local development integration options

- -
-
-
-
Best Practices
-

Learn about best practices for developing .NET Dapr applications

- -
-
-
-
-
NuGet packages
-

NuGet packages for adding the Dapr to your .NET applications.

- -
-
-
-
\ No newline at end of file diff --git a/daprdocs/content/en/dotnet-sdk-docs/dotnet-actors/_index.md b/daprdocs/content/en/dotnet-sdk-docs/dotnet-actors/_index.md deleted file mode 100644 index 6be7b947f..000000000 --- a/daprdocs/content/en/dotnet-sdk-docs/dotnet-actors/_index.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -type: docs -title: "Dapr actors .NET SDK" -linkTitle: "Actors" -weight: 30000 -description: Get up and running with the Dapr actors .NET SDK ---- - -With the Dapr actor package, you can interact with Dapr virtual actors from a .NET application. - -To get started, walk through the [Dapr actors]({{% ref dotnet-actors-howto.md %}}) how-to guide. \ No newline at end of file diff --git a/daprdocs/content/en/dotnet-sdk-docs/dotnet-actors/dotnet-actors-client.md b/daprdocs/content/en/dotnet-sdk-docs/dotnet-actors/dotnet-actors-client.md deleted file mode 100644 index 6431e4a49..000000000 --- a/daprdocs/content/en/dotnet-sdk-docs/dotnet-actors/dotnet-actors-client.md +++ /dev/null @@ -1,114 +0,0 @@ ---- -type: docs -title: "The IActorProxyFactory interface" -linkTitle: "Actors client" -weight: 100000 -description: Learn how to create actor clients with the IActorProxyFactory interface ---- - -Inside of an `Actor` class or an ASP.NET Core project, the `IActorProxyFactory` interface is recommended to create actor clients. - -The `AddActors(...)` method will register actor services with ASP.NET Core dependency injection. - -- **Outside of an actor instance:** The `IActorProxyFactory` instance is available through dependency injection as a singleton service. -- **Inside an actor instance:** The `IActorProxyFactory` instance is available as a property (`this.ProxyFactory`). - -The following is an example of creating a proxy inside an actor: - -```csharp -public Task GetDataAsync() -{ - var proxy = this.ProxyFactory.CreateActorProxy(ActorId.CreateRandom(), "OtherActor"); - await proxy.DoSomethingGreat(); - - return this.StateManager.GetStateAsync("my_data"); -} -``` - -In this guide, you will learn how to use `IActorProxyFactory`. - -{{% alert title="Tip" color="primary" %}} -For a non-dependency-injected application, you can use the static methods on `ActorProxy`. Since the `ActorProxy` methods are error prone, try to avoid using them when configuring custom settings. -{{% /alert %}} - -## Identifying an actor - -All of the APIs on `IActorProxyFactory` will require an actor _type_ and actor _id_ to communicate with an actor. For strongly-typed clients, you also need one of its interfaces. - -- **Actor type** uniquely identifies the actor implementation across the whole application. -- **Actor id** uniquely identifies an instance of that type. - -If you don't have an actor `id` and want to communicate with a new instance, create a random id with `ActorId.CreateRandom()`. Since the random id is a cryptographically strong identifier, the runtime will create a new actor instance when you interact with it. - -You can use the type `ActorReference` to exchange an actor type and actor id with other actors as part of messages. - -## Two styles of actor client - -The actor client supports two different styles of invocation: - -| Actor client style | Description | -| ------------------ | ----------- | -| Strongly-typed | Strongly-typed clients are based on .NET interfaces and provide the typical benefits of strong-typing. They don't work with non-.NET actors. | -| Weakly-typed | Weakly-typed clients use the `ActorProxy` class. It is recommended to use these only when required for interop or other advanced reasons. | - -### Using a strongly-typed client - -The following example uses the `CreateActorProxy<>` method to create a strongly-typed client. `CreateActorProxy<>` requires an actor interface type, and will return an instance of that interface. - -```csharp -// Create a proxy for IOtherActor to type OtherActor with a random id -var proxy = this.ProxyFactory.CreateActorProxy(ActorId.CreateRandom(), "OtherActor"); - -// Invoke a method defined by the interface to invoke the actor -// -// proxy is an implementation of IOtherActor so we can invoke its methods directly -await proxy.DoSomethingGreat(); -``` - -### Using a weakly-typed client - -The following example uses the `Create` method to create a weakly-typed client. `Create` returns an instance of `ActorProxy`. - -```csharp -// Create a proxy for type OtherActor with a random id -var proxy = this.ProxyFactory.Create(ActorId.CreateRandom(), "OtherActor"); - -// Invoke a method by name to invoke the actor -// -// proxy is an instance of ActorProxy. -await proxy.InvokeMethodAsync("DoSomethingGreat"); -``` - -Since `ActorProxy` is a weakly-typed proxy, you need to pass in the actor method name as a string. - -You can also use `ActorProxy` to invoke methods with both a request and a response message. Request and response messages will be serialized using the `System.Text.Json` serializer. - -```csharp -// Create a proxy for type OtherActor with a random id -var proxy = this.ProxyFactory.Create(ActorId.CreateRandom(), "OtherActor"); - -// Invoke a method on the proxy to invoke the actor -// -// proxy is an instance of ActorProxy. -var request = new MyRequest() { Message = "Hi, it's me.", }; -var response = await proxy.InvokeMethodAsync("DoSomethingGreat", request); -``` - -When using a weakly-typed proxy, you _must_ proactively define the correct actor method names and message types. When using a strongly-typed proxy, these names and types are defined for you as part of the interface definition. - -### Actor method invocation exception details - -The actor method invocation exception details are surfaced to the caller and the callee, providing an entry point to track down the issue. Exception details include: - - Method name - - Line number - - Exception type - - UUID - -You use the UUID to match the exception on the caller and callee side. Below is an example of exception details: -``` -Dapr.Actors.ActorMethodInvocationException: Remote Actor Method Exception, DETAILS: Exception: NotImplementedException, Method Name: ExceptionExample, Line Number: 14, Exception uuid: d291a006-84d5-42c4-b39e-d6300e9ac38b -``` - -## Next steps - -[Learn how to author and run actors with `ActorHost`]({{% ref dotnet-actors-usage.md %}}). \ No newline at end of file diff --git a/daprdocs/content/en/dotnet-sdk-docs/dotnet-actors/dotnet-actors-howto.md b/daprdocs/content/en/dotnet-sdk-docs/dotnet-actors/dotnet-actors-howto.md deleted file mode 100644 index bac91f795..000000000 --- a/daprdocs/content/en/dotnet-sdk-docs/dotnet-actors/dotnet-actors-howto.md +++ /dev/null @@ -1,467 +0,0 @@ ---- -type: docs -title: "How to: Run and use virtual actors in the .NET SDK" -linkTitle: "How to: Run & use virtual actors" -weight: 300000 -description: Try out .NET Dapr virtual actors with this example ---- - -The Dapr actor package allows you to interact with Dapr virtual actors from a .NET application. In this guide, you learn how to: - -- Create an Actor (`MyActor`). -- Invoke its methods on the client application. - -``` -MyActor --- MyActor.Interfaces - | - +- MyActorService - | - +- MyActorClient -``` - -**The interface project (\MyActor\MyActor.Interfaces)** - -This project contains the interface definition for the actor. Actor interfaces can be defined in any project with any name. The interface defines the actor contract shared by: - -- The actor implementation -- The clients calling the actor - -Because client projects may depend on it, it's better to define it in an assembly separate from the actor implementation. - -**The actor service project (\MyActor\MyActorService)** - -This project implements the ASP.Net Core web service that hosts the actor. It contains the implementation of the actor, `MyActor.cs`. An actor implementation is a class that: - -- Derives from the base type Actor -- Implements the interfaces defined in the `MyActor.Interfaces` project. - -An actor class must also implement a constructor that accepts an `ActorService` instance and an `ActorId`, and passes them to the base Actor class. - -**The actor client project (\MyActor\MyActorClient)** - -This project contains the implementation of the actor client which calls MyActor's method defined in Actor Interfaces. - -## Prerequisites -- [Dapr CLI]({{< ref install-dapr-cli.md >}}) installed. -- Initialized [Dapr environment]({{< ref install-dapr-selfhost.md >}}). -- [.NET 8](https://dotnet.microsoft.com/download) or [.NET 9](https://dotnet.microsoft.com/download) installed - -## Step 0: Prepare - -Since we'll be creating 3 projects, choose an empty directory to start from, and open it in your terminal of choice. - -## Step 1: Create actor interfaces - -Actor interface defines the actor contract that is shared by the actor implementation and the clients calling the actor. - -Actor interface is defined with the below requirements: - -- Actor interface must inherit `Dapr.Actors.IActor` interface -- The return type of Actor method must be `Task` or `Task` -- Actor method can have one argument at a maximum - -### Create interface project and add dependencies - -```bash -# Create Actor Interfaces -dotnet new classlib -o MyActor.Interfaces - -cd MyActor.Interfaces - -# Add Dapr.Actors nuget package. Please use the latest package version from nuget.org -dotnet add package Dapr.Actors - -cd .. -``` - -### Implement IMyActor interface - -Define `IMyActor` interface and `MyData` data object. Paste the following code into `MyActor.cs` in the `MyActor.Interfaces` project. - -```csharp -using Dapr.Actors; -using Dapr.Actors.Runtime; -using System.Threading.Tasks; - -namespace MyActor.Interfaces -{ - public interface IMyActor : IActor - { - Task SetDataAsync(MyData data); - Task GetDataAsync(); - Task RegisterReminder(); - Task UnregisterReminder(); - Task GetReminder(); - Task RegisterTimer(); - Task UnregisterTimer(); - } - - public class MyData - { - public string PropertyA { get; set; } - public string PropertyB { get; set; } - - public override string ToString() - { - var propAValue = this.PropertyA == null ? "null" : this.PropertyA; - var propBValue = this.PropertyB == null ? "null" : this.PropertyB; - return $"PropertyA: {propAValue}, PropertyB: {propBValue}"; - } - } -} -``` - -## Step 2: Create actor service - -Dapr uses ASP.NET web service to host Actor service. This section will implement `IMyActor` actor interface and register Actor to Dapr Runtime. - -### Create actor service project and add dependencies - -```bash -# Create ASP.Net Web service to host Dapr actor -dotnet new web -o MyActorService - -cd MyActorService - -# Add Dapr.Actors.AspNetCore nuget package. Please use the latest package version from nuget.org -dotnet add package Dapr.Actors.AspNetCore - -# Add Actor Interface reference -dotnet add reference ../MyActor.Interfaces/MyActor.Interfaces.csproj - -cd .. -``` - -### Add actor implementation - -Implement IMyActor interface and derive from `Dapr.Actors.Actor` class. Following example shows how to use Actor Reminders as well. 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. - -Paste the following code into `MyActor.cs` in the `MyActorService` project: - -```csharp -using Dapr.Actors; -using Dapr.Actors.Runtime; -using MyActor.Interfaces; -using System; -using System.Threading.Tasks; - -namespace MyActorService -{ - internal class MyActor : Actor, IMyActor, IRemindable - { - // The constructor must accept ActorHost as a parameter, and can also accept additional - // parameters that will be retrieved from the dependency injection container - // - /// - /// Initializes a new instance of MyActor - /// - /// The Dapr.Actors.Runtime.ActorHost that will host this actor instance. - public MyActor(ActorHost host) - : base(host) - { - } - - /// - /// This method is called whenever an actor is activated. - /// An actor is activated the first time any of its methods are invoked. - /// - protected override Task OnActivateAsync() - { - // Provides opportunity to perform some optional setup. - Console.WriteLine($"Activating actor id: {this.Id}"); - return Task.CompletedTask; - } - - /// - /// This method is called whenever an actor is deactivated after a period of inactivity. - /// - protected override Task OnDeactivateAsync() - { - // Provides Opporunity to perform optional cleanup. - Console.WriteLine($"Deactivating actor id: {this.Id}"); - return Task.CompletedTask; - } - - /// - /// Set MyData into actor's private state store - /// - /// the user-defined MyData which will be stored into state store as "my_data" state - public async Task SetDataAsync(MyData data) - { - // Data is saved to configured state store implicitly after each method execution by Actor's runtime. - // Data can also be saved explicitly by calling this.StateManager.SaveStateAsync(); - // State to be saved must be DataContract serializable. - await this.StateManager.SetStateAsync( - "my_data", // state name - data); // data saved for the named state "my_data" - - return "Success"; - } - - /// - /// Get MyData from actor's private state store - /// - /// the user-defined MyData which is stored into state store as "my_data" state - public Task GetDataAsync() - { - // Gets state from the state store. - return this.StateManager.GetStateAsync("my_data"); - } - - /// - /// Register MyReminder reminder with the actor - /// - public async Task RegisterReminder() - { - await this.RegisterReminderAsync( - "MyReminder", // The name of the reminder - null, // User state passed to IRemindable.ReceiveReminderAsync() - TimeSpan.FromSeconds(5), // Time to delay before invoking the reminder for the first time - TimeSpan.FromSeconds(5)); // Time interval between reminder invocations after the first invocation - } - - /// - /// Get MyReminder reminder details with the actor - /// - public async Task GetReminder() - { - await this.GetReminderAsync("MyReminder"); - } - - /// - /// Unregister MyReminder reminder with the actor - /// - public Task UnregisterReminder() - { - Console.WriteLine("Unregistering MyReminder..."); - return this.UnregisterReminderAsync("MyReminder"); - } - - // - // Implement IRemindeable.ReceiveReminderAsync() which is call back invoked when an actor reminder is triggered. - // - public Task ReceiveReminderAsync(string reminderName, byte[] state, TimeSpan dueTime, TimeSpan period) - { - Console.WriteLine("ReceiveReminderAsync is called!"); - return Task.CompletedTask; - } - - /// - /// Register MyTimer timer with the actor - /// - public Task RegisterTimer() - { - return this.RegisterTimerAsync( - "MyTimer", // The name of the timer - nameof(this.OnTimerCallBack), // Timer callback - null, // User state passed to OnTimerCallback() - TimeSpan.FromSeconds(5), // Time to delay before the async callback is first invoked - TimeSpan.FromSeconds(5)); // Time interval between invocations of the async callback - } - - /// - /// Unregister MyTimer timer with the actor - /// - public Task UnregisterTimer() - { - Console.WriteLine("Unregistering MyTimer..."); - return this.UnregisterTimerAsync("MyTimer"); - } - - /// - /// Timer callback once timer is expired - /// - private Task OnTimerCallBack(byte[] data) - { - Console.WriteLine("OnTimerCallBack is called!"); - return Task.CompletedTask; - } - } -} -``` - -### Register actor runtime with ASP.NET Core startup - -The Actor runtime is configured through ASP.NET Core `Startup.cs`. - -The runtime uses the ASP.NET Core dependency injection system to register actor types and essential services. This integration is provided through the `AddActors(...)` method call in `ConfigureServices(...)`. Use the delegate passed to `AddActors(...)` to register actor types and configure actor runtime settings. You can register additional types for dependency injection inside `ConfigureServices(...)`. These will be available to be injected into the constructors of your Actor types. - -Actors are implemented via HTTP calls with the Dapr runtime. This functionality is part of the application's HTTP processing pipeline and is registered inside `UseEndpoints(...)` inside `Configure(...)`. - -Paste the following code into `Startup.cs` in the `MyActorService` project: - -```csharp -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; - -namespace MyActorService -{ - public class Startup - { - public void ConfigureServices(IServiceCollection services) - { - services.AddActors(options => - { - // Register actor types and configure actor settings - options.Actors.RegisterActor(); - }); - } - - public void Configure(IApplicationBuilder app, IWebHostEnvironment env) - { - if (env.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - } - - app.UseRouting(); - - // Register actors handlers that interface with the Dapr runtime. - app.MapActorsHandlers(); - } - } -} -``` - -## Step 3: Add a client - -Create a simple console app to call the actor service. Dapr SDK provides Actor Proxy client to invoke actor methods defined in Actor Interface. - -### Create actor client project and add dependencies - -```bash -# Create Actor's Client -dotnet new console -o MyActorClient - -cd MyActorClient - -# Add Dapr.Actors nuget package. Please use the latest package version from nuget.org -dotnet add package Dapr.Actors - -# Add Actor Interface reference -dotnet add reference ../MyActor.Interfaces/MyActor.Interfaces.csproj - -cd .. -``` - -### Invoke actor methods with strongly-typed client - -You can use `ActorProxy.Create(..)` to create a strongly-typed client and invoke methods on the actor. - -Paste the following code into `Program.cs` in the `MyActorClient` project: - -```csharp -using System; -using System.Threading.Tasks; -using Dapr.Actors; -using Dapr.Actors.Client; -using MyActor.Interfaces; - -namespace MyActorClient -{ - class Program - { - static async Task MainAsync(string[] args) - { - Console.WriteLine("Startup up..."); - - // Registered Actor Type in Actor Service - var actorType = "MyActor"; - - // An ActorId uniquely identifies an actor instance - // If the actor matching this id does not exist, it will be created - var actorId = new ActorId("1"); - - // Create the local proxy by using the same interface that the service implements. - // - // You need to provide the type and id so the actor can be located. - var proxy = ActorProxy.Create(actorId, actorType); - - // Now you can use the actor interface to call the actor's methods. - Console.WriteLine($"Calling SetDataAsync on {actorType}:{actorId}..."); - var response = await proxy.SetDataAsync(new MyData() - { - PropertyA = "ValueA", - PropertyB = "ValueB", - }); - Console.WriteLine($"Got response: {response}"); - - Console.WriteLine($"Calling GetDataAsync on {actorType}:{actorId}..."); - var savedData = await proxy.GetDataAsync(); - Console.WriteLine($"Got response: {savedData}"); - } - } -} -``` - -## Running the code - -The projects that you've created can now to test the sample. - -1. Run MyActorService - - Since `MyActorService` is hosting actors, it needs to be run with the Dapr CLI. - - ```bash - cd MyActorService - dapr run --app-id myapp --app-port 5000 --dapr-http-port 3500 -- dotnet run - ``` - - You will see commandline output from both `daprd` and `MyActorService` in this terminal. You should see something like the following, which indicates that the application started successfully. - - ```txt - ... - ℹ️ Updating metadata for app command: dotnet run - ✅ You're up and running! Both Dapr and your app logs will appear here. - - == APP == info: Microsoft.Hosting.Lifetime[0] - - == APP == Now listening on: https://localhost:5001 - - == APP == info: Microsoft.Hosting.Lifetime[0] - - == APP == Now listening on: http://localhost:5000 - - == APP == info: Microsoft.Hosting.Lifetime[0] - - == APP == Application started. Press Ctrl+C to shut down. - - == APP == info: Microsoft.Hosting.Lifetime[0] - - == APP == Hosting environment: Development - - == APP == info: Microsoft.Hosting.Lifetime[0] - - == APP == Content root path: /Users/ryan/actortest/MyActorService - ``` - -2. Run MyActorClient - - `MyActorClient` is acting as the client, and it can be run normally with `dotnet run`. - - Open a new terminal an navigate to the `MyActorClient` directory. Then run the project with: - - ```bash - dotnet run - ``` - - You should see commandline output like: - - ```txt - Startup up... - Calling SetDataAsync on MyActor:1... - Got response: Success - Calling GetDataAsync on MyActor:1... - Got response: PropertyA: ValueA, PropertyB: ValueB - ``` - -> 💡 This sample relies on a few assumptions. The default listening port for an ASP.NET Core web project is 5000, which is being passed to `dapr run` as `--app-port 5000`. The default HTTP port for the Dapr sidecar is 3500. We're telling the sidecar for `MyActorService` to use 3500 so that `MyActorClient` can rely on the default value. - -Now you have successfully created an actor service and client. See the related links section to learn more. - -## Related links - -- [.NET Dapr Actors client guide]({{% ref dotnet-actors-client.md %}}) -- [.NET Dapr Actors usage guide]({{% ref dotnet-actors-usage.md %}}) diff --git a/daprdocs/content/en/dotnet-sdk-docs/dotnet-actors/dotnet-actors-serialization.md b/daprdocs/content/en/dotnet-sdk-docs/dotnet-actors/dotnet-actors-serialization.md deleted file mode 100644 index 20271f7bf..000000000 --- a/daprdocs/content/en/dotnet-sdk-docs/dotnet-actors/dotnet-actors-serialization.md +++ /dev/null @@ -1,595 +0,0 @@ ---- -type: docs -title: "Actor serialization in the .NET SDK" -linkTitle: "Actor serialization" -weight: 300000 -description: Necessary steps to serialize your types remoted and non-remoted Actors in .NET ---- -# Actor Serialization - -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. - -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. - -## 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). - -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))] -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` 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 : JsonConverter where T : struct, Enum -{ - /// Reads and converts the JSON to type . - /// The reader. - /// The type to convert. - /// An object that specifies serialization options to use. - /// The converted value. - 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()) - { - // 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}"); - } - - /// Writes a specified value as JSON. - /// The writer to write to. - /// The value to convert to JSON. - /// An object that specifies serialization options to use. - 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))] -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"} -``` - -### Polymorphic Serialization -When working with polymorphic types in Dapr Actor clients, it is essential to handle serialization and deserialization correctly to ensure that the appropriate -derived types are instantiated. Polymorphic serialization allows you to serialize objects of a base type while preserving the specific derived type information. - -To enable polymorphic deserialization, you must use the `[JsonPolymorphic]` attribute on your base type. Additionally, -it is crucial to include the `[AllowOutOfOrderMetadataProperties]` attribute to ensure that metadata properties, such as `$type` -can be processed correctly by System.Text.Json even if they are not the first properties in the JSON object. - -#### Example -```cs -[JsonPolymorphic] -[AllowOutOfOrderMetadataProperties] -public abstract class SampleValueBase -{ - public string CommonProperty { get; set; } -} - -public class DerivedSampleValue : SampleValueBase -{ - public string SpecificProperty { get; set; } -} -``` -In this example, the `SampleValueBase` class is marked with both `[JsonPolymorphic]` and `[AllowOutOfOrderMetadataProperties]` -attributes. This setup ensures that the `$type` metadata property can be correctly identified and processed during -deserialization, regardless of its position in the JSON object. - -By following this approach, you can effectively manage polymorphic serialization and deserialization in your Dapr Actor -clients, ensuring that the correct derived types are instantiated and used. - - -## 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 not 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 -- All types must either expose a public parameterless constructor or be decorated with the DataContractAttribute attribute -- Init-only setters are only supported with the use of the DataContractAttribute attribute -- Read-only fields, properties without a Get and Set method and internal or properties with private Get and Set methods are ignored during serialization -- Serialization is supported for types that use other complex types that are not themselves marked with the DataContractAttribute attribute through the use of the KnownTypesAttribute attribute -- If a type is marked with the DataContractAttribute attribute, all members you wish to serialize and deserialize must be decorated with the DataMemberAttribute attribute as well or they'll be set to their default values - -### How does deserialization work? -The approach used for deserialization depends on whether or not the type is decorated with the [DataContractAttribute](https://learn.microsoft.com/en-us/dotnet/api/system.runtime.serialization.datacontractattribute) attribute. If this attribute isn't present, an instance of the type is created using the parameterless constructor. Each of the properties and fields are then mapped into the type using their respective setters and the instance is returned to the caller. - -If the type _is_ marked with `[DataContract]`, the serializer instead uses reflection to read the metadata of the type and determine which properties or fields should be included based on whether or not they're marked with the DataMemberAttribute attribute as it's performed on an opt-in basis. It then allocates an uninitialized object in memory (avoiding the use of any constructors, parameterless or not) and then sets the value directly on each mapped property or field, even if private or uses init-only setters. Serialization callbacks are invoked as applicable throughout this process and then the object is returned to the caller. - -Use of the serialization attributes is highly recommended as they grant more flexibility to override names and namespaces and generally use more of the modern C# functionality. While the default serializer can be relied on for primitive types, it's not recommended for any of your own types, whether they be classes, structs or records. It's recommended that if you decorate a type with the DataContractAttribute attribute, you also explicitly decorate each of the members you want to serialize or deserialize with the DataMemberAttribute attribute as well. - -#### .NET Classes -Classes are fully supported in the Data Contract Serializer provided that that other rules detailed on this page and the [Data Contract Serializer](https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/serializable-types) documentation are also followed. - -The most important thing to remember here is that you must either have a public parameterless constructor or you must decorate it with the appropriate attributes. Let's review some examples to really clarify what will and won't work. - -In the following example, we present a simple class named Doodad. We don't provide an explicit constructor here, so the compiler will provide an default parameterless constructor. Because we're using [supported primitive types](###supported-primitive-types) (Guid, string and int32) and all our members have a public getter and setter, no attributes are required and we'll be able to use this class without issue when sending and receiving it from a Dapr actor method. - -```csharp -public class Doodad -{ - public Guid Id { get; set; } - public string Name { get; set; } - public int Count { get; set; } -} -``` - -By default, this will serialize using the names of the members as used in the type and whatever values it was instantiated with: - -```xml - - a06ced64-4f42-48ad-84dd-46ae6a7e333d - DoodadName - 5 - -``` - -So let's tweak it - let's add our own constructor and only use init-only setters on the members. This will fail to serialize and deserialize not because of the use of the init-only setters, but because there's no parameterless constructors. - -```csharp -// WILL NOT SERIALIZE PROPERLY! -public class Doodad -{ - public Doodad(string name, int count) - { - Id = Guid.NewGuid(); - Name = name; - Count = count; - } - - public Guid Id { get; set; } - public string Name { get; init; } - public int Count { get; init; } -} -``` - -If we add a public parameterless constructor to the type, we're good to go and this will work without further annotations. - -```csharp -public class Doodad -{ - public Doodad() - { - } - - public Doodad(string name, int count) - { - Id = Guid.NewGuid(); - Name = name; - Count = count; - } - - public Guid Id { get; set; } - public string Name { get; set; } - public int Count { get; set; } -} -``` - -But what if we don't want to add this constructor? Perhaps you don't want your developers to accidentally create an instance of this Doodad using an unintended constructor. That's where the more flexible attributes are useful. If you decorate your type with a [DataContractAttribute](https://learn.microsoft.com/en-us/dotnet/api/system.runtime.serialization.datacontractattribute) attribute, you can drop your parameterless constructor and it will work once again. - -```csharp -[DataContract] -public class Doodad -{ - public Doodad(string name, int count) - { - Id = Guid.NewGuid(); - Name = name; - Count = count; - } - - public Guid Id { get; set; } - public string Name { get; set; } - public int Count { get; set; } -} -``` - -In the above example, we don't need to also use the [DataMemberAttribute](https://learn.microsoft.com/en-us/dotnet/api/system.runtime.serialization.datamemberattribute) attributes because again, we're using [built-in primitives](###supported-primitive-types) that the serializer supports. But, we do get more flexibility if we use the attributes. From the DataContractAttribute attribute, we can specify our own XML namespace with the Namespace argument and, via the Name argument, change the name of the type as used when serialized into the XML document. - -It's a recommended practice to append the DataContractAttribute attribute to the type and the DataMemberAttribute attributes to all the members you want to serialize anyway - if they're not necessary and you're not changing the default values, they'll just be ignored, but they give you a mechanism to opt into serializing members that wouldn't otherwise have been included such as those marked as private or that are themselves complex types or collections. - -Note that if you do opt into serializing your private members, their values will be serialized into plain text - they can very well be viewed, intercepted and potentially manipulated based on how you're handing the data once serialized, so it's an important consideration whether you want to mark these members or not in your use case. - -In the following example, we'll look at using the attributes to change the serialized names of some of the members as well as introduce the [IgnoreDataMemberAttribute](https://learn.microsoft.com/en-us/dotnet/api/system.runtime.serialization.ignoredatamemberattribute) attribute. As the name indicates, this tells the serializer to skip this property even though it'd be otherwise eligible to serialize. Further, because I'm decorating the type with the DataContractAttribute attribute, it means that I can use init-only setters on the properties. - -```csharp -[DataContract(Name="Doodad")] -public class Doodad -{ - public Doodad(string name = "MyDoodad", int count = 5) - { - Id = Guid.NewGuid(); - Name = name; - Count = count; - } - - [DataMember(Name = "id")] - public Guid Id { get; init; } - [IgnoreDataMember] - public string Name { get; init; } - [DataMember] - public int Count { get; init; } -} -``` - -When this is serialized, because we're changing the names of the serialized members, we can expect a new instance of Doodad using the default values this to be serialized as: - -```xml - - a06ced64-4f42-48ad-84dd-46ae6a7e333d - 5 - -``` - -##### Classes in C# 12 - Primary Constructors -C# 12 brought us primary constructors on classes. Use of a primary constructor means the compiler will be prevented from creating the default implicit parameterless constructor. While a primary constructor on a class doesn't generate any public properties, it does mean that if you pass this primary constructor any arguments or have non-primitive types in your class, you'll either need to specify your own parameterless constructor or use the serialization attributes. - -Here's an example where we're using the primary constructor to inject an ILogger to a field and add our own parameterless constructor without the need for any attributes. - -```csharp -public class Doodad(ILogger _logger) -{ - public Doodad() {} //Our parameterless constructor - - public Doodad(string name, int count) - { - Id = Guid.NewGuid(); - Name = name; - Count = count; - } - - public Guid Id { get; set; } - public string Name { get; set; } - public int Count { get; set; } -} -``` - -And using our serialization attributes (again, opting for init-only setters since we're using the serialization attributes): - -```csharp -[DataContract] -public class Doodad(ILogger _logger) -{ - public Doodad(string name, int count) - { - Id = Guid.NewGuid(); - Name = name; - Count = count; - } - - [DataMember] - public Guid Id { get; init; } - [DataMember] - public string Name { get; init; } - [DataMember] - public int Count { get; init; } -} -``` - -#### .NET Structs -Structs are supported by the Data Contract serializer provided that they are marked with the DataContractAttribute attribute and the members you wish to serialize are marked with the DataMemberAttribute attribute. Further, to support deserialization, the struct will also need to have a parameterless constructor. This works even if you define your own parameterless constructor as enabled in C# 10. - -```csharp -[DataContract] -public struct Doodad -{ - [DataMember] - public int Count { get; set; } -} -``` - -#### .NET Records -Records were introduced in C# 9 and follow precisely the same rules as classes when it comes to serialization. We recommend that you should decorate all your records with the DataContractAttribute attribute and members you wish to serialize with DataMemberAttribute attributes so you don't experience any deserialization issues using this or other newer C# functionalities. Because record classes use init-only setters for properties by default and encourage the use of the primary constructor, applying these attributes to your types ensures that the serializer can properly otherwise accommodate your types as-is. - -Typically records are presented as a simple one-line statement using the new primary constructor concept: - -```csharp -public record Doodad(Guid Id, string Name, int Count); -``` - -This will throw an error encouraging the use of the serialization attributes as soon as you use it in a Dapr actor method invocation because there's no parameterless constructor available nor is it decorated with the aforementioned attributes. - -Here we add an explicit parameterless constructor and it won't throw an error, but none of the values will be set during deserialization since they're created with init-only setters. Because this doesn't use the DataContractAttribute attribute or the DataMemberAttribute attribute on any members, the serializer will be unable to map the target members correctly during deserialization. -```csharp -public record Doodad(Guid Id, string Name, int Count) -{ - public Doodad() {} -} -``` - -This approach does without the additional constructor and instead relies on the serialization attributes. Because we mark the type with the DataContractAttribute attribute and decorate each member with its own DataMemberAttribute attribute, the serialization engine will be able to map from the XML document to our type without issue. -```csharp -[DataContract] -public record Doodad( - [property: DataMember] Guid Id, - [property: DataMember] string Name, - [property: DataMember] int Count) -``` - -#### Supported Primitive Types -There are several types built into .NET that are considered primitive and eligible for serialization without additional effort on the part of the developer: - -- [Byte](https://learn.microsoft.com/en-us/dotnet/api/system.byte) -- [SByte](https://learn.microsoft.com/en-us/dotnet/api/system.sbyte) -- [Int16](https://learn.microsoft.com/en-us/dotnet/api/system.int16) -- [Int32](https://learn.microsoft.com/en-us/dotnet/api/system.int32) -- [Int64](https://learn.microsoft.com/en-us/dotnet/api/system.int64) -- [UInt16](https://learn.microsoft.com/en-us/dotnet/api/system.uint16) -- [UInt32](https://learn.microsoft.com/en-us/dotnet/api/system.uint32) -- [UInt64](https://learn.microsoft.com/en-us/dotnet/api/system.uint64) -- [Single](https://learn.microsoft.com/en-us/dotnet/api/system.single) -- [Double](https://learn.microsoft.com/en-us/dotnet/api/system.double) -- [Boolean](https://learn.microsoft.com/en-us/dotnet/api/system.boolean) -- [Char](https://learn.microsoft.com/en-us/dotnet/api/system.char) -- [Decimal](https://learn.microsoft.com/en-us/dotnet/api/system.decimal) -- [Object](https://learn.microsoft.com/en-us/dotnet/api/system.object) -- [String](https://learn.microsoft.com/en-us/dotnet/api/system.string) - -There are additional types that aren't actually primitives but have similar built-in support: - -- [DateTime](https://learn.microsoft.com/en-us/dotnet/api/system.datetime) -- [TimeSpan](https://learn.microsoft.com/en-us/dotnet/api/system.timespan) -- [Guid](https://learn.microsoft.com/en-us/dotnet/api/system.guid) -- [Uri](https://learn.microsoft.com/en-us/dotnet/api/system.uri) -- [XmlQualifiedName](https://learn.microsoft.com/en-us/dotnet/api/system.xml.xmlqualifiedname) - -Again, if you want to pass these types around via your actor methods, no additional consideration is necessary as they'll be serialized and deserialized without issue. Further, types that are themselves marked with the (SerializeableAttribute)[https://learn.microsoft.com/en-us/dotnet/api/system.serializableattribute] attribute will be serialized. - -#### Enumeration Types -Enumerations, including flag enumerations are serializable if appropriately marked. The enum members you wish to be serialized must be marked with the [EnumMemberAttribute](https://learn.microsoft.com/en-us/dotnet/api/system.runtime.serialization.enummemberattribute) attribute in order to be serialized. Passing a custom value into the optional Value argument on this attribute will allow you to specify the value used for the member in the serialized document instead of having the serializer derive it from the name of the member. - -The enum type does not require that the type be decorated with the `DataContractAttribute` attribute - only that the members you wish to serialize be decorated with the `EnumMemberAttribute` attributes. - -```csharp -public enum Colors -{ - [EnumMember] - Red, - [EnumMember(Value="g")] - Green, - Blue, //Even if used by a type, this value will not be serialized as it's not decorated with the EnumMember attribute -} -``` - -#### Collection Types -With regards to the data contact serializer, all collection types that implement the [IEnumerable](https://learn.microsoft.com/en-us/dotnet/api/system.collections.ienumerable) interface including arays and generic collections are considered collections. Those types that implement [IDictionary](https://learn.microsoft.com/en-us/dotnet/api/system.collections.idictionary) or the generic [IDictionary](https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.idictionary-2) are considered dictionary collections; all others are list collections. - -Not unlike other complex types, collection types must have a parameterless constructor available. Further, they must also have a method called Add so they can be properly serialized and deserialized. The types used by these collection types must themselves be marked with the `DataContractAttribute` attribute or otherwise be serializable as described throughout this document. - -#### Data Contract Versioning -As the data contract serializer is only used in Dapr with respect to serializing the values in the .NET SDK to and from the Dapr actor instances via the proxy methods, there's little need to consider versioning of data contracts as the data isn't being persisted between application versions using the same serializer. For those interested in learning more about data contract versioning visit [here](https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/data-contract-versioning). - -#### Known Types -Nesting your own complex types is easily accommodated by marking each of the types with the [DataContractAttribute](https://learn.microsoft.com/en-us/dotnet/api/system.runtime.serialization.datacontractattribute) attribute. This informs the serializer as to how deserialization should be performed. -But what if you're working with polymorphic types and one of your members is a base class or interface with derived classes or other implementations? Here, you'll use the [KnownTypeAttribute](https://learn.microsoft.com/en-us/dotnet/api/system.runtime.serialization.knowntypeattribute) attribute to give a hint to the serializer about how to proceed. - -When you apply the [KnownTypeAttribute](https://learn.microsoft.com/en-us/dotnet/api/system.runtime.serialization.knowntypeattribute) attribute to a type, you are informing the data contract serializer about what subtypes it might encounter allowing it to properly handle the serialization and deserialization of these types, even when the actual type at runtime is different from the declared type. - -```chsarp -[DataContract] -[KnownType(typeof(DerivedClass))] -public class BaseClass -{ - //Members of the base class -} - -[DataContract] -public class DerivedClass : BaseClass -{ - //Additional members of the derived class -} -``` - -In this example, the `BaseClass` is marked with `[KnownType(typeof(DerivedClass))]` which tells the data contract serializer that `DerivedClass` is a possible implementation of `BaseClass` that it may need to serialize or deserialize. Without this attribute, the serialize would not be aware of the `DerivedClass` when it encounters an instance of `BaseClass` that is actually of type `DerivedClass` and this could lead to a serialization exception because the serializer would not know how to handle the derived type. By specifying all possible derived types as known types, you ensure that the serializer can process the type and its members correctly. - -For more information and examples about using `[KnownType]`, please refer to the [official documentation](https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/data-contract-known-types). \ No newline at end of file diff --git a/daprdocs/content/en/dotnet-sdk-docs/dotnet-actors/dotnet-actors-usage.md b/daprdocs/content/en/dotnet-sdk-docs/dotnet-actors/dotnet-actors-usage.md deleted file mode 100644 index c69dc6ec1..000000000 --- a/daprdocs/content/en/dotnet-sdk-docs/dotnet-actors/dotnet-actors-usage.md +++ /dev/null @@ -1,244 +0,0 @@ ---- -type: docs -title: "Author & run actors" -linkTitle: "Authoring actors" -weight: 200000 -description: Learn all about authoring and running actors with the .NET SDK ---- - -## Author actors - -### ActorHost - -The `ActorHost`: - -- Is a required constructor parameter of all actors -- Is provided by the runtime -- Must be passed to the base class constructor -- Contains all of the state that allows that actor instance to communicate with the runtime - -```csharp -internal class MyActor : Actor, IMyActor, IRemindable -{ - public MyActor(ActorHost host) // Accept ActorHost in the constructor - : base(host) // Pass ActorHost to the base class constructor - { - } -} -``` - -Since the `ActorHost` contains state unique to the actor, you don't need to pass the instance into other parts of your code. It's recommended only create your own instances of `ActorHost` in tests. - -### Dependency injection - -Actors support [dependency injection](https://docs.microsoft.com/aspnet/core/fundamentals/dependency-injection) of additional parameters into the constructor. Any other parameters you define will have their values satisfied from the dependency injection container. - -```csharp -internal class MyActor : Actor, IMyActor, IRemindable -{ - public MyActor(ActorHost host, BankService bank) // Accept BankService in the constructor - : base(host) - { - ... - } -} -``` - -An actor type should have a single `public` constructor. The actor infrastructure uses the [`ActivatorUtilities`](https://docs.microsoft.com/en-us/dotnet/core/extensions/dependency-injection#constructor-injection-behavior) pattern for constructing actor instances. - -You can register types with dependency injection in `Startup.cs` to make them available. Read more about [the different ways of registering your types](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?#service-registration-methods). - -```csharp -// In Startup.cs -public void ConfigureServices(IServiceCollection services) -{ - ... - - // Register additional types with dependency injection. - services.AddSingleton(); -} -``` - -Each actor instance has its own dependency injection scope and remains in memory for some time after performing an operation. During that time, the dependency injection scope associated with the actor is also considered live. The scope will be released when the actor is deactivated. - -If an actor injects an `IServiceProvider` in the constructor, the actor will receive a reference to the `IServiceProvider` associated with its scope. The `IServiceProvider` can be used to resolve services dynamically in the future. - -```csharp -internal class MyActor : Actor, IMyActor, IRemindable -{ - public MyActor(ActorHost host, IServiceProvider services) // Accept IServiceProvider in the constructor - : base(host) - { - ... - } -} -``` - -When using this pattern, avoid creating many instances of **transient** services which implement `IDisposable`. Since the scope associated with an actor could be considered valid for a long time, you can accumulate many services in memory. See the [dependency injection guidelines](https://docs.microsoft.com/en-us/dotnet/core/extensions/dependency-injection-guidelines) for more information. - -### IDisposable and actors - -Actors can implement `IDisposable` or `IAsyncDisposable`. It's recommended that you rely on dependency injection for resource management rather than implementing dispose functionality in application code. Dispose support is provided in the rare case where it is truly necessary. - -### Logging - -Inside an actor class, you have access to an `ILogger` instance through a property on the base `Actor` class. This instance is connected to the ASP.NET Core logging system and should be used for all logging inside an actor. Read more about [logging](https://docs.microsoft.com/en-us/dotnet/core/extensions/logging?tabs=command-line). You can configure a variety of different logging formats and output sinks. - -Use _structured logging_ with _named placeholders_ like the example below: - -```csharp -public Task GetDataAsync() -{ - this.Logger.LogInformation("Getting state at {CurrentTime}", DateTime.UtcNow); - return this.StateManager.GetStateAsync("my_data"); -} -``` - -When logging, avoid using format strings like: `$"Getting state at {DateTime.UtcNow}"` - -Logging should use the [named placeholder syntax](https://docs.microsoft.com/dotnet/core/extensions/logging?tabs=command-line#log-message-template) which offers better performance and integration with logging systems. - -### Using an explicit actor type name - -By default, the _type_ of the actor, as seen by clients, is derived from the _name_ of the actor implementation class. The default name will be the class name (without namespace). - -If desired, you can specify an explicit type name by attaching an `ActorAttribute` attribute to the actor implementation class. - -```csharp -[Actor(TypeName = "MyCustomActorTypeName")] -internal class MyActor : Actor, IMyActor -{ - // ... -} -``` - -In the example above, the name will be `MyCustomActorTypeName`. - -No change is needed to the code that registers the actor type with the runtime, providing the value via the attribute is all that is required. - -## Host actors on the server - -### Registering actors - -Actor registration is part of `ConfigureServices` in `Startup.cs`. You can register services with dependency injection via the `ConfigureServices` method. Registering the set of actor types is part of the registration of actor services. - -Inside `ConfigureServices` you can: - -- Register the actor runtime (`AddActors`) -- Register actor types (`options.Actors.RegisterActor<>`) -- Configure actor runtime settings `options` -- Register additional service types for dependency injection into actors (`services`) - -```csharp -// In Startup.cs -public void ConfigureServices(IServiceCollection services) -{ - // Register actor runtime with DI - services.AddActors(options => - { - // Register actor types and configure actor settings - options.Actors.RegisterActor(); - - // Configure default settings - options.ActorIdleTimeout = TimeSpan.FromMinutes(10); - options.ActorScanInterval = TimeSpan.FromSeconds(35); - options.DrainOngoingCallTimeout = TimeSpan.FromSeconds(35); - options.DrainRebalancedActors = true; - }); - - // Register additional services for use with actors - services.AddSingleton(); -} -``` - -### Configuring JSON options - -The actor runtime uses [System.Text.Json](https://docs.microsoft.com/dotnet/standard/serialization/system-text-json-overview) for: - -- Serializing data to the state store -- Handling requests from the weakly-typed client - -By default, the actor runtime uses settings based on [JsonSerializerDefaults.Web](https://docs.microsoft.com/dotnet/api/system.text.json.jsonserializerdefaults?view=net-5.0). - -You can configure the `JsonSerializerOptions` as part of `ConfigureServices`: - -```csharp -// In Startup.cs -public void ConfigureServices(IServiceCollection services) -{ - services.AddActors(options => - { - ... - - // Customize JSON options - options.JsonSerializerOptions = ... - }); -} -``` - -### Actors and routing - -The ASP.NET Core hosting support for actors uses the [endpoint routing](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/routing) system. The .NET SDK provides no support hosting actors with the legacy routing system from early ASP.NET Core releases. - -Since actors uses endpoint routing, the actors HTTP handler is part of the middleware pipeline. The following is a minimal example of a `Configure` method setting up the middleware pipeline with actors. - -```csharp -// in Startup.cs -public void Configure(IApplicationBuilder app, IWebHostEnvironment env) -{ - if (env.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - } - - app.UseRouting(); - - app.UseEndpoints(endpoints => - { - // Register actors handlers that interface with the Dapr runtime. - endpoints.MapActorsHandlers(); - }); -} -``` - -The `UseRouting` and `UseEndpoints` calls are necessary to configure routing. Configure actors as part of the pipeline by adding `MapActorsHandlers` inside the endpoint middleware. - -This is a minimal example, it's valid for Actors functionality to existing alongside: - -- Controllers -- Razor Pages -- Blazor -- gRPC Services -- Dapr pub/sub handler -- other endpoints such as health checks - -### Problematic middleware - -Certain middleware may interfere with the routing of Dapr requests to the actors handlers. In particular, the `UseHttpsRedirection` is problematic for Dapr's default configuration. Dapr sends requests over unencrypted HTTP by default, which the `UseHttpsRedirection` middleware will block. This middleware cannot be used with Dapr at this time. - -```csharp -// in Startup.cs -public void Configure(IApplicationBuilder app, IWebHostEnvironment env) -{ - if (env.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - } - - // INVALID - this will block non-HTTPS requests - app.UseHttpsRedirection(); - // INVALID - this will block non-HTTPS requests - - app.UseRouting(); - - app.UseEndpoints(endpoints => - { - // Register actors handlers that interface with the Dapr runtime. - endpoints.MapActorsHandlers(); - }); -} -``` - -## Next steps - -Try the [Running and using virtual actors example]({{% ref dotnet-actors-howto.md %}}). \ No newline at end of file diff --git a/daprdocs/content/en/dotnet-sdk-docs/dotnet-ai/_index.md b/daprdocs/content/en/dotnet-sdk-docs/dotnet-ai/_index.md deleted file mode 100644 index e3398ec5a..000000000 --- a/daprdocs/content/en/dotnet-sdk-docs/dotnet-ai/_index.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -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. \ No newline at end of file diff --git a/daprdocs/content/en/dotnet-sdk-docs/dotnet-ai/dotnet-ai-conversation-howto.md b/daprdocs/content/en/dotnet-sdk-docs/dotnet-ai/dotnet-ai-conversation-howto.md deleted file mode 100644 index 88e7a3c55..000000000 --- a/daprdocs/content/en/dotnet-sdk-docs/dotnet-ai/dotnet-ai-conversation-howto.md +++ /dev/null @@ -1,79 +0,0 @@ ---- -type: docs -title: "How to: Create and use Dapr AI Conversations in the .NET SDK" -linkTitle: "How to: Use the AI Conversations client" -weight: 50010 -description: Learn how to create and use the Dapr Conversational AI client using the .NET SDK ---- - -## Prerequisites -- [.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) - -## 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(); -services.AddDaprAiConversation((serviceProvider, clientBuilder) => { - //Inject a service to source a value from - var optionsProvider = serviceProvider.GetRequiredService(); - 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. diff --git a/daprdocs/content/en/dotnet-sdk-docs/dotnet-ai/dotnet-ai-conversation-usage.md b/daprdocs/content/en/dotnet-sdk-docs/dotnet-ai/dotnet-ai-conversation-usage.md deleted file mode 100644 index 6fdec0f88..000000000 --- a/daprdocs/content/en/dotnet-sdk-docs/dotnet-ai/dotnet-ai-conversation-usage.md +++ /dev/null @@ -1,135 +0,0 @@ ---- -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 -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 services 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(); - -builder.Services.AddDaprConversationClient((serviceProvider, daprConversationClientBuilder) => { - //Retrieve an instance of the `SecretService` from the service provider - var secretService = serviceProvider.GetRequiredService(); - var daprApiToken = secretService.GetSecret("DaprApiToken").Value; - - //Configure the `DaprConversationClientBuilder` - daprConversationClientBuilder.UseDaprApiToken(daprApiToken); -}); - -var app = builder.Build(); -``` \ No newline at end of file diff --git a/daprdocs/content/en/dotnet-sdk-docs/dotnet-ai/dotnet-ai-extensions-howto.md b/daprdocs/content/en/dotnet-sdk-docs/dotnet-ai/dotnet-ai-extensions-howto.md deleted file mode 100644 index c21d2b8f2..000000000 --- a/daprdocs/content/en/dotnet-sdk-docs/dotnet-ai/dotnet-ai-extensions-howto.md +++ /dev/null @@ -1,142 +0,0 @@ ---- -type: docs -title: "How to: Using Microsoft's AI extensions with Dapr's .NET Conversation SDK" -linkTitle: "How to: Use Microsoft's AI extensions with Dapr" -weight: 50020 -description: Learn how to create and use Dapr with Microsoft's AI extensions ---- - -## Prerequisites -- [.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) - -## Installation - -To get started with this SDK, install both the [Dapr.AI](https://www.nuget.org/packages/Dapr.AI) and -[Dapr.AI.Microsoft.Extensions](https://www.nuget.org/packages/Dapr.AI.Microsoft.Extensions) packages from NuGet: -```sh -dotnet add package Dapr.AI -dotnet add package Dapr.AI.Microsoft.Extensions -``` - -The `DaprChatClient` is a Dapr-based implementation of the `IChatClient` interface provided in the -`Microsoft.Extensions.AI.Abstractions` package using Dapr's [conversation building block]({{ ref conversation-overview.md }}). It allows -developers to build against the types provided by Microsoft's abstraction while providing the greatest conformity to the -Dapr conversation building block available. As both approaches adopt OpenAI's API approach, these are expected to increasingly -converge over time. - -{{% alert title="Dapr Conversation Building Block" color="primary" %}} - -Do note that Dapr's conversation building block is still in an alpha state, meaning that the shape of the API -is likely to change future releases. It's the intent of this SDK package to provide an API that's aligned with -Microsoft's AI extensions that also maps to and conforms with the Dapr API, but the names of types and properties -may change from one release to the next, so please be aware of this possibility when using this SDK. - -{{% /alert %}} - -## About Microsoft.Extensions.AI -The `Dapr.AI.Microsoft.Extensions` package implements the `Microsoft.Extensions.AI` abstractions, providing a unified API for -AI services in .NET applications. `Microsoft.Extensions.AI` is designed to offer a consistent programming model across -different AI providers and scenarios. For detailed information about `Microsoft.Extensions.AI`, refer to the -[official documentation](https://learn.microsoft.com/en-us/dotnet/ai/microsoft-extensions-ai). - -{{% alert title="Limited Support" color="warning" %}} - -Note that Microsoft's AI extension provide many more properties and methods than Dapr's conversation building block currently -supports. This package will only map those properties that have Dapr support and will ignore the others, so just because -it's available in the Microsoft.Extensions.AI package doesn't mean it's supported by Dapr. Rely on this documentation -and the exposed XML documentation in the package to understand what is and isn't supported. - -{{% /alert %}} - -## Service Registration -The `DaprChatClient` can be registered with the dependency injection container using several extension methods. First, -ensure that you reigster the `DaprConversationClient` that's part of the `Dapr.AI` package from NuGet: - -```csharp -services.AddDaprConversationClient(); -``` - -Then register the `DaprChatClient` with your conversation component name: - -```csharp -services.AddDaprChatClient("my-conversation-component"); -``` - -### Configuration Options -You can confiugre the `DaprChatClient` using the `DaprChatClientOptions` though the current implementation only -provides configuration for the component name itself. This is expected to change in future releases. - -```csharp -services.AddDaprChatClient("my-conversation-component", options => -{ - // Configure additional options here -}); -``` - -You can also configure the service lifetime (this defaults to `ServiceLifetime.Scoped`): - -```csharp -services.AddDaprChatClient("my-conversation-component", ServiceLifetime.Singleton); -``` - -## Usage -Once registered, you can inject and use `IChatClient` in your services: - -```csharp -public class ChatService(IChatClient chatClient) -{ - public async Task> GetResponseAsync(string message) - { - var response = await chatClient.GetResponseAsync([ - new ChatMessage(ChatRole.User, - "Please write me a poem in iambic pentameter about the joys of using Dapr to develop distributed applications with .NET") - ]); - - return response.Messages.Select(msg => msg.Text).ToList(); - } -} -``` - -### Streaming Conversations -The `DaprChatClient` does not yet support streaming responses and use of the corresponding `GetStreamingResponseAsync` -methods will throw a `NotImplemenetedException`. This is expected to change in a future release once the Dapr runtime -supports this functionality. - -### Tool Integration -The client supports function calling through the `Microsoft.Extensions.AI` tool integration. Tools registered with the -conversation will be automatically available to the large language model. - -```csharp -string GetCurrentWeather() => Random.Shared.NextDouble() > 0.5 ? "It's sunny today!" : "It's raining today!"; -var toolChatOptions = new ChatOptions { Tools = [AIFunctionFactory.Create(GetCurrentWeather, "weather")] }; -var toolResponse = await chatClient.GetResponseAsync("What's the weather like today?", toolChatOptions); -foreach (var toolResp in toolResponse.Messages) -{ - Console.WriteLine(toolResp); -} -``` - -## Error Handling -The `DaprChatClient` integrates with Dapr's error handling and will throw appropriate exceptions when issues occur. - -## Configuration and Metadata -The underlying Dapr conversation component can be configured with metadata and parameters through the Dapr conversation -building block configuration. The `DaprChatClient` will respect these settings when making calls to the conversation component. - -## Best Practices - -1. **Service Lifetime**: Use `ServiceLifetime.Scoped` or `ServiceLifetime.Singleton` for the `DaprChatClient` registration to avoid creating multiple instances unnecessarily. - -2. **Error Handling**: Always wrap calls in appropriate try-catch blocks to handle both Dapr-specific and general exceptions. - -3. **Resource Management**: The `DaprChatClient` properly implements `IDisposable` through its base classes, so resources are automatically managed when using dependency injection. - -4. **Configuration**: Configure your Dapr conversation component properly to ensure optimal performance and reliability. - -## Related Links - -- [Dapr Conversation Building Block]({{ ref conversation-overview.md }}) -- [Microsoft.Extensions.AI Documentation](https://learn.microsoft.com/en-us/dotnet/ai/microsoft-extensions-ai) -- [Dapr .NET Conversation SDK]({{% ref dotnet-ai-conversation-howto.md %}}) diff --git a/daprdocs/content/en/dotnet-sdk-docs/dotnet-client/_index.md b/daprdocs/content/en/dotnet-sdk-docs/dotnet-client/_index.md deleted file mode 100644 index 05781c629..000000000 --- a/daprdocs/content/en/dotnet-sdk-docs/dotnet-client/_index.md +++ /dev/null @@ -1,412 +0,0 @@ ---- -type: docs -title: "Getting started with the Dapr client .NET SDK" -linkTitle: "Client" -weight: 20000 -description: How to get up and running with the Dapr .NET SDK -no_list: true ---- - -The Dapr client package allows you to interact with other Dapr applications from a .NET application. - -{{% alert title="Note" color="primary" %}} - If you haven't already, [try out one of the quickstarts]({{% ref quickstarts %}}) for a quick walk-through on how to use the Dapr .NET SDK with an API building block. - -{{% /alert %}} - - -## Building blocks - -The .NET SDK allows you to interface with all of the [Dapr building blocks]({{% ref building-blocks %}}). - -{{% alert title="Note" color="primary" %}} - -We will only include the dependency injection registration for the `DaprClient` in the first example -(service invocation). In nearly all other examples, it's assumed you've already registered the `DaprClient` in your -application in the latter examples and have injected an instance of `DaprClient` into your code as an instance named -`client`. - -{{% /alert %}} - -### 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 %}} - - -{{< tabpane text=true >}} - -{{% tab header="ASP.NET Core Project" %}} -```csharp -var builder = WebApplication.CreateBuilder(args); -builder.Services.AddDaprClient(); -var app = builder.Build(); - -using var scope = app.Services.CreateScope(); -var client = scope.ServiceProvider.GetRequiredService(); - -// Invokes a POST method named "deposit" that takes input of type "Transaction" -var data = new { id = "17", amount = 99m }; -var account = await client.InvokeMethodAsync("routing", "deposit", data, cancellationToken); -Console.WriteLine("Returned: id:{0} | Balance:{1}", account.Id, account.Balance); -``` -{{% /tab %}} - -{{% tab header="Console Project" %}} -using Microsoft.Extensins.Hosting; -using Microsoft.Extensions.DependencyInjection; - -var builder = Host.CreateApplicationBuilder(args); -builder.Services.AddDaprClient(); -var app = builder.Build(); - -using var scope = app.Services.CreateScope(); -var client = scope.ServiceProvider.GetRequiredService(); - -// Invokes a POST method named "deposit" that takes input of type "Transaction" -var data = new { id = "17", amount = 99m }; -var account = await client.InvokeMethodAsync("routing", "deposit", data, cancellationToken); -Console.WriteLine("Returned: id:{0} | Balance:{1}", account.Id, account.Balance); -{{% /tab %}} - -{{% tab header="HTTP" %}} -```csharp -var client = DaprClient.CreateInvokeHttpClient(appId: "routing"); - -// To set a timeout on the HTTP client: -client.Timeout = TimeSpan.FromSeconds(2); - -var deposit = new Transaction { Id = "17", Amount = 99m }; -var response = await client.PostAsJsonAsync("/deposit", deposit, cancellationToken); -var account = await response.Content.ReadFromJsonAsync(cancellationToken: cancellationToken); -Console.WriteLine("Returned: id:{0} | Balance:{1}", account.Id, account.Balance); -``` -{{% /tab %}} -{{< /tabpane >}} - -#### 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 - -```csharp -var state = new Widget() { Size = "small", Color = "yellow", }; -await client.SaveStateAsync(storeName, stateKeyName, state, cancellationToken: cancellationToken); -Console.WriteLine("Saved State!"); - -state = await client.GetStateAsync(storeName, stateKeyName, cancellationToken: cancellationToken); -Console.WriteLine($"Got State: {state.Size} {state.Color}"); - -await client.DeleteStateAsync(storeName, stateKeyName, cancellationToken: cancellationToken); -Console.WriteLine("Deleted State!"); -``` - -### Query State (Alpha) - -```csharp -var query = "{" + - "\"filter\": {" + - "\"EQ\": { \"value.Id\": \"1\" }" + - "}," + - "\"sort\": [" + - "{" + - "\"key\": \"value.Balance\"," + - "\"order\": \"DESC\"" + - "}" + - "]" + - "}"; -var queryResponse = await client.QueryStateAsync("querystore", query, cancellationToken: cancellationToken); - -Console.WriteLine($"Got {queryResponse.Results.Count}"); -foreach (var account in queryResponse.Results) -{ - Console.WriteLine($"Account: {account.Data.Id} has {account.Data.Balance}"); -} -``` - -- For a full list of state operations visit [How-To: Get & save state]({{% ref howto-get-save-state.md %}}). - -### Publish messages - -```csharp -var eventData = new { Id = "17", Amount = 10m, }; -await client.PublishEventAsync(pubsubName, "deposit", eventData, cancellationToken); -Console.WriteLine("Published deposit event!"); -``` - -- For a full list of state operations visit [How-To: Publish & subscribe]({{% ref howto-publish-subscribe.md %}}). -- Visit [.NET SDK examples](https://github.com/dapr/dotnet-sdk/tree/master/examples/Client/PublishSubscribe) for code samples and instructions to try out pub/sub - -### Interact with output bindings - -When calling `InvokeBindingAsync`, you have the option to handle serialization and encoding yourself, -or to have the SDK serialize it to JSON and then encode it to bytes for you. - -{{% alert title="Important" color="warning" %}} -Bindings differ in the shape of data they expect, take special note and ensure that the data you -are sending is handled accordingly. If you are authoring both an output and an input, make sure -that they both follow the same conventions for serialization. -{{% /alert %}} - -#### Manual serialization - -For most scenarios, you're advised to use this overload of `InvokeBindingAsync` as it gives you clarity and control over -how the data is being handled. - -_In this example, the data is sent as the UTF-8 byte representation of the string._ - -```csharp -using var client = new DaprClientBuilder().Build(); - -var request = new BindingRequest("send-email", "create") -{ - // note: This is an example payload for the Twilio SendGrid binding - Data = Encoding.UTF8.GetBytes("

Testing Dapr Bindings

This is a test.
Bye!"), - Metadata = - { - { "emailTo", "customer@example.com" }, - { "subject", "An email from Dapr SendGrid binding" }, - }, -} -await client.InvokeBindingAsync(request); -``` - -#### Automatic serialzation and encoding - -_In this example, the data is sent as a UTF-8 encoded byte representation of the value serialized to JSON._ - -```csharp -using var client = new DaprClientBuilder().Build(); - -var email = new -{ - // note: This is an example payload for the Twilio SendGrid binding - data = "

Testing Dapr Bindings

This is a test.
Bye!", - metadata = new - { - emailTo = "customer@example.com", - subject = "An email from Dapr SendGrid binding", - }, -}; -await client.InvokeBindingAsync("send-email", "create", email); -``` - -- For a full guide on output bindings visit [How-To: Use bindings]({{% ref howto-bindings.md %}}). - -### Retrieve secrets -Prior to retrieving secrets, it's important that the outbound channel be registered and ready or the SDK will be unable -to communicate bidirectionally with the Dapr sidecar. The SDK provides a helper method intended to be used for this -purpose called `CheckOutboundHealthAsync`. This isn't referring to outbound from the SDK to the runtime, so much as -outbound from the Dapr runtime back into the client application using the SDK. - -This method is simply opening a connection to the {{% ref "health_api#wait-for-specific-health-check-against-outbound-path" %}} -endpoint in the Dapr Health API and evaluating the HTTP status code returned to determine the health of the endpoint -as reported by the runtime. - -It's important to note that this and the `WaitForSidecarAsync` methods perform nearly identical operations; `WaitForSidecarAsync` -polls the `CheckOutboundHealthAsync` endpoint indefinitely until it returns a healthy status value. They are intended -exclusively for situations like secrets or configuration retrieval. Using them in other scenarios will result in -unintended behavior (e.g., the endpoint never being ready because there are no registered components that use an -"outbound" channel). - -This behavior will be changed in a future release and should only be relied on sparingly. - - -{{< tabpane text=true >}} - -{{% tab header="Multi-value-secret" %}} - -```csharp -// Get an instance of the DaprClient from DI -var client = scope.GetRequiredService(); - -// Wait for the outbound channel to be established - only use for this scenario and not generally -await client.WaitForOutboundHealthAsync(); - -// Retrieve a key-value-pair-based secret - returns a Dictionary -var secrets = await client.GetSecretAsync("mysecretstore", "key-value-pair-secret"); -Console.WriteLine($"Got secret keys: {string.Join(", ", secrets.Keys)}"); -``` - -{{% /tab %}} - -{{% tab header="Single-value-secret" %}} - -```csharp -// Get an instance of the DaprClient from DI -var client = scope.GetRequiredService(); - -// Wait for the outbound channel to be established - only use for this scenario and not generally -await client.WaitForOutboundHealthAsync(); - -// Retrieve a key-value-pair-based secret - returns a Dictionary -var secrets = await client.GetSecretAsync("mysecretstore", "key-value-pair-secret"); -Console.WriteLine($"Got secret keys: {string.Join(", ", secrets.Keys)}"); - -// Retrieve a single-valued secret - returns a Dictionary -// containing a single value with the secret name as the key -var data = await client.GetSecretAsync("mysecretstore", "single-value-secret"); -var value = data["single-value-secret"] -Console.WriteLine("Got a secret value, I'm not going to be print it, it's a secret!"); -``` - -{{% /tab %}} - -{{< /tabpane >}} - -- For a full guide on secrets visit [How-To: Retrieve secrets]({{% ref howto-secrets.md %}}). - -### Get Configuration Keys -```csharp -// Retrieve a specific set of keys. -var specificItems = await client.GetConfiguration("configstore", new List() { "key1", "key2" }); -Console.WriteLine($"Here are my values:\n{specificItems[0].Key} -> {specificItems[0].Value}\n{specificItems[1].Key} -> {specificItems[1].Value}"); - -// Retrieve all configuration items by providing an empty list. -var specificItems = await client.GetConfiguration("configstore", new List()); -Console.WriteLine($"I got {configItems.Count} entires!"); -foreach (var item in configItems) -{ - Console.WriteLine($"{item.Key} -> {item.Value}") -} -``` - -### Subscribe to Configuration Keys -```csharp -// The Subscribe Configuration API returns a wrapper around an IAsyncEnumerable>. -// Iterate through it by accessing its Source in a foreach loop. The loop will end when the stream is severed -// or if the cancellation token is cancelled. -var subscribeConfigurationResponse = await daprClient.SubscribeConfiguration(store, keys, metadata, cts.Token); -await foreach (var items in subscribeConfigurationResponse.Source.WithCancellation(cts.Token)) -{ - foreach (var item in items) - { - Console.WriteLine($"{item.Key} -> {item.Value}") - } -} -``` - -### Distributed lock (Alpha) - -#### Acquire a lock - -```csharp -using System; -using Dapr.Client; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.DependencyInjection; - -namespace LockService -{ - class Program - { - [Obsolete("Distributed Lock API is in Alpha, this can be removed once it is stable.")] - static async Task Main(string[] args) - { - const string daprLockName = "lockstore"; - const string fileName = "my_file_name"; - - var builder = Host.CreateDefaultBuilder(); - builder.ConfigureServices(services => - { - services.AddDaprClient(); - }); - var app = builder.Build(); - - using var scope = app.Services.CreateScope(); - var client = scope.ServiceProvider.GetRequiredService(); - - // Locking with this approach will also unlock it automatically, as this is a disposable object - await using (var fileLock = await client.Lock(DAPR_LOCK_NAME, fileName, "random_id_abc123", 60)) - { - if (fileLock.Success) - { - Console.WriteLine("Success"); - } - else - { - Console.WriteLine($"Failed to lock {fileName}."); - } - } - } - } -} -``` - -#### Unlock an existing lock - -```csharp -using System; -using Dapr.Client; - -namespace LockService -{ - class Program - { - static async Task Main(string[] args) - { - var daprLockName = "lockstore"; - - var builder = Host.CreateDefaultBuilder(); - builder.ConfigureServices(services => - { - services.AddDaprClient(); - }); - var app = builder.Build(); - - using var scope = app.Services.CreateScope(); - var client = scope.ServiceProvider.GetRequiredService(); - - var response = await client.Unlock(DAPR_LOCK_NAME, "my_file_name", "random_id_abc123")); - Console.WriteLine(response.status); - } - } -} -``` - -## Sidecar APIs -### Sidecar Health -While the .NET SDK provides a way to poll for the sidecar health, it is not generally recommended that developer -utilize this functionality unless they are explicitly using Dapr to also retrieve secrets or configuration values. - -There are two methods available: -- `CheckOutboundHealthAsync` which queries an outbound readiness endpoint in the Dapr Health API {{% ref "health_api#wait-for-specific-health-check-against-outbound-path" %}} -for a successful HTTP status code and reports readiness based on this value. -- `WaitForSidecarAsync` continuously polls `CheckOutboundHealthAsync` until it returns a successful status code. - -The "outbound" direction refers to the communication outbound from the Dapr runtime to your application. If your -application doesn't use actors, secret management, configuration retrieval or workflows, the runtime will not attempt -to create an outbound connection. This means that if your application takes a dependency on `WaitForSidecarAsync` -without using any of these Dapr components, it will indefinitely lock up during startup as the endpoint will never be established. - -A future release will remove these methods altogether and perform this as an internal SDK operation, so neither -method should be relied on in general. Reach out in the Discord #dotnet-sdk channel for more clarification as -to whether your scenario may necessitate using this, but in most situations, these methods should not be required. - - -### Shutdown the sidecar -```csharp -var client = new DaprClientBuilder().Build(); -await client.ShutdownSidecarAsync(); -``` - -## Related links -- [.NET SDK examples](https://github.com/dapr/dotnet-sdk/tree/master/examples) diff --git a/daprdocs/content/en/dotnet-sdk-docs/dotnet-client/dotnet-daprclient-usage.md b/daprdocs/content/en/dotnet-sdk-docs/dotnet-client/dotnet-daprclient-usage.md deleted file mode 100644 index 08c67ab95..000000000 --- a/daprdocs/content/en/dotnet-sdk-docs/dotnet-client/dotnet-daprclient-usage.md +++ /dev/null @@ -1,225 +0,0 @@ ---- -type: docs -title: "DaprClient usage" -linkTitle: "DaprClient usage" -weight: 100000 -description: Essential tips and advice for using DaprClient ---- - -## Lifetime management - -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(); -services.AddDaprClient((serviceProvider, daprBuilder) => { - var sampleService = serviceProvider.GetRequiredService(); - 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. - -## Configuring DaprClient - -A `DaprClient` can be configured by invoking methods on `DaprClientBuilder` class before calling `.Build()` to create the client. The settings for each `DaprClient` object are separate and cannot be changed after calling `.Build()`. - -```C# -var daprClient = new DaprClientBuilder() - .UseJsonSerializerSettings( ... ) // Configure JSON serializer - .Build(); -``` - -By default, the `DaprClientBuilder` will prioritize the following locations, in the following order, to source the configuration -values: - -- 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: - -- `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 - -{{% 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 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() - .UseGrpcChannelOptions(new GrpcChannelOptions { ... ThrowOperationCanceledOnCancellation = true }) - .Build(); -``` - -## Using cancellation with DaprClient - -The APIs on DaprClient that perform asynchronous operations accept an optional `CancellationToken` parameter. This follows a standard .NET idiom 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`. - -## Understanding DaprClient JSON serialization - -Many methods on `DaprClient` perform JSON serialization using the `System.Text.Json` serializer. Methods that accept an application data type as an argument will JSON serialize it, unless the documentation clearly states otherwise. - -It is worth reading the [System.Text.Json documentation](https://docs.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-overview) if you have advanced requirements. The Dapr .NET SDK provides no unique serialization behavior or customizations - it relies on the underlying serializer to convert data to and from the application's .NET types. - -`DaprClient` is configured to use a serializer options object configured from [JsonSerializerDefaults.Web](https://docs.microsoft.com/en-us/dotnet/api/system.text.json.jsonserializerdefaults?view=net-5.0). This means that `DaprClient` will use `camelCase` for property names, allow reading quoted numbers (`"10.99"`), and will bind properties case-insensitively. These are the same settings used with ASP.NET Core and the `System.Text.Json.Http` APIs, and are designed to follow interoperable web conventions. - -`System.Text.Json` as of .NET 5.0 does not have good support for all of F# language features built-in. If you are using F# you may want to use one of the converter packages that add support for F#'s features such as [FSharp.SystemTextJson](https://github.com/Tarmil/FSharp.SystemTextJson). - -### Simple guidance for JSON serialization - -Your experience using JSON serialization and `DaprClient` will be smooth if you use a feature set that maps to JSON's type system. These are general guidelines that will simplify your code where they can be applied. - -- Avoid inheritance and polymorphism -- Do not attempt to serialize data with cyclic references -- Do not put complex or expensive logic in constructors or property accessors -- Use .NET types that map cleanly to JSON types (numeric types, strings, `DateTime`) -- Create your own classes for top-level messages, events, or state values so you can add properties in the future -- Design types with `get`/`set` properties OR use the [supported pattern](https://docs.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-immutability?pivots=dotnet-5-0) for immutable types with JSON - -### Polymorphism and serialization - -The `System.Text.Json` serializer used by `DaprClient` uses the declared type of values when performing serialization. - -This section will use `DaprClient.SaveStateAsync(...)` in examples, but the advice is applicable to any Dapr building block exposed by the SDK. - -```C# -public class Widget -{ - public string Color { get; set; } -} -... - -// Storing a Widget value as JSON in the state store -widget widget = new Widget() { Color = "Green", }; -await client.SaveStateAsync("mystatestore", "mykey", widget); -``` - -In the example above, the type parameter `TValue` has its type argument inferred from the type of the `widget` variable. This is important because the `System.Text.Json` serializer will perform serialization based on the *declared type* of the value. The result is that the JSON value `{ "color": "Green" }` will be stored. - -Consider what happens when you try to use derived type of `Widget`: - -```C# -public class Widget -{ - public string Color { get; set; } -} - -public class SuperWidget : Widget -{ - public bool HasSelfCleaningFeature { get; set; } -} -... - -// Storing a SuperWidget value as JSON in the state store -Widget widget = new SuperWidget() { Color = "Green", HasSelfCleaningFeature = true, }; -await client.SaveStateAsync("mystatestore", "mykey", widget); -``` - -In this example we're using a `SuperWidget` but the variable's declared type is `Widget`. Since the JSON serializer's behavior is determined by the declared type, it only sees a simple `Widget` and will save the value `{ "color": "Green" }` instead of `{ "color": "Green", "hasSelfCleaningFeature": true }`. - -If you want the properties of `SuperWidget` to be serialized, then the best option is to override the type argument with `object`. This will cause the serializer to include all data as it knows nothing about the type. - -```C# -Widget widget = new SuperWidget() { Color = "Green", HasSelfCleaningFeature = true, }; -await client.SaveStateAsync("mystatestore", "mykey", widget); -``` - -## Error handling - -Methods on `DaprClient` will throw `DaprException` or a subclass when a failure is encountered. - -```C# -try -{ - var widget = new Widget() { Color = "Green", }; - await client.SaveStateAsync("mystatestore", "mykey", widget); -} -catch (DaprException ex) -{ - // handle the exception, log, retry, etc. -} -``` - -The most common cases of failure will be related to: - -- Incorrect configuration of Dapr component -- Transient failures such as a networking problem -- Invalid data, such as a failure to deserialize JSON - -In any of these cases you can examine more exception details through the `.InnerException` property. diff --git a/daprdocs/content/en/dotnet-sdk-docs/dotnet-cryptography/_index.md b/daprdocs/content/en/dotnet-sdk-docs/dotnet-cryptography/_index.md deleted file mode 100644 index 3fe574f14..000000000 --- a/daprdocs/content/en/dotnet-sdk-docs/dotnet-cryptography/_index.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -type: docs -title: "Dapr Cryptography .NET SDK" -linkTitle: "Cryptography" -weight: 51000 -description: Get up and running with the Dapr Cryptography .NET SDK ---- - -With the Dapr Cryptography package, you can perform high-performance encryption and decryption operations with Dapr. - -To get started with this functionality, walk through the [Dapr Cryptography({{< ref dotnet-cryptography-howto.md >}}) -how-to guide. \ No newline at end of file diff --git a/daprdocs/content/en/dotnet-sdk-docs/dotnet-cryptography/dotnet-cryptography-howto.md b/daprdocs/content/en/dotnet-sdk-docs/dotnet-cryptography/dotnet-cryptography-howto.md deleted file mode 100644 index 1bf4f20f2..000000000 --- a/daprdocs/content/en/dotnet-sdk-docs/dotnet-cryptography/dotnet-cryptography-howto.md +++ /dev/null @@ -1,74 +0,0 @@ ---- -type: docs -title: "How to: Create an use Dapr Cryptography in the .NET SDK" -linkTitle: "How to: Use the Cryptography client" -weight: 510100 -description: Learn how to create and use the Dapr Cryptography client using the .NET SDK ---- - -## Prerequisites -- [.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) - -## Installation -To get started with the Dapr Cryptography client, install the [Dapr.Cryptography package](https://www.nuget.org/packages/Dapr.Cryptography) from NuGet: -```sh -dotnet add package Dapr.Cryptography -``` - -A `DaprEncryptionClient` maintains access to networking resources in the form of TCP sockets used to communicate with -the Dapr sidecar. - -### Dependency Injection - -The `AddDaprEncryptionClient()` method will register the Dapr client with dependency injection and is the recommended approach -for using this package. This method accepts an optional options delegate for configuring the `DaprEncryptionClient` 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 `DaprEncryptionClient`: - -```csharp -services.AddDaprEncryptionClient(); -``` - -The optional configuration delegate is used to configure the `DaprEncryptionClient` by specifying options on the -`DaprEncryptionClientBuilder` as in the following example: -```csharp -services.AddSingleton(); -services.AddDaprEncryptionClient((serviceProvider, clientBuilder) => { - //Inject a service to source a value from - var optionsProvider = serviceProvider.GetRequiredService(); - var standardTimeout = optionsProvider.GetStandardTimeout(); - - //Configure the value on the client builder - clientBuilder.UseTimeout(standardTimeout); -}); -``` - -### Manual Instantiation -Rather than using dependency injection, a `DaprEncryptionClient` can also be built using the static client builder. - -For best performance, create a single long-lived instance of `DaprEncryptionClient` and provide access to that shared instance throughout -your application. `DaprEncryptionClient` instances are thread-safe and intended to be shared. - -Avoid creating a `DaprEncryptionClient` per-operation. - -A `DaprEncryptionClient` can be configured by invoking methods on the `DaprEncryptionClientBuilder` class before calling `.Build()` -to create the client. The settings for each `DaprEncryptionClient` are separate and cannot be changed after calling `.Build()`. - -```csharp -var daprEncryptionClient = new DaprEncryptionClientBuilder() - .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/Cryptography) | Clone the SDK repo to try out some examples and get started. | diff --git a/daprdocs/content/en/dotnet-sdk-docs/dotnet-cryptography/dotnet-cryptography-usage.md b/daprdocs/content/en/dotnet-sdk-docs/dotnet-cryptography/dotnet-cryptography-usage.md deleted file mode 100644 index 760e39651..000000000 --- a/daprdocs/content/en/dotnet-sdk-docs/dotnet-cryptography/dotnet-cryptography-usage.md +++ /dev/null @@ -1,131 +0,0 @@ ---- -type: docs -title: "Dapr Cryptography Client" -linkTitle: "Cryptography client" -weight: 510005 -description: Learn how to create Dapr Crytography clients ---- - -The Dapr Cryptography package allows you to perform encryption and decryption operations provided by the Dapr sidecar. - -## Lifetime management -A `DaprEncryptionClient` is a version of the Dapr client that is dedicated to interacting with the Dapr Cryptography 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 `DaprEncryptionClient` and provide access to that shared -instance throughout your application. `DaprEncryptionClient` instances are thread-safe and intended to be shared. - -This can be aided by utilizing the dependency injection functionality. The registration method supports registration -as a singleton, a scoped instance, or as a 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 `DaprEncryptionClient` for each operation. - -## Configuring `DaprEncryptionClient` via `DaprEncryptionClientBuilder` -A `DaprCryptographyClient` can be configured by invoking methods on the `DaprEncryptionClientBuilder` class before calling -`.Build()` to create the client itself. The settings for each `DaprEncryptionClientBuilder` are separate can cannot be -changed after calling `.Build()`. - -```cs -var daprEncryptionClient = new DaprEncryptionClientBuilder() - .UseDaprApiToken("abc123") //Specify the API token used to authenticate to the Dapr sidecar - .Build(); -``` - -The `DaprEncryptionClientBuilder` 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 daprEncryptionClient = new DaprEncryptionClientBuilder() - .UseGrpcChannelOptions(new GrpcChannelOptions { .. ThrowOperationCanceledOnCancellation = true }) - .Build(); -``` - -## Using cancellation with `DaprEncryptionClient` -The APIs on `DaprEncryptionClient` 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 `DaprEncryptionClient` via dependency injection -Using the built-in extension methods for registering the `DaprEncryptionClient` 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 `DaprEncryptionClientBuilder` 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 `DaprEncryptionClient` is configured with the -default settings. - -```cs -var builder = WebApplication.CreateBuilder(args); - -builder.Services.AddDaprEncryptionClent(); //Registers the `DaprEncryptionClient` 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 `DaprEncryptionClientBuiler` and exposes methods for configuring -the necessary options. - -```cs -var builder = WebApplication.CreateBuilder(args); - -builder.Services.AddDaprEncryptionClient((_, daprEncrpyptionClientBuilder) => { - //Set the API token - daprEncryptionClientBuilder.UseDaprApiToken("abc123"); - //Specify a non-standard HTTP endpoint - daprEncryptionClientBuilder.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(); - -builder.Services.AddDaprEncryptionClient((serviceProvider, daprEncryptionClientBuilder) => { - //Retrieve an instance of the `SecretService` from the service provider - var secretService = serviceProvider.GetRequiredService(); - var daprApiToken = secretService.GetSecret("DaprApiToken").Value; - - //Configure the `DaprEncryptionClientBuilder` - daprEncryptionClientBuilder.UseDaprApiToken(daprApiToken); -}); - -var app = builder.Build(); -``` \ No newline at end of file diff --git a/daprdocs/content/en/dotnet-sdk-docs/dotnet-distributed-lock/_index.md b/daprdocs/content/en/dotnet-sdk-docs/dotnet-distributed-lock/_index.md deleted file mode 100644 index b8aad64e3..000000000 --- a/daprdocs/content/en/dotnet-sdk-docs/dotnet-distributed-lock/_index.md +++ /dev/null @@ -1,143 +0,0 @@ ---- -type: docs -title: "Dapr Distributed Lock .NET SDK" -linkTitle: "Distributed Lock" -weight: 61000 -description: Get up and running with the Dapr Distributed .NET SDK ---- - -With the Dapr Distributed Lock package, you can create and remove locks on resources to manage exclusivity across -your distributed applications. - -While this capability is implemented in both the `Dapr.Client` and `Dapr.DistributedLock` packages, the approach differs -slightly between them and a future release will see the `Dapr.Client` package be deprecated. It's recommended that new -implementations use the `Dapr.DistributedLock` package. This document will reflect the implementation in the -`Dapr.DistributedLock` package. - -## Lifetime management -A `DaprDistributedLockClient` is a version of the Dapr client that is dedicated to interacting with Dapr's distributed -lock 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 runtime. - -For best performance, it is recommended that you utilize the dependency injection container mechanisms provided with the -`Dapr.DistributedLock` package to provide easy access to an injected instance throughout your application. These injected -instances are thread-safe and intended to be used across different types within your application. Registration via -dependency injection can utilize values from an `IConfiguration` or other injected services in a way that's impractical -when creating the client from scratch in each of your classes. - -If you do opt to manually create a `DaprDistributedLockClient` instance, it is recommended that you use the `DaprClientBuilder` -to create the client. This will ensure that the client is properly configured to communicate with the Dapr sidecar runtime.` - -Avoid creawting a `DaprDistributedLockClient` for each operation. - -## Configuring a `DaprDistributedLockClient` via `DaprDistributedLockBuilder` - -A `DaprDistributedLockClient` can be configured by invoking methods on the `DaprDistributedLockBuilder` class before calling -`.Build()` to create the client itself. The settings for each `DaprDistributedLockClient` are separate and cannot be changed -after calling `.Build()`. - -```csharp -var daprDistributedLockClient = new DaprDistributedLockBuilder() - .UseDaprApiToken("abc123") // Optionally specify the API token used to authenticate to other Dapr sidecars - .Build(); -``` - -The `DaprDistributedLockBuilder` 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 daprDistributedLockClient = new DaprDistributedLockBuilder() - .UseGrpcChannelOptions(new GrpcChannelOptions { ... ThrowOperationCanceledOnCancellation = true }) - .Build(); -``` - -## Using cancellation with `DaprDistributedLockClient` - -The APIs on `DaprDistributedLockClient` 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 `DaprDistributedLockClient` via dependency injection - -Using the built-in extension methods for registering the `DaprDistributedLockClient` 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 `DaprDistributedLockBuilder` 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 `DaprDistributedLockClient` is configured with the -default settings. - -```cs -var builder = WebApplication.CreateBuilder(args); - -builder.Services.AddDaprDistributedLock(); //Registers the `DaprDistributedLockClient` 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 `DaprDistributedLockBuilder` and exposes methods for configuring -the necessary options. - -```cs -var builder = WebApplication.CreateBuilder(args); - -builder.Services.AddDaprDistributedLock((_, daprDistributedLockBuilder) => { - //Set the API token - daprDistributedLockBuilder.UseDaprApiToken("abc123"); - //Specify a non-standard HTTP endpoint - daprDistributedLockBuilder.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(); - -builder.Services.AddDaprDistributedLock((serviceProvider, daprDistributedLockBuilder) => { - //Retrieve an instance of the `SecretService` from the service provider - var secretService = serviceProvider.GetRequiredService(); - var daprApiToken = secretService.GetSecret("DaprApiToken").Value; - - //Configure the `DaprDistributedLockBuilder` - daprDistributedLockBuilder.UseDaprApiToken(daprApiToken); -}); - -var app = builder.Build(); -``` - diff --git a/daprdocs/content/en/dotnet-sdk-docs/dotnet-distributed-lock/dotnet-distributedlock-howto.md b/daprdocs/content/en/dotnet-sdk-docs/dotnet-distributed-lock/dotnet-distributedlock-howto.md deleted file mode 100644 index 89fbe0afd..000000000 --- a/daprdocs/content/en/dotnet-sdk-docs/dotnet-distributed-lock/dotnet-distributedlock-howto.md +++ /dev/null @@ -1,80 +0,0 @@ ---- -type: docs -title: "How to: Create and use Dapr Distributed Lock in the .NET SDK" -linkTitle: "How to: Use the Distributed Lock client" -weight: 61050 -description: Learn how to create and use the Dapr Distributed Lock client using the .NET SDK ---- - -## Prerequisites -- [.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) - -## Installation - -To get started with the Dapr Distributed lock .NET SDK client, install the [Dapr.Distributed Lock package](https://www.nuget.org/packages/Dapr.DistributedLock) from NuGet: -```sh -dotnet add package Dapr.DistributedLock -``` - -A `DaprDistributedLockClient` maintains access to networking resources in the form of TCP sockets used to communicate with the Dapr sidecar. - -### Dependency Injection - -The `AddDaprDistributedLock()` 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 `DaprDistributedLockClient` 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 `DaprDistributedLockClient`: - -```csharp -services.AddDaprDistributedLock(); -``` - -The optional configuration delegate is used to configure the `DaprDistributedLockClient` by specifying options on the -`DaprDistributedLockBuilder` as in the following example: -```csharp -services.AddSingleton(); -services.AddDaprDistributedLock((serviceProvider, clientBuilder) => { - //Inject a service to source a value from - var optionsProvider = serviceProvider.GetRequiredService(); - var standardTimeout = optionsProvider.GetStandardTimeout(); - - //Configure the value on the client builder - clientBuilder.UseTimeout(standardTimeout); -}); -``` - -### Manual Instantiation -Rather than using dependency injection, a `DaprDistributedLockClient` can also be built using the static client builder. - -For best performance, create a single long-lived instance of `DaprDistributedLockClient` and provide access to that shared instance throughout -your application. `DaprDistributedLockClient` instances are thread-safe and intended to be shared. - -Avoid creating a `DaprDistributedLockClient` per-operation. - -A `DaprDistributedLockClient` can be configured by invoking methods on the `DaprDistributedLockBuilder` class before calling `.Build()` -to create the client. The settings for each `DaprDistributedLockClient` are separate and cannot be changed after calling `.Build()`. - -```csharp -var daprDistributedLockClient = new DaprDistributedLockBuilder() - .UseJsonSerializerSettings( ... ) //Configure JSON serializer - .Build(); -``` - -See the .NET [documentation here]({{% ref dotnet-distributed-lock %}}) for more information about the options available -when configuring the Dapr Distributed Lock client via the builder. - -## Try it out -Put the Dapr Distributed Lock .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 Distributed Lock API to place and remove locks for managing -resource exclusivity across your distributed applications. diff --git a/daprdocs/content/en/dotnet-sdk-docs/dotnet-guidance/_index.md b/daprdocs/content/en/dotnet-sdk-docs/dotnet-guidance/_index.md deleted file mode 100644 index 2df0581fc..000000000 --- a/daprdocs/content/en/dotnet-sdk-docs/dotnet-guidance/_index.md +++ /dev/null @@ -1,53 +0,0 @@ ---- -type: docs -title: "Best Practices for the Dapr .NET SDK" -linkTitle: "Best Practices" -weight: 85000 -description: Using Dapr .NET SDK effectively ---- - -## Building with confidence - -The Dapr .NET SDK offers a rich set of capabilities for building distributed applications. This section provides -practical guidance for using the SDK effectively in production scenarios—focusing on reliability, maintainability, and -developer experience. - -Topics covered include: - -- Error handling strategies across Dapr building blocks -- Managing experimental features and suppressing related warnings -- Leveraging source analyzers and generators to reduce boilerplate and catch issues early -- General .NET development practices in Dapr-based applications - -## Error model guidance - -Dapr operations can fail for many reasons—network issues, misconfigured components, or transient faults. The SDK -provides structured error types to help you distinguish between retryable and fatal errors. - -Learn how to use `DaprException` and its derived types effectively [here]({{% ref dotnet-guidance-error-model.md %}}). - -## Experimental attributes - -Some SDK features are marked as experimental and may change in future releases. These are annotated with -`[Experimental]` and generate build-time warnings by default. You can: - -- Suppress warnings selectively using `#pragma warning disable` -- Use `SuppressMessage` attributes for finer control -- Track experimental usage across your codebase - -Learn more about our use of the `[Experimenta]` attribute [here]({{% ref dotnet-guidance-experimental-attributes.md %}}). - -## Source tooling - -The SDK includes Roslyn-based analyzers and source generators to help you write better code with less effort. These tools: - -- Warn about common misuses of the SDK -- Generate boilerplate for actor registration and invocation -- Support IDE integration for faster feedback - -Read more about how to install and use these analyzers [here]({{% ref dotnet-guidance-source-generators.md %}}). - -## Additional guidance - -This section is designed to support a wide range of development scenarios. As your applications grow in complexity, you'll find increasingly relevant practices and patterns for working with Dapr in .NET—from actor lifecycle management to configuration strategies and performance tuning. - diff --git a/daprdocs/content/en/dotnet-sdk-docs/dotnet-guidance/dotnet-guidance-error-model.md b/daprdocs/content/en/dotnet-sdk-docs/dotnet-guidance/dotnet-guidance-error-model.md deleted file mode 100644 index 53f84b5b0..000000000 --- a/daprdocs/content/en/dotnet-sdk-docs/dotnet-guidance/dotnet-guidance-error-model.md +++ /dev/null @@ -1,141 +0,0 @@ ---- -type: docs -title: "Error Model in the Dapr .NET SDK" -linkTitle: "Error Model" -weight: 85100 -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 it 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 notifying 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 client's 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 with further 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"). - - - - - - - - - - diff --git a/daprdocs/content/en/dotnet-sdk-docs/dotnet-guidance/dotnet-guidance-experimental-attributes.md b/daprdocs/content/en/dotnet-sdk-docs/dotnet-guidance/dotnet-guidance-experimental-attributes.md deleted file mode 100644 index 23dc0b6f9..000000000 --- a/daprdocs/content/en/dotnet-sdk-docs/dotnet-guidance/dotnet-guidance-experimental-attributes.md +++ /dev/null @@ -1,138 +0,0 @@ ---- -type: docs -title: "Experimental Attributes" -linkTitle: "Experimental Attributes" -weight: 85200 -description: Learn about why we mark some methods with the `[Experimental]` attribute ---- - -## Experimental Attributes - -### Introduction to Experimental Attributes - -With the release of .NET 8, C# 12 introduced the `[Experimental]` attribute, which provides a standardized way to mark -APIs that are still in development or experimental. This attribute is defined in the `System.Diagnostics.CodeAnalysis` -namespace and requires a diagnostic ID parameter used to generate compiler warnings when the experimental API -is used. - -In the Dapr .NET SDK, we now use the `[Experimental]` attribute instead of `[Obsolete]` to mark building blocks and -components that have not yet passed the stable lifecycle certification. This approach provides a clearer distinction -between: - -1. **Experimental APIs** - Features that are available but still evolving and have not yet been certified as stable -according to the [Dapr Component Certification Lifecycle](https://docs.dapr.io/operations/components/certification-lifecycle/). - -2. **Obsolete APIs** - Features that are truly deprecated and will be removed in a future release. - -### Usage in the Dapr .NET SDK - -In the Dapr .NET SDK, we apply the `[Experimental]` attribute at the class level for building blocks that are still in -the Alpha or Beta stages of the [Component Certification Lifecycle](https://docs.dapr.io/operations/components/certification-lifecycle/). -The attribute includes: - -- A diagnostic ID that identifies the experimental building block -- A URL that points to the relevant documentation for that block - -For example: - -```csharp -using System.Diagnostics.CodeAnalysis; -namespace Dapr.Cryptography.Encryption -{ - [Experimental("DAPR_CRYPTOGRAPHY", UrlFormat = "https://docs.dapr.io/developing-applications/building-blocks/cryptography/cryptography-overview/")] - public class DaprEncryptionClient - { - // Implementation - } -} -``` - -The diagnostic IDs follow a naming convention of `DAPR_[BUILDING_BLOCK_NAME]`, such as: - -- `DAPR_CONVERSATION` - For the Conversation building block -- `DAPR_CRYPTOGRAPHY` - For the Cryptography building block -- `DAPR_JOBS` - For the Jobs building block -- `DAPR_DISTRIBUTEDLOCK` - For the Distributed Lock building block - -### Suppressing Experimental Warnings - -When you use APIs marked with the `[Experimental]` attribute, the compiler will generate errors. -To build your solution without marking your own code as experimental, you will need to suppress these errors. Here are -several approaches to do this: - -#### Option 1: Using #pragma directive - -You can use the `#pragma warning` directive to suppress the warning for specific sections of code: - -```csharp -// Disable experimental warning -#pragma warning disable DAPR_CRYPTOGRAPHY -// Your code using the experimental API -var client = new DaprEncryptionClient(); -// Re-enable the warning -#pragma warning restore DAPR_CRYPTOGRAPHY -``` - -This approach is useful when you want to suppress warnings only for specific sections of your code. - -#### Option 2: Project-level suppression - -To suppress warnings for an entire project, add the following to your `.csproj` file. -file. - -```xml - - $(NoWarn);DAPR_CRYPTOGRAPHY - -``` - -You can include multiple diagnostic IDs separated by semicolons: - -```xml - - $(NoWarn);DAPR_CONVERSATION;DAPR_JOBS;DAPR_DISTRIBUTEDLOCK;DAPR_CRYPTOGRAPHY - -``` - -This approach is particularly useful for test projects that need to use experimental APIs. - -#### Option 3: Directory-level suppression - -For suppressing warnings across multiple projects in a directory, add a `Directory.Build.props` file: - -```xml - - $(NoWarn);DAPR_CONVERSATION;DAPR_JOBS;DAPR_DISTRIBUTEDLOCK;DAPR_CRYPTOGRAPHY - -``` - -This file should be placed in the root directory of your test projects. You can learn more about using -`Directory.Build.props` files in the -[MSBuild documentation](https://learn.microsoft.com/en-us/visualstudio/msbuild/customize-by-directory). - -### Lifecycle of Experimental APIs - -As building blocks move through the certification lifecycle and reach the "Stable" stage, the `[Experimental]` attribute will be removed. No migration or code changes will be required from users when this happens, except for the removal of any warning suppressions if they were added. - -Conversely, the `[Obsolete]` attribute will now be reserved exclusively for APIs that are truly deprecated and scheduled for removal. When you see a method or class marked with `[Obsolete]`, you should plan to migrate away from it according to the migration guidance provided in the attribute message. - -### Best Practices - -1. **In application code:** - - Be cautious when using experimental APIs, as they may change in future releases - - Consider isolating usage of experimental APIs to make future updates easier - - Document your use of experimental APIs for team awareness - -2. **In test code:** - - Use project-level suppression to avoid cluttering test code with warning suppressions - - Regularly review which experimental APIs you're using and check if they've been stabilized - -3. **When contributing to the SDK:** - - Use `[Experimental]` for new building blocks that haven't completed certification - - Use `[Obsolete]` only for truly deprecated APIs - - Provide clear documentation links in the `UrlFormat` parameter - -### Additional Resources - -- [Dapr Component Certification Lifecycle](https://docs.dapr.io/operations/components/certification-lifecycle/) -- [C# Experimental Attribute Documentation](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-12.0/experimental-attribute) diff --git a/daprdocs/content/en/dotnet-sdk-docs/dotnet-guidance/dotnet-guidance-source-generators.md b/daprdocs/content/en/dotnet-sdk-docs/dotnet-guidance/dotnet-guidance-source-generators.md deleted file mode 100644 index ebe49cf55..000000000 --- a/daprdocs/content/en/dotnet-sdk-docs/dotnet-guidance/dotnet-guidance-source-generators.md +++ /dev/null @@ -1,82 +0,0 @@ ---- -type: docs -title: "Dapr source code analyzers and generators" -linkTitle: "Roslyn Analyzers/Generators" -weight: 85300 -description: Code analyzers and fixes for common Dapr issues -no_list: true ---- - -Dapr supports a growing collection of optional Roslyn analyzers and code fix providers that inspect your code for -code quality issues. Starting with the release of v1.16, developers have the opportunity to install additional projects -from NuGet alongside each of the standard capability packages to enable these analyzers in their solutions. - -{{% alert title="Note" color="primary" %}} - -A future release of the Dapr .NET SDK will include these analyzers by default without requiring a separate package -install. - -{{% /alert %}} - -Rule violations will typically be marked as `Info` or `Warning` so that if the analyzer identifies an issue, it won't -necessarily break builds. All code analysis violations appear with the prefix "DAPR" and are uniquely distinguished -by a number following this prefix. - -{{% alert title="Note" color="primary" %}} - -At this time, the first two digits of the diagnostic identifier map one-to-one to distinct Dapr packages, but this -is subject to change in the future as more analyzers are developed. - -{{% /alert %}} - -## Install and configure analyzers -The following packages will be available via NuGet following the v1.16 Dapr release: -- Dapr.Actors.Analyzers -- Dapr.Jobs.Analyzers -- Dapr.Workflow.Analyzers - -Install each NuGet package on every project where you want the analyzers to run. The package will be installed as a -project dependency and analyzers will run as you write your code or as part of a CI/CD build. The analyzers will flag -issues in your existing code and warn you about new issues as you build your project. - -Many of our analyzers have associated code fixes that can be applied to automatically correct the problem. If your IDE -supports this capability, any available code fixes will show up as an inline menu option in your code. - -Further, most of our analyzers should also report a specific line and column number in your code of the syntax that's -been identified as a key aspect of the rule. If your IDE supports it, double clicking any of the analyzer warnings -should jump directly to the part of your code responsible for the violating the analyzer's rule. - -### Suppress specific analyzers -If you wish to keep an analyzer from firing against some particular piece of your project, their outputs can be -individually targeted for suppression through a number of ways. Read more about suppressing analyzers in projects -or files in the associated [.NET documentation](https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/suppress-warnings#use-the-suppressmessageattribute). - -### Disable all analyzers -If you wish to disable all analyzers in your project without removing any packages providing them, set -the `EnableNETAnalyzers` property to `false` in your csproj file. - -## Available Analyzers - -| Diagnostic ID | Dapr Package | Category | Severity | Version Added | Description | Code Fix Available | -| -- | -- |------------------|--------------|-----------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------| -- | -| DAPR1301 | Dapr.Workflow | Usage | Warning | 1.16 | The workflow type is not registered with the dependency injection provider | Yes | -| DAPR1302 | Dapr.Workflow | Usage | Warning | 1.16 | The workflow activity type is not registered with the dependency injection provider | Yes | -| DAPR1401 | Dapr.Actors | Usage | Warning | 1.16 | Actor timer method invocations require the named callback method to exist on type | No | -| DAPR1402 | Dapr.Actors | Usage | Warning | 1.16 | The actor type is not registered with dependency injection | Yes | -| DAPR1403 | Dapr.Actors | Interoperability | Info | 1.16 | Set options.UseJsonSerialization to true to support interoperability with non-.NET actors | Yes | -| DAPR1404 | Dapr.Actors | Usage | Warning | 1.16 | Call app.MapActorsHandlers to map endpoints for Dapr actors | Yes | -| DAPR1501 | Dapr.Jobs | Usage | Warning | 1.16 | Job invocations require the MapDaprScheduledJobHandler to be set and configured for each anticipated job on IEndpointRouteBuilder | No | - -## Analyzer Categories -The following are each of the eligible categories that an analyzer can be assigned to and are modeled after the -standard categories used by the .NET analyzers: -- Design -- Documentation -- Globalization -- Interoperability -- Maintainability -- Naming -- Performance -- Reliability -- Security -- Usage \ No newline at end of file diff --git a/daprdocs/content/en/dotnet-sdk-docs/dotnet-integrations/_index.md b/daprdocs/content/en/dotnet-sdk-docs/dotnet-integrations/_index.md deleted file mode 100644 index ff03c4115..000000000 --- a/daprdocs/content/en/dotnet-sdk-docs/dotnet-integrations/_index.md +++ /dev/null @@ -1,42 +0,0 @@ ---- -type: docs -title: "Developing applications with the Dapr .NET SDK" -linkTitle: "Deployment Integrations" -weight: 90000 -description: Deployment integrations with the Dapr .NET SDK ---- - -## Thinking more than one at a time - -Using your favorite IDE or editor to launch an application typically assumes that you only need to run one thing: -the application you're debugging. However, developing microservices challenges you to think about your local -development process for *more than one at a time*. A microservices application has multiple services that you might -need running simultaneously, and dependencies (like state stores) to manage. - -Adding Dapr to your development process means you need to manage the following concerns: - -- Each service you want to run -- A Dapr sidecar for each service -- Dapr component and configuration manifests -- Additional dependencies such as state stores -- optional: the Dapr placement service for actors - -This document assumes that you're building a production application and want to create a repeatable and robust set of -development practices. The guidance here is generalized, and applies to any .NET server application using -Dapr (including actors). - -## Managing components - -You have two primary methods of storing component definitions for local development with Dapr: - -- Use the default location (`~/.dapr/components`) -- Use your own location - -Creating a folder within your source code repository to store components and configuration will give you a way to -version and share these definitions. The guidance provided here will assume you created a folder next to the -application source code to store these files. - -## Development options - -Choose one of these links to learn about tools you can use in local development scenarios. It's suggested that -you familiarize yourself with each of them to get a sense of the options provided by the .NET SDK. diff --git a/daprdocs/content/en/dotnet-sdk-docs/dotnet-integrations/dotnet-development-dapr-aspire.md b/daprdocs/content/en/dotnet-sdk-docs/dotnet-integrations/dotnet-development-dapr-aspire.md deleted file mode 100644 index f60e8b7fe..000000000 --- a/daprdocs/content/en/dotnet-sdk-docs/dotnet-integrations/dotnet-development-dapr-aspire.md +++ /dev/null @@ -1,155 +0,0 @@ ---- -type: docs -title: "Dapr .NET SDK Development with .NET Aspire" -linkTitle: ".NET Aspire" -weight: 90300 -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). - -An end-to-end demonstration featuring the following and demonstrating service invocation between multiple Dapr-enabled -services can be found [here](https://github.com/dapr/dotnet-sdk/tree/master/examples/Hosting/Aspire/ServiceInvocationDemo). - -## Prerequisites -- Both the Dapr .NET SDK and .NET Aspire are 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.16 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 twp projects that'll serve as our Dapr application and demonstrate Dapr functionality. From the same -directory, use the following to create an empty ASP.NET Core project called `FrontEndApp` and another called -'BackEndApp'. Either one will be created relative to your current directory in -`FrontEndApp\FrontEndApp.csproj` and `BackEndApp\BackEndApp.csproj`, respectively. - -```sh -dotnet new web --name FrontEndApp -``` - -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 `CommunityToolkit.Aspire.Hosting.Dapr` package from NuGet into the project. - -We'll also add a reference to our `FrontEndApp` project so we can reference it during the registration process. - -{{% alert color="primary" %}} - -This package was previously called `Aspire.Hosting.Dapr`, which has been [marked as deprecated](https://www.nuget.org/packages/Aspire.Hosting.Dapr). - -{{% /alert %}} - -```sh -cd aspiredemo.AppHost -dotnet add package CommunityToolkit.Aspire.Hosting.Dapr -dotnet add reference ../FrontEndApp/ -dotnet add reference ../BackEndApp/ -``` - -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 backEndApp = builder - .AddProject("be") - .WithDaprSidecar(); - -var frontEndApp = builder - .AddProject("fe") - .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 = "how-dapr-identifies-your-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("be") - .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 %}} - -Finally, let's add an endpoint to the back-end app that we can invoke using Dapr's service invocation to display to a -page to demonstrate that Dapr is working as expected. - -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. - diff --git a/daprdocs/content/en/dotnet-sdk-docs/dotnet-integrations/dotnet-development-dapr-cli.md b/daprdocs/content/en/dotnet-sdk-docs/dotnet-integrations/dotnet-development-dapr-cli.md deleted file mode 100644 index 2c7e95f96..000000000 --- a/daprdocs/content/en/dotnet-sdk-docs/dotnet-integrations/dotnet-development-dapr-cli.md +++ /dev/null @@ -1,53 +0,0 @@ ---- -type: docs -title: "Dapr .NET SDK Development with Dapr CLI" -linkTitle: "Dapr CLI" -weight: 90100 -description: Learn about local development with the Dapr CLI ---- - -## Dapr CLI - -*Consider this to be a .NET companion to the [Dapr Self-Hosted with Docker Guide]({{% ref self-hosted-with-docker.md %}})*. - -The Dapr CLI provides you with a good base to work from by initializing a local redis container, zipkin container, the placement service, and component manifests for redis. This will enable you to work with the following building blocks on a fresh install with no additional setup: - -- [Service invocation]({{% ref service-invocation %}}) -- [State Store]({{% ref state-management %}}) -- [Pub/Sub]({{% ref pubsub %}}) -- [Actors]({{% ref actors %}}) - -You can run .NET services with `dapr run` as your strategy for developing locally. Plan on running one of these commands per-service in order to launch your application. - -- **Pro:** this is easy to set up since its part of the default Dapr installation -- **Con:** this uses long-running docker containers on your machine, which might not be desirable -- **Con:** the scalability of this approach is poor since it requires running a separate command per-service - -### Using the Dapr CLI - -For each service you need to choose: - -- A unique app-id for addressing (`app-id`) -- A unique listening port for HTTP (`port`) - -You also should have decided on where you are storing components (`components-path`). - -The following command can be run from multiple terminals to launch each service, with the respective values substituted. - -```sh -dapr run --app-id --app-port --components-path -- dotnet run -p --urls http://localhost: -``` - -**Explanation:** this command will use `dapr run` to launch each service and its sidecar. The first half of the command (before `--`) passes required configuration to the Dapr CLI. The second half of the command (after `--`) passes required configuration to the `dotnet run` command. - -{{% alert title="💡 Ports" color="primary" %}} -Since you need to configure a unique port for each service, you can use this command to pass that port value to **both** Dapr and the service. `--urls http://localhost:` will configure ASP.NET Core to listen for traffic on the provided port. Using configuration at the commandline is a more flexible approach than hardcoding a listening port elsewhere. -{{% /alert %}} - -If any of your services do not accept HTTP traffic, then modify the command above by removing the `--app-port` and `--urls` arguments. - -### Next steps - -If you need to debug, then use the attach feature of your debugger to attach to one of the running processes. - -If you want to scale up this approach, then consider building a script which automates this process for your whole application. diff --git a/daprdocs/content/en/dotnet-sdk-docs/dotnet-integrations/dotnet-development-docker-compose.md b/daprdocs/content/en/dotnet-sdk-docs/dotnet-integrations/dotnet-development-docker-compose.md deleted file mode 100644 index 99cdecf52..000000000 --- a/daprdocs/content/en/dotnet-sdk-docs/dotnet-integrations/dotnet-development-docker-compose.md +++ /dev/null @@ -1,44 +0,0 @@ ---- -type: docs -title: "Dapr .NET SDK Development with Docker-Compose" -linkTitle: "Docker Compose" -weight: 90200 -description: Learn about local development with Docker-Compose ---- - -## Docker-Compose - -*Consider this to be a .NET companion to the [Dapr Self-Hosted with Docker Guide]({{% ref self-hosted-with-docker.md %}})*. - -`docker-compose` is a CLI tool included with Docker Desktop that you can use to run multiple containers at a time. It is a way to automate the lifecycle of multiple containers together, and offers a development experience similar to a production environment for applications targeting Kubernetes. - -- **Pro:** Since `docker-compose` manages containers for you, you can make dependencies part of the application definition and stop the long-running containers on your machine. -- **Con:** most investment required, services need to be containerized to get started. -- **Con:** can be difficult to debug and troubleshoot if you are unfamilar with Docker. - -### Using docker-compose - -From the .NET perspective, there is no specialized guidance needed for `docker-compose` with Dapr. `docker-compose` runs containers, and once your service is in a container, configuring it similar to any other programming technology. - -{{% alert title="💡 App Port" color="primary" %}} -In a container, an ASP.NET Core app will listen on port 80 by default. Remember this for when you need to configure the `--app-port` later. -{{% /alert %}} - -To summarize the approach: - -- Create a `Dockerfile` for each service -- Create a `docker-compose.yaml` and place check it in to the source code repository - -To understand the authoring the `docker-compose.yaml` you should start with the [Hello, docker-compose sample](https://github.com/dapr/samples/tree/master/hello-docker-compose). - -Similar to running locally with `dapr run` for each service you need to choose a unique app-id. Choosing the container name as the app-id will make this simple to remember. - -The compose file will contain at a minimum: - -- A network that the containers use to communicate -- Each service's container -- A `-daprd` sidecar container with the service's port and app-id specified -- Additional dependencies that run in containers (redis for example) -- optional: Dapr placement container (for actors) - -You can also view a larger example from the [eShopOnContainers](https://github.com/dotnet-architecture/eShopOnDapr/blob/master/docker-compose.yml) sample application. diff --git a/daprdocs/content/en/dotnet-sdk-docs/dotnet-jobs/_index.md b/daprdocs/content/en/dotnet-sdk-docs/dotnet-jobs/_index.md deleted file mode 100644 index d4f4f52be..000000000 --- a/daprdocs/content/en/dotnet-sdk-docs/dotnet-jobs/_index.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -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. diff --git a/daprdocs/content/en/dotnet-sdk-docs/dotnet-jobs/dotnet-jobs-howto.md b/daprdocs/content/en/dotnet-sdk-docs/dotnet-jobs/dotnet-jobs-howto.md deleted file mode 100644 index 1187979f4..000000000 --- a/daprdocs/content/en/dotnet-sdk-docs/dotnet-jobs/dotnet-jobs-howto.md +++ /dev/null @@ -1,419 +0,0 @@ ---- -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 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 - -## 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(); -builder.Services.AddDaprJobsClient((serviceProvider, daprJobsClientBuilder) => -{ - var secretRetriever = serviceProvider.GetRequiredService(); - 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 { - { "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` 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 jobPayload) => { - var logger = serviceProvider.GetService(); - 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 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 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. The following -shows the different arguments available when scheduling a job: - -| Argument Name | Type | Description | Required | -|---|---|---|---| -| jobName | string | The name of the job being scheduled. | Yes | -| schedule | DaprJobSchedule | The schedule defining when the job will be triggered. | Yes | -| payload | ReadOnlyMemory | Job data provided to the invocation endpoint when triggered. | No | -| 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 | -| overwrite | bool | A flag indicating whether an existing job should be overwritten when submitted or false to require that an existing job with the same name be deleted first. | No | -| cancellationToken | CancellationToken | Used to cancel out of the operation early, e.g. because of an operation timeout. | No | - -### `DaprJobSchedule` -All jobs are scheduled via the SDK using the `DaprJobSchedule` which creates an expression passed to the -runtime to schedule jobs. There are several static methods exposed on the `DaprJobSchedule` used to faciliate -easy registration of each of the kinds of job schedules available as follows. This separates specifying -the job schedule itself from any additional options like repeating the operation or providing a cancellation token. - -### 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. - -`DaprJobSchedule.FromDateTime(DateTimeOffset scheduledTime)` - -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 = DateTimeOffset.UtcNow; - var threeDaysFromNow = today.AddDays(3); - - var schedule = DaprJobSchedule.FromDateTime(threeDaysFromNow); - await daprJobsClient.ScheduleJobAsync("job", schedule, 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. - -`DaprJobSchedule.FromDuration(TimeSpan interval)` - -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 - var schedule = DaprJobSchedule.FromDuration(hourlyInterval); - await daprJobsClient.ScheduleJobAsync("job", schedule, 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. - -`DaprJobSchedule.FromCronExpression(string cronExpression)` - -There are two different approaches supported to scheduling a Cron-based job in the Dapr SDK. - -#### Provide your own Cron expression -You can just provide your own Cron expression via a string via `DaprJobSchedule.FromExpression()`: - -```csharp -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 * *"; - var schedule = DaprJobSchedule.FromExpression(cronSchedule); - - //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); - - await daprJobsClient.ScheduleJobAsync("myJobName", ) - await daprJobsClient.ScheduleCronJobAsync("myJobName", schedule, dueTime: firstOfNextMonth, cancellationToken: cancellationToken); - } -} -``` - -#### Use the `CronExpressionBuilder` -Alternatively, you can use our fluent builder to produce a valid Cron expression: - -```csharp -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 - var cronExpression = new CronExpressionBuilder() - .Every(EveryCronPeriod.Hour, 2) - .On(OnCronPeriod.DayOfMonth, 5) - .ToString(); - var schedule = DaprJobSchedule.FromExpression(cronExpression); - - //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); - - await daprJobsClient.ScheduleJobAsync("myJobName", ) - await daprJobsClient.ScheduleCronJobAsync("myJobName", schedule, 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 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); - } -} -``` \ No newline at end of file diff --git a/daprdocs/content/en/dotnet-sdk-docs/dotnet-jobs/dotnet-jobsclient-usage.md b/daprdocs/content/en/dotnet-sdk-docs/dotnet-jobs/dotnet-jobsclient-usage.md deleted file mode 100644 index ddbf226ee..000000000 --- a/daprdocs/content/en/dotnet-sdk-docs/dotnet-jobs/dotnet-jobsclient-usage.md +++ /dev/null @@ -1,197 +0,0 @@ ---- -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(); - -builder.Services.AddDaprJobsClient((serviceProvider, daprJobsClientBuilder) => { - //Retrieve an instance of the `SecretService` from the service provider - var secretService = serviceProvider.GetRequiredService(); - 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` 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` that is populated with the bytes originally provided during the job registration. - -Because the payload is stored as a `ReadOnlyMemory`, 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. diff --git a/daprdocs/content/en/dotnet-sdk-docs/dotnet-messaging/_index.md b/daprdocs/content/en/dotnet-sdk-docs/dotnet-messaging/_index.md deleted file mode 100644 index 9927145d7..000000000 --- a/daprdocs/content/en/dotnet-sdk-docs/dotnet-messaging/_index.md +++ /dev/null @@ -1,17 +0,0 @@ ---- -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]({{% ref "dotnet-messaging-pubsub-howto.md#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. diff --git a/daprdocs/content/en/dotnet-sdk-docs/dotnet-messaging/dotnet-messaging-pubsub-howto.md b/daprdocs/content/en/dotnet-sdk-docs/dotnet-messaging/dotnet-messaging-pubsub-howto.md deleted file mode 100644 index 5b748b61c..000000000 --- a/daprdocs/content/en/dotnet-sdk-docs/dotnet-messaging/dotnet-messaging-pubsub-howto.md +++ /dev/null @@ -1,262 +0,0 @@ ---- -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 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 - -## 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(); -builder.Services.AddDaprPubSubClient((serviceProvider, daprPubSubClientBuilder) => { - var secretRetriever = serviceProvider.GetRequiredService(); - 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 { - { "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` 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 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(); - -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. \ No newline at end of file diff --git a/daprdocs/content/en/dotnet-sdk-docs/dotnet-messaging/dotnet-messaging-pubsub-usage.md b/daprdocs/content/en/dotnet-sdk-docs/dotnet-messaging/dotnet-messaging-pubsub-usage.md deleted file mode 100644 index 8b3359d0c..000000000 --- a/daprdocs/content/en/dotnet-sdk-docs/dotnet-messaging/dotnet-messaging-pubsub-usage.md +++ /dev/null @@ -1,130 +0,0 @@ ---- -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(); - -builder.Services.AddDaprPublishSubscribeClient((serviceProvider, daprPubSubClientBuilder) => { - //Retrieve an instance of the `SecretService` from the service provider - var secretService = serviceProvider.GetRequiredService(); - var daprApiToken = secretService.GetSecret("DaprApiToken").Value; - - //Configure the `DaprPublishSubscribeClientBuilder` - daprPubSubClientBuilder.UseDaprApiToken(daprApiToken); -}); - -var app = builder.Build(); -``` \ No newline at end of file diff --git a/daprdocs/content/en/dotnet-sdk-docs/dotnet-troubleshooting/_index.md b/daprdocs/content/en/dotnet-sdk-docs/dotnet-troubleshooting/_index.md deleted file mode 100644 index 5ffd7c7c6..000000000 --- a/daprdocs/content/en/dotnet-sdk-docs/dotnet-troubleshooting/_index.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -type: docs -title: "How to troubleshoot and debug with the Dapr .NET SDK" -linkTitle: "Troubleshooting" -weight: 120000 -description: Tips, tricks, and guides for troubleshooting and debugging with the Dapr .NET SDKs ---- \ No newline at end of file diff --git a/daprdocs/content/en/dotnet-sdk-docs/dotnet-troubleshooting/dotnet-troubleshooting-pubsub.md b/daprdocs/content/en/dotnet-sdk-docs/dotnet-troubleshooting/dotnet-troubleshooting-pubsub.md deleted file mode 100644 index 12fb2be00..000000000 --- a/daprdocs/content/en/dotnet-sdk-docs/dotnet-troubleshooting/dotnet-troubleshooting-pubsub.md +++ /dev/null @@ -1,284 +0,0 @@ ---- -type: docs -title: "Troubleshoot Pub/Sub with the .NET SDK" -linkTitle: "Troubleshoot pub/sub" -weight: 100000 -description: Troubleshoot Pub/Sub with the .NET SDK ---- - -# Troubleshooting Pub/Sub - -The most common problem with pub/sub is that the pub/sub endpoint in your application is not being called. - -There are a few layers to this problem with different solutions: - -- The application is not receiving any traffic from Dapr -- The application is not registering pub/sub endpoints with Dapr -- The pub/sub endpoints are registered with Dapr, but the request is not reaching the desired endpoint - -## Step 1: Turn up the logs - -**This is important. Future steps will depend on your ability to see logging output. ASP.NET Core logs almost nothing with the default log settings, so you will need to change it.** - -Adjust the logging verbosity to include `Information` logging for ASP.NET Core as described [here](https://docs.microsoft.com/en-us/aspnet/core/mvc/controllers/routing?view=aspnetcore-5.0#debug-diagnostics). Set the `Microsoft` key to `Information`. - -## Step 2: Verify you can receive traffic from Dapr - -1. Start the application as you would normally (`dapr run ...`). Make sure that you're including an `--app-port` argument in the commandline. Dapr needs to know that your application is listening for traffic. By default an ASP.NET Core application will listen for HTTP on port 5000 in local development. - -2. Wait for Dapr to finish starting - -3. Examine the logs - -You should see a log entry like: - -``` -info: Microsoft.AspNetCore.Hosting.Diagnostics[1] - Request starting HTTP/1.1 GET http://localhost:5000/..... -``` - -During initialization Dapr will make some requests to your application for configuration. If you can't find these then it means that something has gone wrong. Please ask for help either via an issue or in Discord (include the logs). If you see requests made to your application, then continue to step 3. - -## Step 3: Verify endpoint registration - -1. Start the application as you would normally (`dapr run ...`). - -2. Use `curl` at the command line (or another HTTP testing tool) to access the `/dapr/subscribe` endpoint. - -Here's an example command assuming your application's listening port is 5000: - -```sh -curl http://localhost:5000/dapr/subscribe -v -``` - -For a correctly configured application the output should look like the following: - -```txt -* Trying ::1... -* TCP_NODELAY set -* Connected to localhost (::1) port 5000 (#0) -> GET /dapr/subscribe HTTP/1.1 -> Host: localhost:5000 -> User-Agent: curl/7.64.1 -> Accept: */* -> -< HTTP/1.1 200 OK -< Date: Fri, 15 Jan 2021 22:31:40 GMT -< Content-Type: application/json -< Server: Kestrel -< Transfer-Encoding: chunked -< -* Connection #0 to host localhost left intact -[{"topic":"deposit","route":"deposit","pubsubName":"pubsub"},{"topic":"withdraw","route":"withdraw","pubsubName":"pubsub"}]* Closing connection 0 -``` - -Pay particular attention to the HTTP status code, and the JSON output. - -```txt -< HTTP/1.1 200 OK -``` - -A 200 status code indicates success. - - -The JSON blob that's included near the end is the output of `/dapr/subscribe` that's processed by the Dapr runtime. In this case it's using the `ControllerSample` in this repo - so this is an example of correct output. - -```json -[ - {"topic":"deposit","route":"deposit","pubsubName":"pubsub"}, - {"topic":"withdraw","route":"withdraw","pubsubName":"pubsub"} -] -``` - ---- - -With the output of this command in hand, you are ready to diagnose a problem or move on to the next step. - -### Option 0: The response was a 200 included some pub/sub entries - -**If you have entries in the JSON output from this test then the problem lies elsewhere, move on to step 2.** - -### Option 1: The response was not a 200, or didn't contain JSON - -If the response was not a 200 or did not contain JSON, then the `MapSubscribeHandler()` endpoint was not reached. - -Make sure you have some code like the following in `Startup.cs` and repeat the test. - -```cs -app.UseRouting(); - -app.UseCloudEvents(); - -app.UseEndpoints(endpoints => -{ - endpoints.MapSubscribeHandler(); // This is the Dapr subscribe handler - endpoints.MapControllers(); -}); -``` - -**If adding the subscribe handler did not resolve the problem, please open an issue on this repo and include the contents of your `Startup.cs` file.** - -### Option 2: The response contained JSON but it was empty (like `[]`) - -If the JSON output was an empty array (like `[]`) then the subscribe handler is registered, but no topic endpoints were registered. - ---- - -If you're using a controller for pub/sub you should have a method like: - -```C# -[Topic("pubsub", "deposit")] -[HttpPost("deposit")] -public async Task Deposit(...) - -// Using Pub/Sub routing -[Topic("pubsub", "transactions", "event.type == \"withdraw.v2\"", 1)] -[HttpPost("withdraw")] -public async Task Withdraw(...) -``` - -In this example the `Topic` and `HttpPost` attributes are required, but other details might be different. - ---- - -If you're using routing for pub/sub you should have an endpoint like: - -```C# -endpoints.MapPost("deposit", ...).WithTopic("pubsub", "deposit"); -``` - -In this example the call to `WithTopic(...)` is required but other details might be different. - ---- - -**After correcting this code and re-testing if the JSON output is still the empty array (like `[]`) then please open an issue on this repository and include the contents of `Startup.cs` and your pub/sub endpoint.** - -## Step 4: Verify endpoint reachability - -In this step we'll verify that the entries registered with pub/sub are reachable. The last step should have left you with some JSON output like the following: - -```json -[ - { - "pubsubName": "pubsub", - "topic": "deposit", - "route": "deposit" - }, - { - "pubsubName": "pubsub", - "topic": "deposit", - "routes": { - "rules": [ - { - "match": "event.type == \"withdraw.v2\"", - "path": "withdraw" - } - ] - } - } -] -``` - -Keep this output, as we'll use the `route` information to test the application. - -1. Start the application as you would normally (`dapr run ...`). - -2. Use `curl` at the command line (or another HTTP testing tool) to access one of the routes registered with a pub/sub endpoint. - -Here's an example command assuming your application's listening port is 5000, and one of your pub/sub routes is `withdraw`: - -```sh -curl http://localhost:5000/withdraw -H 'Content-Type: application/json' -d '{}' -v -``` - -Here's the output from running the above command against the sample: - -```txt -* Trying ::1... -* TCP_NODELAY set -* Connected to localhost (::1) port 5000 (#0) -> POST /withdraw HTTP/1.1 -> Host: localhost:5000 -> User-Agent: curl/7.64.1 -> Accept: */* -> Content-Type: application/json -> Content-Length: 2 -> -* upload completely sent off: 2 out of 2 bytes -< HTTP/1.1 400 Bad Request -< Date: Fri, 15 Jan 2021 22:53:27 GMT -< Content-Type: application/problem+json; charset=utf-8 -< Server: Kestrel -< Transfer-Encoding: chunked -< -* Connection #0 to host localhost left intact -{"type":"https://tools.ietf.org/html/rfc7231#section-6.5.1","title":"One or more validation errors occurred.","status":400,"traceId":"|5e9d7eee-4ea66b1e144ce9bb.","errors":{"Id":["The Id field is required."]}}* Closing connection 0 -``` - -Based on the HTTP 400 and JSON payload, this response indicates that the endpoint was reached but the request was rejected due to a validation error. - -You should also look at the console output of the running application. This is example output with the Dapr logging headers stripped away for clarity. - -``` -info: Microsoft.AspNetCore.Hosting.Diagnostics[1] - Request starting HTTP/1.1 POST http://localhost:5000/withdraw application/json 2 -info: Microsoft.AspNetCore.Routing.EndpointMiddleware[0] - Executing endpoint 'ControllerSample.Controllers.SampleController.Withdraw (ControllerSample)' -info: Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker[3] - Route matched with {action = "Withdraw", controller = "Sample"}. Executing controller action with signature System.Threading.Tasks.Task`1[Microsoft.AspNetCore.Mvc.ActionResult`1[ControllerSample.Account]] Withdraw(ControllerSample.Transaction, Dapr.Client.DaprClient) on controller ControllerSample.Controllers.SampleController (ControllerSample). -info: Microsoft.AspNetCore.Mvc.Infrastructure.ObjectResultExecutor[1] - Executing ObjectResult, writing value of type 'Microsoft.AspNetCore.Mvc.ValidationProblemDetails'. -info: Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker[2] - Executed action ControllerSample.Controllers.SampleController.Withdraw (ControllerSample) in 52.1211ms -info: Microsoft.AspNetCore.Routing.EndpointMiddleware[1] - Executed endpoint 'ControllerSample.Controllers.SampleController.Withdraw (ControllerSample)' -info: Microsoft.AspNetCore.Hosting.Diagnostics[2] - Request finished in 157.056ms 400 application/problem+json; charset=utf-8 -``` - -The log entry of primary interest is the one coming from routing: - -```txt -info: Microsoft.AspNetCore.Routing.EndpointMiddleware[0] - Executing endpoint 'ControllerSample.Controllers.SampleController.Withdraw (ControllerSample)' -``` - -This entry shows that: - -- Routing executed -- Routing chose the `ControllerSample.Controllers.SampleController.Withdraw (ControllerSample)'` endpoint - -Now you have the information needed to troubleshoot this step. - -### Option 0: Routing chose the correct endpoint - -If the information in the routing log entry is correct, then it means that in isolation your application is behaving correctly. - -Example: - -```txt -info: Microsoft.AspNetCore.Routing.EndpointMiddleware[0] - Executing endpoint 'ControllerSample.Controllers.SampleController.Withdraw (ControllerSample)' -``` - -You might want to try using the Dapr cli to execute send a pub/sub message directly and compare the logging output. - -Example command: - -```sh -dapr publish --pubsub pubsub --topic withdraw --data '{}' -``` - -**If after doing this you still don't understand the problem please open an issue on this repo and include the contents of your `Startup.cs`.** - -### Option 1: Routing did not execute - -If you don't see an entry for `Microsoft.AspNetCore.Routing.EndpointMiddleware` in the logs, then it means that the request was handled by something other than routing. Usually the problem in this case is a misbehaving middleware. Other logs from the request might give you a clue to what's happening. - -**If you need help understanding the problem please open an issue on this repo and include the contents of your `Startup.cs`.** - -### Option 2: Routing chose the wrong endpoint - -If you see an entry for `Microsoft.AspNetCore.Routing.EndpointMiddleware` in the logs, but it contains the wrong endpoint then it means that you've got a routing conflict. The endpoint that was chosen will appear in the logs so that should give you an idea of what's causing the conflict. - -**If you need help understanding the problem please open an issue on this repo and include the contents of your `Startup.cs`.** diff --git a/daprdocs/content/en/dotnet-sdk-docs/dotnet-workflow/_index.md b/daprdocs/content/en/dotnet-sdk-docs/dotnet-workflow/_index.md deleted file mode 100644 index df8244ac3..000000000 --- a/daprdocs/content/en/dotnet-sdk-docs/dotnet-workflow/_index.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -type: docs -title: "Dapr Workflow .NET SDK" -linkTitle: "Workflow" -weight: 40000 -description: Get up and running with Dapr Workflow and the Dapr .NET SDK ---- - diff --git a/daprdocs/content/en/dotnet-sdk-docs/dotnet-workflow/dotnet-workflow-howto.md b/daprdocs/content/en/dotnet-sdk-docs/dotnet-workflow/dotnet-workflow-howto.md deleted file mode 100644 index d38652ddc..000000000 --- a/daprdocs/content/en/dotnet-sdk-docs/dotnet-workflow/dotnet-workflow-howto.md +++ /dev/null @@ -1,172 +0,0 @@ ---- -type: docs -title: "How to: Author and manage Dapr Workflow in the .NET SDK" -linkTitle: "How to: Author & manage workflows" -weight: 100000 -description: Learn how to author and manage Dapr Workflow using the .NET SDK ---- - -Let's create a Dapr workflow and invoke it using the console. In the [provided order processing workflow example](https://github.com/dapr/dotnet-sdk/tree/master/examples/Workflow), the console prompts provide directions on how to both purchase and restock items. In this guide, you will: - -- Deploy a .NET console application ([WorkflowConsoleApp](https://github.com/dapr/dotnet-sdk/tree/master/examples/Workflow/WorkflowConsoleApp)). -- Utilize the .NET workflow SDK and API calls to start and query workflow instances. - -In the .NET example project: -- The main [`Program.cs`](https://github.com/dapr/dotnet-sdk/blob/master/examples/Workflow/WorkflowConsoleApp/Program.cs) file contains the setup of the app, including the registration of the workflow and workflow activities. -- The workflow definition is found in the [`Workflows` directory](https://github.com/dapr/dotnet-sdk/tree/master/examples/Workflow/WorkflowConsoleApp/Workflows). -- The workflow activity definitions are found in the [`Activities` directory](https://github.com/dapr/dotnet-sdk/tree/master/examples/Workflow/WorkflowConsoleApp/Activities). - -## Prerequisites - -- [Dapr CLI](https://docs.dapr.io/getting-started/install-dapr-cli/) -- [Initialized Dapr environment](https://docs.dapr.io/getting-started/install-dapr-selfhost/) -- [.NET 8](https://dotnet.microsoft.com/download/dotnet/8.0) or [.NET 9](https://dotnet.microsoft.com/download/dotnet/9.0) installed - -## 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 Workflow example. - -```sh -cd examples/Workflow -``` - -## Run the application locally - -To run the Dapr application, you need to start the .NET program and a Dapr sidecar. Navigate to the `WorkflowConsoleApp` directory. - -```sh -cd WorkflowConsoleApp -``` - -Start the program. - -```sh -dotnet run -``` - -In a new terminal, navigate again to the `WorkflowConsoleApp` directory and run the Dapr sidecar alongside the program. - -```sh -dapr run --app-id wfapp --dapr-grpc-port 4001 --dapr-http-port 3500 -``` - -> Dapr listens for HTTP requests at `http://localhost:3500` and internal workflow gRPC requests at `http://localhost:4001`. - -## Start a workflow - -To start a workflow, you have two options: - -1. Follow the directions from the console prompts. -1. Use the workflow API and send a request to Dapr directly. - -This guide focuses on the workflow API option. - -{{% alert title="Note" color="primary" %}} - - You can find the commands below in the `WorkflowConsoleApp`/`demo.http` file. - - The body of the curl request is the purchase order information used as the input of the workflow. - - The "12345678" in the commands represents the unique identifier for the workflow and can be replaced with any identifier of your choosing. -{{% /alert %}} - - -Run the following command to start a workflow. - -{{< tabpane text=true >}} - -{{% tab header="Linux/MacOS" %}} - -```bash -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}' -``` - -{{% /tab %}} - -{{% tab header="Windows" %}} - -```powershell -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}' -``` - -{{% /tab %}} - -{{< /tabpane >}} - -If successful, you should see a response like the following: - -```json -{"instanceID":"12345678"} -``` - -Send an HTTP request to get the status of the workflow that was started: - -```bash -curl -i -X GET http://localhost:3500/v1.0/workflows/dapr/12345678 -``` - -The workflow is designed to take several seconds to complete. If the workflow hasn't completed when you issue the HTTP request, you'll see the following JSON response (formatted for readability) with workflow status as `RUNNING`: - -```json -{ - "instanceID": "12345678", - "workflowName": "OrderProcessingWorkflow", - "createdAt": "2023-05-10T00:42:03.911444105Z", - "lastUpdatedAt": "2023-05-10T00:42:06.142214153Z", - "runtimeStatus": "RUNNING", - "properties": { - "dapr.workflow.custom_status": "", - "dapr.workflow.input": "{\"Name\": \"Paperclips\", \"TotalCost\": 99.95, \"Quantity\": 1}" - } -} -``` - -Once the workflow has completed running, you should see the following output, indicating that it has reached the `COMPLETED` status: - -```json -{ - "instanceID": "12345678", - "workflowName": "OrderProcessingWorkflow", - "createdAt": "2023-05-10T00:42:03.911444105Z", - "lastUpdatedAt": "2023-05-10T00:42:18.527704176Z", - "runtimeStatus": "COMPLETED", - "properties": { - "dapr.workflow.custom_status": "", - "dapr.workflow.input": "{\"Name\": \"Paperclips\", \"TotalCost\": 99.95, \"Quantity\": 1}", - "dapr.workflow.output": "{\"Processed\":true}" - } -} -``` - -When the workflow has completed, the stdout of the workflow app should look like: - -```log -info: WorkflowConsoleApp.Activities.NotifyActivity[0] - Received order 12345678 for Paperclips at $99.95 -info: WorkflowConsoleApp.Activities.ReserveInventoryActivity[0] - Reserving inventory: 12345678, Paperclips, 1 -info: WorkflowConsoleApp.Activities.ProcessPaymentActivity[0] - Processing payment: 12345678, 99.95, USD -info: WorkflowConsoleApp.Activities.NotifyActivity[0] - Order 12345678 processed successfully! -``` - -If you have Zipkin configured for Dapr locally on your machine, then you can view the workflow trace spans in the Zipkin web UI (typically at http://localhost:9411/zipkin/). - -## Demo - -Watch this video [demonstrating .NET Workflow](https://youtu.be/BxiKpEmchgQ?t=2557): - - - -## Next steps - -- [Try the Dapr Workflow quickstart]({{% ref workflow-quickstart.md %}}) -- [Learn more about Dapr Workflow]({{% ref workflow-overview.md %}}) diff --git a/daprdocs/content/en/dotnet-sdk-docs/dotnet-workflow/dotnet-workflowclient-usage.md b/daprdocs/content/en/dotnet-sdk-docs/dotnet-workflow/dotnet-workflowclient-usage.md deleted file mode 100644 index a376e6acb..000000000 --- a/daprdocs/content/en/dotnet-sdk-docs/dotnet-workflow/dotnet-workflowclient-usage.md +++ /dev/null @@ -1,137 +0,0 @@ ---- -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(); - options.RegisterActivity(); -}); - -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(); - options.RegisterActivity(); -}, 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(); - options.RegisterActivity(); -}, 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 -{ - private readonly ILogger _logger; - - public MyActivity(ILogger logger) - { - this._logger = logger; - } - - public override Task 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 -{ - public override async Task RunAsync(WorkflowContext context, OrderPayload order) - { - string orderId = context.InstanceId; - var logger = context.CreateReplaySafeLogger(); //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); - - //... - } -} -``` - - - \ No newline at end of file