From 5cc8189f3bd4e5e266235dc70d232c1009a504bd Mon Sep 17 00:00:00 2001 From: Siri Varma Vegiraju Date: Mon, 23 Sep 2024 16:00:03 -0700 Subject: [PATCH 1/6] PR 123 Signed-off-by: Siri Varma Vegiraju --- src/Dapr.Actors/Resources/SR.Designer.cs | 13 +++- .../Runtime/ActorRegistrationCollection.cs | 69 +++++++++++++++++-- .../Runtime/ActorTypeInformation.cs | 38 +++++++++- 3 files changed, 111 insertions(+), 9 deletions(-) diff --git a/src/Dapr.Actors/Resources/SR.Designer.cs b/src/Dapr.Actors/Resources/SR.Designer.cs index f507b596a..866ebd3ba 100644 --- a/src/Dapr.Actors/Resources/SR.Designer.cs +++ b/src/Dapr.Actors/Resources/SR.Designer.cs @@ -230,7 +230,18 @@ internal static string ErrorNotAnActor { return ResourceManager.GetString("ErrorNotAnActor", resourceCulture); } } - + + /// + /// Looks up a localized string similar to The type '{0}' is not an Actor Interface. An actor type must derive from '{1}'.. + /// + internal static string ErrorNotAnActorInterface + { + get + { + return ResourceManager.GetString("ErrorNotAnActorInterface", resourceCulture); + } + } + /// /// Looks up a localized string similar to The type '{0}' is not an actor interface as it does not derive from the interface '{1}'.. /// diff --git a/src/Dapr.Actors/Runtime/ActorRegistrationCollection.cs b/src/Dapr.Actors/Runtime/ActorRegistrationCollection.cs index 69c01646d..b1941c158 100644 --- a/src/Dapr.Actors/Runtime/ActorRegistrationCollection.cs +++ b/src/Dapr.Actors/Runtime/ActorRegistrationCollection.cs @@ -1,4 +1,4 @@ -// ------------------------------------------------------------------------ +// ------------------------------------------------------------------------ // Copyright 2021 The Dapr Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -51,7 +51,7 @@ public void RegisterActor(Action configure = null) public void RegisterActor(ActorRuntimeOptions typeOptions, Action configure = null) where TActor : Actor { - RegisterActor(null, typeOptions, configure); + RegisterActor(typeof(TActor), default, typeOptions, configure); } /// @@ -64,21 +64,78 @@ public void RegisterActor(ActorRuntimeOptions typeOptions, Action(string actorTypeName, Action configure = null) where TActor : Actor { - RegisterActor(actorTypeName, null, configure); + RegisterActor(typeof(TActor), actorTypeName, null, configure); } /// /// Registers an actor type in the collection. /// + /// Type of actor interface. + /// Type of actor. + /// An optional delegate used to configure the actor registration. + public void RegisterActor(Action configure = null) + where TActor : Actor + { + RegisterActor(actorTypeName: null, configure); + } + + /// + /// Registers an actor type in the collection. + /// + /// Type of actor interface. /// Type of actor. - /// The name of the actor type represented by the actor. /// An optional that defines values for this type alone. /// An optional delegate used to configure the actor registration. + public void RegisterActor(ActorRuntimeOptions typeOptions, Action configure = null) + where TActor : Actor + { + RegisterActor(typeof(TActorInterface), typeof(TActor), null, typeOptions, configure); + } + + /// + /// Registers an actor type in the collection. + /// + /// Type of actor interface. + /// Type of actor. + /// The name of the actor type represented by the actor. + /// An optional delegate used to configure the actor registration. /// The value of will have precedence over the default actor type name derived from the actor implementation type or any type name set via . - public void RegisterActor(string actorTypeName, ActorRuntimeOptions typeOptions, Action configure = null) + public void RegisterActor(string actorTypeName, Action configure = null) where TActor : Actor { - var actorTypeInfo = ActorTypeInformation.Get(typeof(TActor), actorTypeName); + RegisterActor(typeof(TActorInterface), typeof(TActor), actorTypeName, null, configure); + } + + /// + /// Registers an actor type in the collection. + /// + /// Type of actor. + /// The name of the actor type represented by the actor. + /// An optional that defines values for this type alone. + /// An optional delegate used to configure the actor registration. + /// The value of will have precedence over the default actor type name derived from the actor implementation type or any type name set via . + public void RegisterActor(Type actorType, string actorTypeName, ActorRuntimeOptions typeOptions, Action configure = null) + { + RegisterActorInternal(default, actorType, actorTypeName, typeOptions, configure); + } + + /// + /// Registers an actor type in the collection. + /// + /// Type of actor interface. + /// Type of actor. + /// The name of the actor type represented by the actor. + /// An optional that defines values for this type alone. + /// An optional delegate used to configure the actor registration. + /// The value of will have precedence over the default actor type name derived from the actor implementation type or any type name set via . + public void RegisterActor(Type actorInterfaceType, Type actorType, string actorTypeName, ActorRuntimeOptions typeOptions, Action configure = null) + { + RegisterActorInternal(actorInterfaceType, actorType, actorTypeName, typeOptions, configure); + } + + private void RegisterActorInternal(Type actorInterfaceType, Type actorType, string actorTypeName, ActorRuntimeOptions typeOptions, Action configure = null) + { + var actorTypeInfo = ActorTypeInformation.Get(actorInterfaceType, actorType, actorTypeName); var registration = new ActorRegistration(actorTypeInfo, typeOptions); configure?.Invoke(registration); this.Add(registration); diff --git a/src/Dapr.Actors/Runtime/ActorTypeInformation.cs b/src/Dapr.Actors/Runtime/ActorTypeInformation.cs index 801524218..7a0cc077e 100644 --- a/src/Dapr.Actors/Runtime/ActorTypeInformation.cs +++ b/src/Dapr.Actors/Runtime/ActorTypeInformation.cs @@ -1,4 +1,4 @@ -// ------------------------------------------------------------------------ +// ------------------------------------------------------------------------ // Copyright 2021 The Dapr Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -120,6 +120,40 @@ public static ActorTypeInformation Get(Type actorType) /// The value of will have precedence over the default actor type name derived from the actor implementation type or any type name set via . public static ActorTypeInformation Get(Type actorType, string actorTypeName) { + return GetInternal(default, actorType, actorTypeName); + } + + /// + /// Creates an from actorType. + /// + /// The type of interface implementing the actor to create ActorTypeInformation for. + /// The type of class implementing the actor to create ActorTypeInformation for. + /// The name of the actor type represented by the actor. + /// created from actorType. + /// + /// When for actorType is not of type . + /// When actorType does not implement an interface deriving from + /// and is not marked as abstract. + /// + /// The value of will have precedence over the default actor type name derived from the actor implementation type or any type name set via . + public static ActorTypeInformation Get(Type actorInterfaceType, Type actorType, string actorTypeName) + { + return GetInternal(actorInterfaceType, actorType, actorTypeName); + } + + private static ActorTypeInformation GetInternal(Type actorInterfaceType, Type actorType, string actorTypeName) + { + if (actorInterfaceType != default && !actorInterfaceType.IsActor()) + { + throw new ArgumentException( + string.Format( + CultureInfo.CurrentCulture, + SR.ErrorNotAnActorInterface, + actorInterfaceType.FullName, + typeof(Actor).FullName), + "actorInterfaceType"); + } + if (!actorType.IsActor()) { throw new ArgumentException( @@ -132,7 +166,7 @@ public static ActorTypeInformation Get(Type actorType, string actorTypeName) } // get all actor interfaces - var actorInterfaces = actorType.GetActorInterfaces(); + var actorInterfaces = actorInterfaceType != default ? new Type[] { actorInterfaceType } : actorType.GetActorInterfaces(); // ensure that the if the actor type is not abstract it implements at least one actor interface if ((actorInterfaces.Length == 0) && (!actorType.GetTypeInfo().IsAbstract)) From 87eedfd02df8e37f9bf4fa5a3c0b54a03e7b43ab Mon Sep 17 00:00:00 2001 From: Siri Varma Vegiraju Date: Mon, 23 Sep 2024 18:40:07 -0700 Subject: [PATCH 2/6] pull request comment Signed-off-by: Siri Varma Vegiraju --- src/Dapr.Actors/Runtime/ActorRegistrationCollection.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Dapr.Actors/Runtime/ActorRegistrationCollection.cs b/src/Dapr.Actors/Runtime/ActorRegistrationCollection.cs index b1941c158..d50fc418f 100644 --- a/src/Dapr.Actors/Runtime/ActorRegistrationCollection.cs +++ b/src/Dapr.Actors/Runtime/ActorRegistrationCollection.cs @@ -74,6 +74,7 @@ public void RegisterActor(string actorTypeName, ActionType of actor. /// An optional delegate used to configure the actor registration. public void RegisterActor(Action configure = null) + where TActorInterface : Actor where TActor : Actor { RegisterActor(actorTypeName: null, configure); @@ -87,6 +88,7 @@ public void RegisterActor(Action con /// An optional that defines values for this type alone. /// An optional delegate used to configure the actor registration. public void RegisterActor(ActorRuntimeOptions typeOptions, Action configure = null) + where TActorInterface : Actor where TActor : Actor { RegisterActor(typeof(TActorInterface), typeof(TActor), null, typeOptions, configure); @@ -101,6 +103,7 @@ public void RegisterActor(ActorRuntimeOptions typeOptio /// An optional delegate used to configure the actor registration. /// The value of will have precedence over the default actor type name derived from the actor implementation type or any type name set via . public void RegisterActor(string actorTypeName, Action configure = null) + where TActorInterface : Actor where TActor : Actor { RegisterActor(typeof(TActorInterface), typeof(TActor), actorTypeName, null, configure); From 404da07d7306a6800228dee438acbc23cc4fb54e Mon Sep 17 00:00:00 2001 From: Siri Varma Vegiraju Date: Mon, 23 Sep 2024 18:55:25 -0700 Subject: [PATCH 3/6] pull request comment Signed-off-by: Siri Varma Vegiraju --- .../Runtime/ActorRegistrationCollection.cs | 6 +-- .../Runtime/ActorTypeInformation.cs | 2 +- .../ActorHostingTest.cs | 49 ++++++++++++++++++- 3 files changed, 52 insertions(+), 5 deletions(-) diff --git a/src/Dapr.Actors/Runtime/ActorRegistrationCollection.cs b/src/Dapr.Actors/Runtime/ActorRegistrationCollection.cs index d50fc418f..69187d6ff 100644 --- a/src/Dapr.Actors/Runtime/ActorRegistrationCollection.cs +++ b/src/Dapr.Actors/Runtime/ActorRegistrationCollection.cs @@ -74,7 +74,7 @@ public void RegisterActor(string actorTypeName, ActionType of actor. /// An optional delegate used to configure the actor registration. public void RegisterActor(Action configure = null) - where TActorInterface : Actor + where TActorInterface : IActor where TActor : Actor { RegisterActor(actorTypeName: null, configure); @@ -88,7 +88,7 @@ public void RegisterActor(Action con /// An optional that defines values for this type alone. /// An optional delegate used to configure the actor registration. public void RegisterActor(ActorRuntimeOptions typeOptions, Action configure = null) - where TActorInterface : Actor + where TActorInterface : IActor where TActor : Actor { RegisterActor(typeof(TActorInterface), typeof(TActor), null, typeOptions, configure); @@ -103,7 +103,7 @@ public void RegisterActor(ActorRuntimeOptions typeOptio /// An optional delegate used to configure the actor registration. /// The value of will have precedence over the default actor type name derived from the actor implementation type or any type name set via . public void RegisterActor(string actorTypeName, Action configure = null) - where TActorInterface : Actor + where TActorInterface : IActor where TActor : Actor { RegisterActor(typeof(TActorInterface), typeof(TActor), actorTypeName, null, configure); diff --git a/src/Dapr.Actors/Runtime/ActorTypeInformation.cs b/src/Dapr.Actors/Runtime/ActorTypeInformation.cs index 7a0cc077e..c4aa21b02 100644 --- a/src/Dapr.Actors/Runtime/ActorTypeInformation.cs +++ b/src/Dapr.Actors/Runtime/ActorTypeInformation.cs @@ -143,7 +143,7 @@ public static ActorTypeInformation Get(Type actorInterfaceType, Type actorType, private static ActorTypeInformation GetInternal(Type actorInterfaceType, Type actorType, string actorTypeName) { - if (actorInterfaceType != default && !actorInterfaceType.IsActor()) + if (actorInterfaceType != default && !actorInterfaceType.IsActorInterface()) { throw new ArgumentException( string.Format( diff --git a/test/Dapr.Actors.AspNetCore.Test/ActorHostingTest.cs b/test/Dapr.Actors.AspNetCore.Test/ActorHostingTest.cs index 8e34eaffd..ee530d5a5 100644 --- a/test/Dapr.Actors.AspNetCore.Test/ActorHostingTest.cs +++ b/test/Dapr.Actors.AspNetCore.Test/ActorHostingTest.cs @@ -1,4 +1,4 @@ -// ------------------------------------------------------------------------ +// ------------------------------------------------------------------------ // Copyright 2021 The Dapr Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -13,6 +13,7 @@ using System.Linq; using System.Text.Json; +using System.Threading.Tasks; using Dapr.Actors.Client; using Dapr.Actors.Runtime; using Microsoft.Extensions.DependencyInjection; @@ -88,6 +89,25 @@ public void CanAccessProxyFactoryWithCustomJsonOptions() Assert.Same(jsonOptions, factory.DefaultOptions.JsonSerializerOptions); } + [Fact] + public void CanRegisterActorsToSpecificInterface() + { + var services = new ServiceCollection(); + services.AddLogging(); + services.AddOptions(); + services.AddActors(options => + { + options.Actors.RegisterActor(); + }); + + var runtime = services.BuildServiceProvider().GetRequiredService(); + + Assert.Collection( + runtime.RegisteredActors.Select(r => r.Type.ActorTypeName).OrderBy(t => t), + t => Assert.Equal(ActorTypeInformation.Get(typeof(TestActor1), actorTypeName: null).ActorTypeName, t), + t => Assert.Equal(ActorTypeInformation.Get(typeof(TestActor2), actorTypeName: null).ActorTypeName, t)); + } + private interface ITestActor : IActor { } @@ -107,5 +127,32 @@ public TestActor2(ActorHost host) { } } + + public interface IMyActor : IActor + { + Task SomeMethod(); + } + + public interface IInternalMyActor : IMyActor + { + void SomeInternalMethod(); + } + + public class InternalMyActor : Actor, IInternalMyActor + { + public InternalMyActor(ActorHost host) + : base(host) + { + } + + public void SomeInternalMethod() + { + } + + public Task SomeMethod() + { + return Task.CompletedTask; + } + } } } From c463186a8b2e8e56917167eccc5d57d1961cf909 Mon Sep 17 00:00:00 2001 From: Siri Varma Vegiraju Date: Mon, 23 Sep 2024 19:20:36 -0700 Subject: [PATCH 4/6] Address more changes Signed-off-by: Siri Varma Vegiraju --- .../Runtime/ActorRegistrationCollection.cs | 6 ++-- .../Runtime/ActorTypeExtensions.cs | 2 +- .../ActorHostingTest.cs | 32 +++++++++++++++++-- 3 files changed, 33 insertions(+), 7 deletions(-) diff --git a/src/Dapr.Actors/Runtime/ActorRegistrationCollection.cs b/src/Dapr.Actors/Runtime/ActorRegistrationCollection.cs index 69187d6ff..bff587a53 100644 --- a/src/Dapr.Actors/Runtime/ActorRegistrationCollection.cs +++ b/src/Dapr.Actors/Runtime/ActorRegistrationCollection.cs @@ -75,7 +75,7 @@ public void RegisterActor(string actorTypeName, ActionAn optional delegate used to configure the actor registration. public void RegisterActor(Action configure = null) where TActorInterface : IActor - where TActor : Actor + where TActor : Actor, TActorInterface { RegisterActor(actorTypeName: null, configure); } @@ -89,7 +89,7 @@ public void RegisterActor(Action con /// An optional delegate used to configure the actor registration. public void RegisterActor(ActorRuntimeOptions typeOptions, Action configure = null) where TActorInterface : IActor - where TActor : Actor + where TActor : Actor, TActorInterface { RegisterActor(typeof(TActorInterface), typeof(TActor), null, typeOptions, configure); } @@ -104,7 +104,7 @@ public void RegisterActor(ActorRuntimeOptions typeOptio /// The value of will have precedence over the default actor type name derived from the actor implementation type or any type name set via . public void RegisterActor(string actorTypeName, Action configure = null) where TActorInterface : IActor - where TActor : Actor + where TActor : Actor, TActorInterface { RegisterActor(typeof(TActorInterface), typeof(TActor), actorTypeName, null, configure); } diff --git a/src/Dapr.Actors/Runtime/ActorTypeExtensions.cs b/src/Dapr.Actors/Runtime/ActorTypeExtensions.cs index 4acaa3912..ddb2e79ed 100644 --- a/src/Dapr.Actors/Runtime/ActorTypeExtensions.cs +++ b/src/Dapr.Actors/Runtime/ActorTypeExtensions.cs @@ -1,4 +1,4 @@ -// ------------------------------------------------------------------------ +// ------------------------------------------------------------------------ // Copyright 2021 The Dapr Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/test/Dapr.Actors.AspNetCore.Test/ActorHostingTest.cs b/test/Dapr.Actors.AspNetCore.Test/ActorHostingTest.cs index ee530d5a5..1c2e891f8 100644 --- a/test/Dapr.Actors.AspNetCore.Test/ActorHostingTest.cs +++ b/test/Dapr.Actors.AspNetCore.Test/ActorHostingTest.cs @@ -11,6 +11,7 @@ // limitations under the License. // ------------------------------------------------------------------------ +using System; using System.Linq; using System.Text.Json; using System.Threading.Tasks; @@ -104,8 +105,33 @@ public void CanRegisterActorsToSpecificInterface() Assert.Collection( runtime.RegisteredActors.Select(r => r.Type.ActorTypeName).OrderBy(t => t), - t => Assert.Equal(ActorTypeInformation.Get(typeof(TestActor1), actorTypeName: null).ActorTypeName, t), - t => Assert.Equal(ActorTypeInformation.Get(typeof(TestActor2), actorTypeName: null).ActorTypeName, t)); + t => Assert.Equal(ActorTypeInformation.Get(typeof(IMyActor), typeof(InternalMyActor), actorTypeName: null).ActorTypeName, t)); + + Assert.Collection( + runtime.RegisteredActors.Select(r => r.Type.InterfaceTypes.First()).OrderBy(t => t), + t => Assert.Equal(ActorTypeInformation.Get(typeof(IMyActor), typeof(InternalMyActor), actorTypeName: null).InterfaceTypes.First(), t)); + + Assert.True(runtime.RegisteredActors.First().Type.InterfaceTypes.Count() == 1); + } + + [Fact] + public void RegisterActorThrowsArgumentExceptionWhenAnyInterfaceInTheChainIsNotIActor() + { + var services = new ServiceCollection(); + services.AddLogging(); + services.AddOptions(); + services.AddActors(options => + { + Assert.Throws(() => options.Actors.RegisterActor()); + }); + } + + private interface INonActor + { + } + + private interface INonActor1 : INonActor, IActor + { } private interface ITestActor : IActor @@ -138,7 +164,7 @@ public interface IInternalMyActor : IMyActor void SomeInternalMethod(); } - public class InternalMyActor : Actor, IInternalMyActor + public class InternalMyActor : Actor, IInternalMyActor, INonActor1 { public InternalMyActor(ActorHost host) : base(host) From 41656aace7190d9060734a46cc382cd4dda5e724 Mon Sep 17 00:00:00 2001 From: Siri Varma Vegiraju Date: Wed, 9 Oct 2024 22:25:08 -0700 Subject: [PATCH 5/6] Update dotnet-actors-usage.md Signed-off-by: Siri Varma Vegiraju --- .../dotnet-actors/dotnet-actors-usage.md | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) 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 index cd642523e..535746763 100644 --- 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 @@ -136,8 +136,11 @@ public void ConfigureServices(IServiceCollection services) // Register actor runtime with DI services.AddActors(options => { - // Register actor types and configure actor settings + // Register actor types and configure actor settings. options.Actors.RegisterActor(); + + // Register MyActor to a specific interface. + options.Actors.RegisterActor(); // Configure default settings options.ActorIdleTimeout = TimeSpan.FromMinutes(10); @@ -150,6 +153,12 @@ public void ConfigureServices(IServiceCollection services) services.AddSingleton(); } ``` +> [!IMPORTANT] +> When registering actors, note the return type requirements for the methods inside the interfaces: +> * Pattern 1: `options.Actors.RegisterActor()` +> * In this case, all interfaces implemented by `MyActor` must have methods that return only `Task` or `Task`. This applies to every method in all interfaces `MyActor` implements. +> * Pattern 2: `options.Actors.RegisterActor()` +> * Here, only the `IMyActor` interface is required to have methods that return `Task` or `Task`. Other interfaces `MyActor` are not subject to this restriction. ### Configuring JSON options @@ -241,4 +250,4 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) ## Next steps -Try the [Running and using virtual actors example]({{< ref dotnet-actors-howto.md >}}). \ No newline at end of file +Try the [Running and using virtual actors example]({{< ref dotnet-actors-howto.md >}}). From d06803e83f53d7d39d1d23c79817099a979bed50 Mon Sep 17 00:00:00 2001 From: Siri Varma Vegiraju Date: Thu, 10 Oct 2024 07:55:18 -0700 Subject: [PATCH 6/6] Update dotnet-actors-usage.md Signed-off-by: Siri Varma Vegiraju --- .../en/dotnet-sdk-docs/dotnet-actors/dotnet-actors-usage.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 index 535746763..60e3bc202 100644 --- 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 @@ -136,10 +136,10 @@ public void ConfigureServices(IServiceCollection services) // Register actor runtime with DI services.AddActors(options => { - // Register actor types and configure actor settings. + // Register actor types and configure actor settings options.Actors.RegisterActor(); - // Register MyActor to a specific interface. + // Register MyActor to a specific interface options.Actors.RegisterActor(); // Configure default settings