Improve actor getting started and add more docs (#623)

* Improve actor getting started and add more docs

Fixes: #546
Fixes: #612

This change runs through the getting started docs and improves trims all
of the unnecessary fat. I've reorganized all of the details that aren't
relevant as part of the first tutorial into separate docs.

I've also fleshed out all of the content we have to be relevant to a new
user and added new sections like DI and logging.

* Update daprdocs/content/en/dotnet-sdk-docs/dotnet-actors/dotnet-actors-howto.md

Co-authored-by: Aaron Crawfis <Aaron.Crawfis@microsoft.com>

* Update daprdocs/content/en/dotnet-sdk-docs/dotnet-actors/dotnet-actors-howto.md

Co-authored-by: Aaron Crawfis <Aaron.Crawfis@microsoft.com>

* Update daprdocs/content/en/dotnet-sdk-docs/dotnet-actors/dotnet-actors-howto.md

Co-authored-by: Aaron Crawfis <Aaron.Crawfis@microsoft.com>

* Update daprdocs/content/en/dotnet-sdk-docs/dotnet-actors/dotnet-actors-howto.md

Co-authored-by: Aaron Crawfis <Aaron.Crawfis@microsoft.com>

Co-authored-by: Aaron Crawfis <Aaron.Crawfis@microsoft.com>
This commit is contained in:
Ryan Nowak 2021-03-15 13:38:27 -07:00 committed by GitHub
parent b62612af3d
commit 4baad5453f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 456 additions and 156 deletions

View File

@ -8,3 +8,4 @@ description: How to get up and running with the Dapr .NET SDK
The Dapr actor package allows you to interact with Dapr virtual actors from a .NET application.
See [How to use Dapr actors]({{ ref dotnet-actors-howto.md }}) for getting started instructions.

View File

@ -0,0 +1,94 @@
---
type: docs
title: "Dapr actor .NET usage guide"
linkTitle: "Actors Usage"
weight: 10000
description: Learn all about using the actor client with the .NET SDK
---
## Using the IActorProxyFactory
Inside of an `Actor` class or otherwisde inside of an ASP.NET Core project you should use the `IActorProxyFactory` interface 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<MyData> GetDataAsync()
{
var proxy = this.ProxyFactory.CreateActorProxy<IOtherActor>(ActorId.CreateRandom(), "OtherActor");
await proxy.DoSomethingGreat();
return this.StateManager.GetStateAsync<MyData>("my_data");
}
```
> 💡 For a non-dependency-injected application you can use the static methods on `ActorProxy`. These methods are error prone when you need to configure custom settings, and should be avoided when possible.
The guidance in this document will focus on `IActorProxyFactory`. `ActorProxy`'s static method functionality is identical except for the ability to manage configuration centrally.
## Identifying an actor
In order to communicate with an actor, you will need to know its type and id, and for a strongly-typed client one of its interfaces. All of the APIs on `IActorProxyFactory` will require an actor type and actor id.
- The actor type uniquely identifies the actor implementation across the whole application.
- The actor id uniquely identifies an instance of that type.
If you do not have an actor id and want to communicate with a new instance, you can use `ActorId.CreateRandom()` to create a randomized id. 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: *strongly-typed* clients that use .NET interfaces and *weakly-typed* clients that use the `ActorProxy` class.
Since *strongly-typed* clients are based on .NET interfaces provide the typical benefits of strong-typing, however they do not work with non-.NET actors. You should use the *weakly-typed* client only when required for interop or other advanced reasons.
### Using a strongly-typed client
Use the `CreateActorProxy<>` method to create a strongly-typed client like the following example. `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<IOtherActor>(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
Use the `Create` method to create a weakly-typed client like the following example. `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 a request message and 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<MyRequest, MyResponse>("DoSomethingGreat", request);
```
When using a weakly-typed proxy, it is your responsbility to define the correct actor method names and message types. This is done for you when using a strongly-typed proxy since the names and types are part of the interface definition.

View File

@ -1,11 +1,13 @@
---
type: docs
title: "Getting started with the Dapr actor .NET SDK"
linkTitle: "Example"
title: "How to use Dapr virtual actors with the .NET SDK"
linkTitle: "Actors"
weight: 100000
description: Try out .NET virtual actors with this example
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.
## Prerequisites
- [Dapr CLI]({{< ref install-dapr-cli.md >}}) installed
@ -14,7 +16,7 @@ description: Try out .NET virtual actors with this example
## Overview
This document describes how to create an Actor(`MyActor`) and invoke its methods on the client application.
This document describes how to create an Actor (`MyActor`) and invoke its methods on the client application.
```
MyActor --- MyActor.Interfaces
@ -30,8 +32,11 @@ MyActor --- MyActor.Interfaces
* **The actor client project(\MyActor\MyActorClient)** This project contains the implementation of the actor client which calls MyActor's method defined in Actor Interfaces.
## Step 0: Prepare
## STEP 1 - Create Actor Interface
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.
@ -41,7 +46,7 @@ Actor interface is defined with the below requirements:
* The return type of Actor method must be `Task` or `Task<object>`
* Actor method can have one argument at a maximum
### Create project and add dependencies
### Create interface project and add dependencies
```bash
# Create Actor Interfaces
@ -50,13 +55,14 @@ 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 -v 1.0.0-rc02
dotnet add package Dapr.Actors -v 1.0.0
cd ..
```
### Implement IMyActor interface
### Implement IMyActor Interface
Define IMyActor Interface and MyData data object.
Define `IMyActor` interface and `MyData` data object. Paste the following code into `MyActor.cs` in the `MyActor.Interfaces` project.
```csharp
using Dapr.Actors;
@ -89,32 +95,33 @@ namespace MyActor.Interfaces
}
```
## STEP 2 - Create Actor Service
## 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 project and add dependencies
### Create actor service project and add dependencies
```bash
# Create ASP.Net Web service to host Dapr actor
dotnet new webapi -o MyActorService
dotnet new web -o MyActorService
cd MyActorService
# Add Dapr.Actors nuget package. Please use the latest package version from nuget.org
dotnet add package Dapr.Actors -v 1.0.0-rc02
# Add Dapr.Actors.AspNetCore nuget package. Please use the latest package version from nuget.org
dotnet add package Dapr.Actors.AspNetCore -v 1.0.0-rc02
dotnet add package Dapr.Actors.AspNetCore -v 1.0.0
# Add Actor Interface reference
dotnet add reference ../MyActor.Interfaces/MyActor.Interfaces.csproj
cd ..
```
### Add Actor implementation
### 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;
@ -249,19 +256,7 @@ namespace MyActorService
}
```
#### 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. 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
{
// ...
}
```
### Register Actor runtime with ASP.NET Core startup
### Register actor runtime with ASP.NET Core startup
The Actor runtime is configured through ASP.NET Core `Startup.cs`.
@ -269,20 +264,25 @@ The runtime uses the ASP.NET Core dependency injection system to register actor
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
// In Startup.cs
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)
{
// Register actor runtime with DI
services.AddActors(options =>
{
// Register actor types and configure actor settings
options.Actors.RegisterActor<MyActor>();
});
// Register additional services for use with actors
services.AddSingleton<BankService>();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
@ -291,10 +291,8 @@ Actors are implemented via HTTP calls with the Dapr runtime. This functionality
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseRouting();
@ -304,41 +302,15 @@ Actors are implemented via HTTP calls with the Dapr runtime. This functionality
endpoints.MapActorsHandlers();
});
}
}
}
```
### **Optional** - Override Default Actor Settings
Actor Settings are per app. The settings described [here](https://docs.dapr.io/reference/api/actors_api/) are available on the options and can be modified as below.
The following code extends the previous section to do this. Please note the values below are an **example** only.
```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<MyActor>();
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<BankService>();
}
```
## STEP 3 - Add a client
## 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 project and add dependencies
### Create actor client project and add dependencies
```bash
# Create Actor's Client
@ -347,129 +319,130 @@ 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 -v 1.0.0-rc02
dotnet add package Dapr.Actors -v 1.0.0
# Add Actor Interface reference
dotnet add reference ../MyActor.Interfaces/MyActor.Interfaces.csproj
cd ..
```
### Invoke Actor method with Actor Service Remoting
### Invoke actor methods with strongly-typed client
We recommend to use the local proxy to actor instance because `ActorProxy.Create<IMyActor>(actorID, actorType)` returns strongly-typed actor instance to set up the remote procedure call.
You can use `ActorProxy.Create<IMyActor>(..)` 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
{
using Dapr.Actors;
using Dapr.Actors.Client;
using MyActor.Interfaces;
using System;
using System.Threading.Tasks;
...
static async Task InvokeActorMethodWithRemotingAsync()
class Program
{
static async Task MainAsync(string[] args)
{
var actorType = "MyActor"; // Registered Actor Type in Actor Service
var actorID = new ActorId("1");
Console.WriteLine("Startup up...");
// Create the local proxy by using the same interface that the service implements
// By using this proxy, you can call strongly typed methods on the interface using Remoting.
var proxy = ActorProxy.Create<IMyActor>(actorID, actorType);
// 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<IMyActor>(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(response);
Console.WriteLine($"Got response: {response}");
Console.WriteLine($"Calling GetDataAsync on {actorType}:{actorId}...");
var savedData = await proxy.GetDataAsync();
Console.WriteLine(savedData);
Console.WriteLine($"Got response: {response}");
}
...
}
}
```
### Invoke Actor method without Actor Service Remoting
You can invoke Actor methods without remoting (directly over http or using helper methods provided in ActorProxy), if Actor method accepts at-most one argument. Actor runtime will deserialize the incoming request body from client and use it as method argument to invoke the actor method.
When making non-remoting calls Actor method arguments and return types are serialized, deserialized as JSON.
## Running the code
`ActorProxy.Create(actorID, actorType)` returns ActorProxy instance and allow to use the raw http client to invoke the method defined in `IMyActor`.
The projects that you've created can now to test the sample.
```csharp
namespace MyActorClient
{
using Dapr.Actors;
using Dapr.Actors.Client;
using MyActor.Interfaces;
using System;
using System.Threading.Tasks;
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
...
static async Task InvokeActorMethodWithoutRemotingAsync()
{
var actorType = "MyActor";
var actorID = new ActorId("1");
Updating metadata for app command: dotnet run
✅ You're up and running! Both Dapr and your app logs will appear here.
// Create Actor Proxy instance to invoke the methods defined in the interface
var proxy = ActorProxy.Create(actorID, actorType);
// Need to specify the method name and response type explicitly
var response = await proxy.InvokeMethodAsync<MyData, string>("SetDataAsync", new MyData()
{
PropertyA = "ValueA",
PropertyB = "ValueB",
});
Console.WriteLine(response);
== APP == info: Microsoft.Hosting.Lifetime[0]
var savedData = await proxy.InvokeMethodAsync<MyData>("GetDataAsync");
Console.WriteLine(savedData);
}
...
}
```
== APP == Now listening on: https://localhost:5001
## Run Actor
== APP == info: Microsoft.Hosting.Lifetime[0]
In order to validate and debug actor service and client, we need to run actor services via Dapr CLI first.
== APP == Now listening on: http://localhost:5000
1. Run Dapr Runtime via Dapr cli
== APP == info: Microsoft.Hosting.Lifetime[0]
```bash
$ dapr run --app-id myapp --app-port 5000 --dapr-http-port 3500 dotnet run
```
== APP == Application started. Press Ctrl+C to shut down.
After executing MyActorService via Dapr runtime, make sure that application is discovered on port 5000 and actor connection is established successfully.
== APP == info: Microsoft.Hosting.Lifetime[0]
```bash
INFO[0000] starting Dapr Runtime -- version -- commit
INFO[0000] log level set to: info
INFO[0000] standalone mode configured
INFO[0000] dapr id: myapp
INFO[0000] loaded component statestore (state.redis)
INFO[0000] application protocol: http. waiting on port 5000
INFO[0000] application discovered on port 5000
INFO[0000] application configuration loaded
2019/08/27 14:42:06 redis: connecting to localhost:6379
2019/08/27 14:42:06 redis: connected to localhost:6379 (localAddr: [::1]:53155, remAddr: [::1]:6379)
INFO[0000] actor runtime started. actor idle timeout: 1h0m0s. actor scan interval: 30s
INFO[0000] actors: starting connection attempt to placement service at localhost:50005
INFO[0000] http server is running on port 3500
INFO[0000] gRPC server is running on port 50001
INFO[0000] dapr initialized. Status: Running. Init Elapsed 19.699438ms
INFO[0000] actors: established connection to placement service at localhost:50005
INFO[0000] actors: placement order received: lock
INFO[0000] actors: placement order received: update
INFO[0000] actors: placement tables updated
INFO[0000] actors: placement order received: unlock
...
```
== APP == Hosting environment: Development
== APP == info: Microsoft.Hosting.Lifetime[0]
== APP == Content root path: /Users/ryan/actortest/MyActorService
```
2. Run MyActorClient
MyActorClient will console out if it calls actor hosted in MyActorService successfully.
`MyActorClient` is acting as the client, and it can be run normally with `dotnet run`.
> If you specify the different Dapr runtime http port (default port: 3500), you need to set DAPR_HTTP_PORT environment variable before running the client.
Open a new terminal an navigate to the `MyActorClient` directory. Then run the project with:
```bash
Success
PropertyA: ValueA, PropertyB: ValueB
```
```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: Success
```
> 💡 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-actor-client.md >}})
- [.NET Dapr Actors usage guide]({{< ref dotnet-actor-usage.md >}})

