How to: Run and use virtual actors in the .NET SDK
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 ().
- Invoke its methods on the client application.
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.
- installed.
- Initialized Dapr environment.
- installed. Dapr .NET SDK uses ASP.NET Core.
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.
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
orTask<object>
- Actor method can have one argument at a maximum
# 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.
using Dapr.Actors;
using System.Threading.Tasks;
namespace MyActor.Interfaces
{
public interface IMyActor : IActor
{
Task<string> SetDataAsync(MyData data);
Task<MyData> GetDataAsync();
Task RegisterReminder();
Task UnregisterReminder();
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 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:
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 inside Configure(...)
.
Paste the following code into Startup.cs
in the MyActorService
project:
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<MyActor>();
});
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
// By default, ASP.Net Core uses port 5000 for HTTP. The HTTP
// redirection will interfere with the Dapr runtime. You can
// move this out of the else block if you use port 5001 in this
// example, and developer tooling (such as the VSCode extension).
app.UseHttpsRedirection();
}
app.UseRouting();
app.UseEndpoints(endpoints =>
{
// Register actors handlers that interface with the Dapr runtime.
endpoints.MapActorsHandlers();
});
}
}
}
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
# 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 ..
Paste the following code into Program.cs
in the MyActorClient
project:
using System;
using System.Threading.Tasks;
using Dapr.Actors;
using Dapr.Actors.Client;
using MyActor.Interfaces;
namespace MyActorClient
{
{
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<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($"Got response: {response}");
Console.WriteLine($"Calling GetDataAsync on {actorType}:{actorId}...");
var savedData = await proxy.GetDataAsync();
Console.WriteLine($"Got response: {response}");
}
}
}
Running the code
The projects that you’ve created can now to test the sample.
Run MyActorService
Since
MyActorService
is hosting actors, it needs to be run with the Dapr CLI.You will see commandline output from both
daprd
andMyActorService
in this terminal. You should see something like the following, which indicates that the application started successfully....
ℹ️ 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
Run MyActorClient
MyActorClient
is acting as the client, and it can be run normally withdotnet run
.Open a new terminal an navigate to the
MyActorClient
directory. Then run the project with:dotnet run
You should see commandline output like:
Startup up...
Calling SetDataAsync on MyActor:1...
Got response: Success
Calling GetDataAsync on MyActor:1...
Got response: Success