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..bff587a53 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,81 @@ 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 TActorInterface : IActor
+ where TActor : Actor, TActorInterface
+ {
+ RegisterActor(actorTypeName: null, configure);
+ }
+
+ ///
+ /// Registers an actor type in the collection.
+ ///
+ /// Type of actor interface.
+ /// Type of 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 TActorInterface : IActor
+ where TActor : Actor, TActorInterface
+ {
+ 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, Action configure = null)
+ where TActorInterface : IActor
+ where TActor : Actor, TActorInterface
+ {
+ 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(string actorTypeName, ActorRuntimeOptions typeOptions, Action configure = null)
- where TActor : Actor
+ 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(typeof(TActor), actorTypeName);
+ 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/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/src/Dapr.Actors/Runtime/ActorTypeInformation.cs b/src/Dapr.Actors/Runtime/ActorTypeInformation.cs
index 801524218..c4aa21b02 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.IsActorInterface())
+ {
+ 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))
diff --git a/test/Dapr.Actors.AspNetCore.Test/ActorHostingTest.cs b/test/Dapr.Actors.AspNetCore.Test/ActorHostingTest.cs
index 8e34eaffd..1c2e891f8 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.
@@ -11,8 +11,10 @@
// limitations under the License.
// ------------------------------------------------------------------------
+using System;
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 +90,50 @@ 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(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
{
}
@@ -107,5 +153,32 @@ public TestActor2(ActorHost host)
{
}
}
+
+ public interface IMyActor : IActor
+ {
+ Task SomeMethod();
+ }
+
+ public interface IInternalMyActor : IMyActor
+ {
+ void SomeInternalMethod();
+ }
+
+ public class InternalMyActor : Actor, IInternalMyActor, INonActor1
+ {
+ public InternalMyActor(ActorHost host)
+ : base(host)
+ {
+ }
+
+ public void SomeInternalMethod()
+ {
+ }
+
+ public Task SomeMethod()
+ {
+ return Task.CompletedTask;
+ }
+ }
}
}