View File

@ -0,0 +1,232 @@
---
type: docs
title: "Dapr actor .NET usage guide"
linkTitle: "Actors Usage"
weight: 10000
description: Learn all about using actors with the .NET SDK
---
## Authoring actors
### ActorHost
The `ActorHost` is a required constructor parameter of all actors, and must be passed to the base class constructor.
```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
{
}
}
```
The `ActorHost` is provided by the runtime and contains all of the state that the allows that actor instance to communicate with the runtime. Since the `ActorHost` contains state unique to the actor, you should not pass the instance into other parts of your code. You should not create your own instances of `ActorHost` except in tests.
### Using dependency injection
Actors support [depenendency injection](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection) of additonal parameters into the constructor. Any other parameters your 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. You can read more about the different ways of registering your types [here](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<BankService>();
}
```
Each actor instance has its own dependency injection scope. Each actor remains in memory for some time after performing an operation, and during that time the dependency injection scope associated with the actor is also considered live. The scope will be releases when the actor is deactivated.
If an actor injects an `IServiceProvider` in the constructor, the actor will recieve 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, take care to 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, it is possible to 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 is recommended that you rely on dependency injection for resource management rather than implementing dispose functionality in application code. Dispose support is provided for the rare case where it is truly necessary.
### Logging
Inside of an actor class you have access to an instance of `ILogger` 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 [here](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.
You should use *structured logging* with *named placeholders* like the example below:
```csharp
public Task<MyData> GetDataAsync()
{
this.Logger.LogInformation("Getting state at {CurrentTime}", DateTime.UtcNow);
return this.StateManager.GetStateAsync<MyData>("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/en-us/dotnet/core/extensions/logging?tabs=command-line#log-message-template) which is more performant and offers better 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 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.
## Hosting actors on the server
### Registering actors
Actor registration is part `ConfigureServices` in `Startup.cs`. The `ConfigureServices` method is where services are registered with dependency injection, and registering the set of actor types is part of the registration of actor services.
Inside `ConfigureServices` you can:
- Register the actor runtime (`UseActors`)
- 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<MyActor>();
// 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<BankService>();
}
```
### Configuring JSON options
The actor runtime uses [System.Text.Json](https://docs.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-overview) for serializing data to the state store, and for handling requests from the weakly-typed client.
By default the actor runtime uses settings based on [JsonSerializerDefaults.Web](https://docs.microsoft.com/en-us/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. Adding `MapActorsHandlers` inside the endpoint middleware is what configures actors as part of the pipline.
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 the default configuration of Dapr. Dapr will send requests over unencrypted HTTP by default, which will then be blocked by the `UseHttpsRedirection` middleware. 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();
});
}
```