dotnet-sdk/examples/GeneratedActor
Manuel Menegazzo cfd4fbee84
FIX: Actor source generator generates invalid code for generic interfaces (#1419)
* Handled generic actor interface

Signed-off-by: Manuel Menegazzo <manuel.menegazzo@outlook.com>

* Added more actor examples

Signed-off-by: Manuel Menegazzo <manuel.menegazzo@outlook.com>

* Updated actor namespace in example project

Signed-off-by: Manuel Menegazzo <manuel.menegazzo@outlook.com>

---------

Signed-off-by: Manuel Menegazzo <manuel.menegazzo@outlook.com>
Co-authored-by: Whit Waldo <whit.waldo@innovian.net>
2024-12-05 11:36:45 -06:00
..
ActorClient FIX: Actor source generator generates invalid code for generic interfaces (#1419) 2024-12-05 11:36:45 -06:00
ActorCommon Source generated actor clients (#1165) 2024-02-16 11:35:29 -08:00
ActorService Source generated actor clients (#1165) 2024-02-16 11:35:29 -08:00
README.md Source generated actor clients (#1165) 2024-02-16 11:35:29 -08:00

README.md

Generated Actor Client Example

An example of generating a strongly-typed actor client.

Prerequisites

Overview

Two options for invoking actor methods exist in the Dapr .NET SDK, a strongly-type (remoting) option and a loosely-typed (non-remoting) option. Each has its own advantages and disadvantages. A "middle" option also exists that combines the two and gains benefits of both without some of the disadvantages of either. Using .NET Source Generators, the Dapr .NET SDK can generate a strongly-typed client implementation that uses loosely-typed method invocation under the covers.

Strongly-typed clients are generated by:

  1. Referencing the Dapr.Actors.Generators NuGet package.

    <Project>
      <ItemGroup>
         <PackageReference Include="Dapr.Actors.Generators" Version="x.x.x" />
      </ItemGroup>
    </Project>
    
  2. Add the Dapr.Actors.Generators.GenerateActorClientAttribute to the actor interface.

    using Dapr.Actors.Generators;
    
    namespace Sample;
    
    internal sealed record SampleState(string Value);
    
    [GenerateActorClient]
    internal interface ISampleActor
    {
        [ActorMethod(Name = "GetState")]
        Task<SampleState> GetStateAsync(CancellationToken cancellationToken = default);
    
        [ActorMethod(Name = "SetState")]
        Task SetStateAsync(SampleState state, CancellationToken cancellationToken = default);
    }
    

    The Dapr.Actors.Generators.ActorMethodAttribute can be used to map interface methods definitions to specific actor methods should the names differ (e.g. the interface uses "Async" suffix common in .NET but the actor methods do not).

  3. A strongly-typed client will be generated that can be used to invoke actor methods.

    using Dapr.Actors;
    using Dapr.Actors.Client;
    using Sample;
    
    var proxy = ActorProxy.Create(ActorId.CreateRandom(), "SampleActor");
    
    using var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(30));
    
    var client = new SampleActorClient(proxy);
    
    var state = await client.GetStateAsync(cancellationTokenSource.Token);
    
    await client.SetStateAsync(new SampleState("Hello, World!"), cancellationTokenSource.Token);
    
    

Run the example

Start the ActorService

Change directory to the ActorService folder:

cd examples/GeneratedActor/ActorService

To start the ActorService, execute the following command:

dapr run --app-id generated-service --app-port 5226 -- dotnet run

Run the ActorClient

Change directory to the ActorClient folder:

cd examples/GeneratedActor/ActorClient

To run the ActorClient, execute the following command:

dapr run --app-id generated-client -- dotnet run

Expected output

You should see the following output from the ActorClient:

== APP == Testing generated client...
== APP == Done!

You should see also see the following output from the ActorService:

== APP == info: GeneratedActor.RemoteActor[0]
== APP ==       GetStateAsync called.
== APP == info: GeneratedActor.RemoteActor[0]
== APP ==       SetStateAsync called.