From c6c6bd3279a4c49b6559b536ed9f238eeed5720c Mon Sep 17 00:00:00 2001 From: Aaron Stannard Date: Tue, 16 Mar 2021 16:29:25 -0500 Subject: [PATCH 01/11] full reformat of Props class --- src/core/Akka/Actor/Props.cs | 616 ++++++++++++++++------------------- 1 file changed, 289 insertions(+), 327 deletions(-) diff --git a/src/core/Akka/Actor/Props.cs b/src/core/Akka/Actor/Props.cs index c4f8cb507f8..475855c05e9 100644 --- a/src/core/Akka/Actor/Props.cs +++ b/src/core/Akka/Actor/Props.cs @@ -9,173 +9,50 @@ using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; -using System.Reflection; using Akka.Dispatch; -using Akka.Util.Internal; -using Akka.Util.Reflection; using Akka.Routing; using Akka.Util; +using Akka.Util.Internal; +using Akka.Util.Reflection; using Newtonsoft.Json; namespace Akka.Actor { /// - /// This class represents a configuration object used in creating an actor. - /// It is immutable and thus thread-safe. - /// - /// + /// This class represents a configuration object used in creating an actor. + /// It is immutable and thus thread-safe. + /// + /// /// private Props props = Props.Empty(); /// private Props props = Props.Create(() => new MyActor(arg1, arg2)); /// /// private Props otherProps = props.WithDispatcher("dispatcher-id"); /// private Props otherProps = props.WithDeploy(deployment info); /// - /// + /// /// - public class Props : IEquatable , ISurrogated + public class Props : IEquatable, ISurrogated { private const string NullActorTypeExceptionText = "Props must be instantiated with an actor type."; - /// - /// This class represents a surrogate of a configuration object. - /// Its main use is to help during the serialization process. - /// - public class PropsSurrogate : ISurrogate - { - /// - /// The type of actor to create - /// - public Type Type { get; set; } - /// - /// The configuration used to deploy the actor. - /// - public Deploy Deploy { get; set; } - /// - /// The arguments used to create the actor. - /// - public object[] Arguments { get; set; } - - /// - /// Creates a encapsulated by this surrogate. - /// - /// The actor system that owns this router. - /// The encapsulated by this surrogate. - public ISurrogated FromSurrogate(ActorSystem system) - { - return new Props(Deploy, Type, Arguments); - } - } - - /// - /// Creates a surrogate representation of the current . - /// - /// The actor system that owns this router. - /// The surrogate representation of the current . - public ISurrogate ToSurrogate(ActorSystem system) - { - return new PropsSurrogate() - { - Arguments = Arguments, - Type = Type, - Deploy = Deploy, - }; - } - - /// - /// Indicates whether the current object is equal to another object of the same type. - /// - /// An object to compare with this object. - /// - /// true if the current object is equal to the parameter; otherwise, false. - /// - public bool Equals(Props other) - { - if (ReferenceEquals(null, other)) return false; - if (ReferenceEquals(this, other)) return true; - return CompareDeploy(other) && CompareSupervisorStrategy(other) && CompareArguments(other) && CompareInputType(other); - } - - private bool CompareInputType(Props other) - { - return inputType == other.inputType; - } - - private bool CompareDeploy(Props other) - { - return Deploy.Equals(other.Deploy); - } - -#pragma warning disable CS0162 // Disabled because it's marked as a TODO - private bool CompareSupervisorStrategy(Props other) - { - return true; //TODO: fix https://github.com/akkadotnet/akka.net/issues/599 - return Equals(SupervisorStrategy, other.SupervisorStrategy); - } -#pragma warning restore CS0162 - - private bool CompareArguments(Props other) - { - if (other == null) - return false; - - if (Arguments == null && other.Arguments == null) - return true; - - if (Arguments == null) - return false; - - if (Arguments.Length != other.Arguments.Length) - return false; - - //TODO: since arguments can be serialized, we can not compare by ref - //arguments may also not implement equality operators, so we can not structurally compare either - //we can not just call a serializer and compare outputs either, since different args may require diff serializer mechanics - - return true; - } - - /// - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != this.GetType()) return false; - return Equals((Props) obj); - } - - /// - public override int GetHashCode() - { - unchecked - { - int hashCode = (Deploy != null ? Deploy.GetHashCode() : 0); - // hashCode = (hashCode*397) ^ (SupervisorStrategy != null ? SupervisorStrategy.GetHashCode() : 0); - // hashCode = (hashCode*397) ^ (Arguments != null ? Arguments.GetHashCode() : 0); - hashCode = (hashCode*397) ^ (inputType != null ? inputType.GetHashCode() : 0); - return hashCode; - } - } - private static readonly Deploy defaultDeploy = new Deploy(); - private static readonly Object[] noArgs = { }; - private static readonly Props empty = Props.Create(); + private static readonly object[] noArgs = { }; /// - /// A pre-configured that doesn't create actors. - /// - /// - /// The value of this field is null. - /// + /// A pre-configured that doesn't create actors. + /// + /// The value of this field is null. + /// /// public static readonly Props None = null; private static readonly IIndirectActorProducer defaultProducer = new DefaultProducer(); private Type inputType; private Type outputType; - private IIndirectActorProducer producer; + private readonly IIndirectActorProducer producer; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// protected Props() : this(defaultDeploy, null, noArgs) @@ -183,7 +60,7 @@ protected Props() } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The object that is being cloned. protected Props(Props copy) @@ -192,16 +69,15 @@ protected Props(Props copy) } /// - /// Initializes a new instance of the class. - /// - /// - /// configured in this way uses the deployer. - /// + /// Initializes a new instance of the class. + /// + /// configured in this way uses the deployer. + /// /// /// The type of the actor to create. /// The arguments needed to create the actor. /// - /// This exception is thrown if is not instantiated with an actor type. + /// This exception is thrown if is not instantiated with an actor type. /// public Props(Type type, object[] args) : this(defaultDeploy, type, args) @@ -211,17 +87,15 @@ public Props(Type type, object[] args) } /// - /// Initializes a new instance of the class. - /// - /// - /// configured in this way uses the deployer. - /// + /// Initializes a new instance of the class. + /// + /// configured in this way uses the deployer. + /// /// /// The type of the actor to create. /// - /// This exception is thrown if is not instantiated with an actor type. + /// This exception is thrown if is not instantiated with an actor type. /// - public Props(Type type) : this(defaultDeploy, type, noArgs) { @@ -230,15 +104,14 @@ public Props(Type type) } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The type of the actor to create. /// The supervisor strategy used to manage the actor. /// The arguments needed to create the actor. /// - /// This exception is thrown if is not instantiated with an actor type. + /// This exception is thrown if is not instantiated with an actor type. /// - public Props(Type type, SupervisorStrategy supervisorStrategy, IEnumerable args) : this(defaultDeploy, type, args.ToArray()) { @@ -249,15 +122,14 @@ public Props(Type type, SupervisorStrategy supervisorStrategy, IEnumerable - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The type of the actor to create. /// The supervisor strategy used to manage the actor. /// The arguments needed to create the actor. /// - /// This exception is thrown if is not instantiated with an actor type. + /// This exception is thrown if is not instantiated with an actor type. /// - public Props(Type type, SupervisorStrategy supervisorStrategy, params object[] args) : this(defaultDeploy, type, args) { @@ -268,15 +140,14 @@ public Props(Type type, SupervisorStrategy supervisorStrategy, params object[] a } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The configuration used to deploy the actor. /// The type of the actor to create. /// The arguments needed to create the actor. /// - /// This exception is thrown if is not instantiated with an actor type. + /// This exception is thrown if is not instantiated with an actor type. /// - public Props(Deploy deploy, Type type, IEnumerable args) : this(deploy, type, args.ToArray()) { @@ -285,12 +156,12 @@ public Props(Deploy deploy, Type type, IEnumerable args) } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The configuration used to deploy the actor. /// The type of the actor to create. /// The arguments needed to create the actor. - /// This exception is thrown if is an unknown actor producer. + /// This exception is thrown if is an unknown actor producer. public Props(Deploy deploy, Type type, params object[] args) { Deploy = deploy; @@ -300,22 +171,21 @@ public Props(Deploy deploy, Type type, params object[] args) } /// - /// The type of the actor that is created. + /// The type of the actor that is created. /// [JsonIgnore] public Type Type { get { - if (outputType == null) { - outputType = producer.ActorType; - } + if (outputType == null) outputType = producer.ActorType; + return outputType; } } /// - /// The dispatcher used in the deployment of the actor. + /// The dispatcher used in the deployment of the actor. /// [JsonIgnore] public string Dispatcher @@ -328,68 +198,143 @@ public string Dispatcher } /// - /// The mailbox used in the deployment of the actor. + /// The mailbox used in the deployment of the actor. /// [JsonIgnore] - public string Mailbox - { - get - { - return Deploy.Mailbox; - } - } + public string Mailbox => Deploy.Mailbox; /// - /// The assembly qualified name of the type of the actor that is created. + /// The assembly qualified name of the type of the actor that is created. /// public string TypeName { - get { return inputType.AssemblyQualifiedName; } + get => inputType.AssemblyQualifiedName; //for serialization - private set { inputType = Type.GetType(value); } + private set => inputType = Type.GetType(value); } /// - /// The router used in the deployment of the actor. + /// The router used in the deployment of the actor. /// [JsonIgnore] - public RouterConfig RouterConfig - { - get { return Deploy.RouterConfig; } - } + public RouterConfig RouterConfig => Deploy.RouterConfig; /// - /// The configuration used to deploy the actor. + /// The configuration used to deploy the actor. /// public Deploy Deploy { get; protected set; } /// - /// The supervisor strategy used to manage the actor. + /// The supervisor strategy used to manage the actor. /// public SupervisorStrategy SupervisorStrategy { get; protected set; } /// - /// A pre-configured that creates an actor that doesn't respond to messages. + /// A pre-configured that creates an actor that doesn't respond to messages. /// - public static Props Empty + public static Props Empty { get; } = Create(); + + /// + /// The arguments needed to create the actor. + /// + public object[] Arguments { get; } + + /// + /// Indicates whether the current object is equal to another object of the same type. + /// + /// An object to compare with this object. + /// + /// true if the current object is equal to the parameter; otherwise, false. + /// + public bool Equals(Props other) { - get { return empty; } + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return CompareDeploy(other) && CompareSupervisorStrategy(other) && CompareArguments(other) && + CompareInputType(other); } /// - /// The arguments needed to create the actor. + /// Creates a surrogate representation of the current . /// - public object[] Arguments { get; private set; } + /// The actor system that owns this router. + /// The surrogate representation of the current . + public ISurrogate ToSurrogate(ActorSystem system) + { + return new PropsSurrogate { Arguments = Arguments, Type = Type, Deploy = Deploy }; + } + + private bool CompareInputType(Props other) + { + return inputType == other.inputType; + } + + private bool CompareDeploy(Props other) + { + return Deploy.Equals(other.Deploy); + } + +#pragma warning disable CS0162 // Disabled because it's marked as a TODO + private bool CompareSupervisorStrategy(Props other) + { + return true; //TODO: fix https://github.com/akkadotnet/akka.net/issues/599 + return Equals(SupervisorStrategy, other.SupervisorStrategy); + } +#pragma warning restore CS0162 + + private bool CompareArguments(Props other) + { + if (other == null) + return false; + + if (Arguments == null && other.Arguments == null) + return true; + + if (Arguments == null) + return false; + + if (Arguments.Length != other.Arguments.Length) + return false; + + //TODO: since arguments can be serialized, we can not compare by ref + //arguments may also not implement equality operators, so we can not structurally compare either + //we can not just call a serializer and compare outputs either, since different args may require diff serializer mechanics + + return true; + } + + /// + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != GetType()) return false; + return Equals((Props)obj); + } + + /// + public override int GetHashCode() + { + unchecked + { + var hashCode = Deploy != null ? Deploy.GetHashCode() : 0; + // hashCode = (hashCode*397) ^ (SupervisorStrategy != null ? SupervisorStrategy.GetHashCode() : 0); + // hashCode = (hashCode*397) ^ (Arguments != null ? Arguments.GetHashCode() : 0); + hashCode = (hashCode * 397) ^ (inputType != null ? inputType.GetHashCode() : 0); + return hashCode; + } + } /// - /// Creates an actor using a specified lambda expression. + /// Creates an actor using a specified lambda expression. /// /// The type of the actor to create. /// The lambda expression used to create the actor. /// Optional: The supervisor strategy used to manage the actor. /// The newly created . /// The create function must be a 'new T (args)' expression - public static Props Create(Expression> factory, SupervisorStrategy supervisorStrategy=null) where TActor : ActorBase + public static Props Create(Expression> factory, + SupervisorStrategy supervisorStrategy = null) where TActor : ActorBase { if (factory.Body is UnaryExpression) return new DynamicProps(factory.Compile()); @@ -398,13 +343,13 @@ public static Props Create(Expression> factory, SupervisorS if (newExpression == null) throw new ArgumentException("The create function must be a 'new T (args)' expression"); - object[] args = newExpression.GetArguments(); + var args = newExpression.GetArguments(); - return new Props(typeof (TActor), supervisorStrategy, args); + return new Props(typeof(TActor), supervisorStrategy, args); } /// - /// Creates an actor using the given arguments. + /// Creates an actor using the given arguments. /// /// The type of the actor to create. /// The arguments needed to create the actor. @@ -415,7 +360,7 @@ public static Props Create(params object[] args) where TActor : ActorBas } /// - /// Creates an actor using a specified actor producer. + /// Creates an actor using a specified actor producer. /// /// The type of producer used to create the actor. /// The arguments needed to create the actor. @@ -426,7 +371,7 @@ public static Props CreateBy(params object[] args) where TProducer : } /// - /// Creates an actor using a specified supervisor strategy. + /// Creates an actor using a specified supervisor strategy. /// /// The type of the actor to create. /// The supervisor strategy used to manage the actor. @@ -437,7 +382,7 @@ public static Props CreateBy(params object[] args) where TProducer : } /// - /// Creates an actor of a specified type. + /// Creates an actor of a specified type. /// /// The type of the actor to create. /// The arguments needed to create the actor. @@ -452,65 +397,61 @@ public static Props Create(Type type, params object[] args) } /// - /// Creates a new with a given . - /// - /// - /// This method is immutable and returns a new instance of . - /// + /// Creates a new with a given . + /// + /// This method is immutable and returns a new instance of . + /// /// /// The mailbox used when deploying the actor. /// A new with the provided . public Props WithMailbox(string mailbox) { - Props copy = Copy(); + var copy = Copy(); copy.Deploy = Deploy.WithMailbox(mailbox); return copy; } /// - /// Creates a new with a given . - /// - /// - /// This method is immutable and returns a new instance of . - /// + /// Creates a new with a given . + /// + /// This method is immutable and returns a new instance of . + /// /// /// The dispatcher used when deploying the actor. /// A new with the provided . public Props WithDispatcher(string dispatcher) { - Props copy = Copy(); + var copy = Copy(); copy.Deploy = Deploy.WithDispatcher(dispatcher); return copy; } /// - /// Creates a new with a given router. - /// - /// - /// This method is immutable and returns a new instance of . - /// + /// Creates a new with a given router. + /// + /// This method is immutable and returns a new instance of . + /// /// /// The router used when deploying the actor. /// A new with the provided . public Props WithRouter(RouterConfig routerConfig) { - Props copy = Copy(); + var copy = Copy(); copy.Deploy = Deploy.WithRouterConfig(routerConfig); return copy; } /// - /// Creates a new with a given deployment configuration. - /// - /// - /// This method is immutable and returns a new instance of . - /// + /// Creates a new with a given deployment configuration. + /// + /// This method is immutable and returns a new instance of . + /// /// /// The configuration used to deploy the actor. /// A new with the provided . public Props WithDeploy(Deploy deploy) { - Props copy = Copy(); + var copy = Copy(); var original = copy.Deploy; // TODO: this is a hack designed to preserve explicit router deployments https://github.com/akkadotnet/akka.net/issues/546 @@ -533,22 +474,21 @@ public Props WithDeploy(Deploy deploy) //{ // copy.Deploy = deploy; //} - + return copy; } - /// - /// Creates a new with a given supervisor strategy. - /// - /// - /// This method is immutable and returns a new instance of . - /// - /// - /// The supervisor strategy used to manage the actor. + /// + /// Creates a new with a given supervisor strategy. + /// + /// This method is immutable and returns a new instance of . + /// + /// + /// The supervisor strategy used to manage the actor. /// A new with the provided . public Props WithSupervisorStrategy(SupervisorStrategy supervisorStrategy) { - Props copy = Copy(); + var copy = Copy(); copy.SupervisorStrategy = supervisorStrategy; return copy; } @@ -556,51 +496,114 @@ public Props WithSupervisorStrategy(SupervisorStrategy supervisorStrategy) //TODO: use Linq Expressions so compile a creator //cache the creator /// - /// Creates a new actor using the configured actor producer. - /// - /// - /// This method is only useful when called during actor creation by the ActorSystem. - /// + /// Creates a new actor using the configured actor producer. + /// + /// This method is only useful when called during actor creation by the ActorSystem. + /// /// /// - /// This exception is thrown if there was an error creating an actor of type - /// with the arguments from . + /// This exception is thrown if there was an error creating an actor of type + /// with the arguments from . /// /// The newly created actor public virtual ActorBase NewActor() { var type = Type; var arguments = Arguments; - try { + try + { return producer.Produce(); - } catch (Exception e) { - throw new TypeLoadException($"Error while creating actor instance of type {type} with {arguments.Length} args: ({StringFormat.SafeJoin(",", arguments)})", e); + } + catch (Exception e) + { + throw new TypeLoadException( + $"Error while creating actor instance of type {type} with {arguments.Length} args: ({StringFormat.SafeJoin(",", arguments)})", + e); } } /// - /// Creates a copy of the current instance. + /// Creates a copy of the current instance. /// - /// The newly created + /// The newly created protected virtual Props Copy() { return new Props(Deploy, inputType, Arguments) { SupervisorStrategy = SupervisorStrategy }; } + private static IIndirectActorProducer CreateProducer(Type type, object[] args) + { + if (type == null) return defaultProducer; + + if (typeof(IIndirectActorProducer).IsAssignableFrom(type)) + return Activator.CreateInstance(type, args).AsInstanceOf(); + + if (typeof(ActorBase).IsAssignableFrom(type)) return new ActivatorProducer(type, args); + + throw new ArgumentException($"Unknown actor producer [{type.FullName}]", nameof(type)); + } + + /// + /// Signals the producer that it can release its reference to the actor. + /// + /// The actor to release + internal void Release(ActorBase actor) + { + try + { + if (producer != null) producer.Release(actor); + } + finally + { + actor = null; + } + } + + /// + /// This class represents a surrogate of a configuration object. + /// Its main use is to help during the serialization process. + /// + public class PropsSurrogate : ISurrogate + { + /// + /// The type of actor to create + /// + public Type Type { get; set; } + + /// + /// The configuration used to deploy the actor. + /// + public Deploy Deploy { get; set; } + + /// + /// The arguments used to create the actor. + /// + public object[] Arguments { get; set; } + + /// + /// Creates a encapsulated by this surrogate. + /// + /// The actor system that owns this router. + /// The encapsulated by this surrogate. + public ISurrogated FromSurrogate(ActorSystem system) + { + return new Props(Deploy, Type, Arguments); + } + } + #region INTERNAL API /// - /// This class represents a specialized that doesn't respond to messages. + /// This class represents a specialized that doesn't respond to messages. /// internal class EmptyActor : UntypedActor { /// - /// Handles messages received by the actor. + /// Handles messages received by the actor. /// /// The message past to the actor. protected override void OnReceive(object message) { - } } @@ -611,10 +614,7 @@ public ActorBase Produce() throw new InvalidOperationException("No actor producer specified!"); } - public Type ActorType - { - get { return typeof(ActorBase); } - } + public Type ActorType => typeof(ActorBase); public void Release(ActorBase actor) @@ -625,24 +625,20 @@ public void Release(ActorBase actor) private class ActivatorProducer : IIndirectActorProducer { - private readonly Type _actorType; private readonly object[] _args; public ActivatorProducer(Type actorType, object[] args) { - _actorType = actorType; + ActorType = actorType; _args = args; } public ActorBase Produce() { - return Activator.CreateInstance(_actorType, _args).AsInstanceOf(); + return Activator.CreateInstance(ActorType, _args).AsInstanceOf(); } - public Type ActorType - { - get { return _actorType; } - } + public Type ActorType { get; } public void Release(ActorBase actor) @@ -665,10 +661,7 @@ public ActorBase Produce() return _factory.Invoke(); } - public Type ActorType - { - get { return typeof(TActor); } - } + public Type ActorType => typeof(TActor); public void Release(ActorBase actor) @@ -678,45 +671,15 @@ public void Release(ActorBase actor) } #endregion - - private static IIndirectActorProducer CreateProducer(Type type, object[] args) - { - if (type == null) { - return defaultProducer; - } - if (typeof(IIndirectActorProducer).IsAssignableFrom(type)) { - return Activator.CreateInstance(type, args).AsInstanceOf(); - } - if (typeof(ActorBase).IsAssignableFrom(type)) { - return new ActivatorProducer(type, args); - } - throw new ArgumentException($"Unknown actor producer [{type.FullName}]", nameof(type)); - } - - /// - /// Signals the producer that it can release its reference to the actor. - /// - /// The actor to release - internal void Release(ActorBase actor) - { - try - { - if (this.producer != null) this.producer.Release(actor); - } - finally - { - actor = null; - } - } } /// - /// This class represents a specialized used when the actor has been terminated. + /// This class represents a specialized used when the actor has been terminated. /// public class TerminatedProps : Props { /// - /// N/A + /// N/A /// /// This exception is thrown automatically since the actor has been terminated. /// N/A @@ -727,12 +690,11 @@ public override ActorBase NewActor() } /// - /// This class represents a specialized that uses dynamic invocation - /// to create new actor instances, rather than a traditional . - /// - /// - /// This is intended to be used in conjunction with Dependency Injection. - /// + /// This class represents a specialized that uses dynamic invocation + /// to create new actor instances, rather than a traditional . + /// + /// This is intended to be used in conjunction with Dependency Injection. + /// /// /// The type of the actor to create. internal class DynamicProps : Props where TActor : ActorBase @@ -740,7 +702,7 @@ internal class DynamicProps : Props where TActor : ActorBase private readonly Func invoker; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The factory method used to create an actor. public DynamicProps(Func invoker) @@ -750,7 +712,7 @@ public DynamicProps(Func invoker) } /// - /// Creates a new actor using the configured factory method. + /// Creates a new actor using the configured factory method. /// /// The actor created using the factory method. public override ActorBase NewActor() @@ -767,17 +729,17 @@ private DynamicProps(Props copy, Func invoker) } /// - /// Creates a copy of the current instance. + /// Creates a copy of the current instance. /// - /// The newly created + /// The newly created protected override Props Copy() { - Props initialCopy = base.Copy(); + var initialCopy = base.Copy(); #if CLONEABLE var invokerCopy = (Func)invoker.Clone(); #else // TODO: CORECLR FIX IT - var invokerCopy = (Func)invoker; + var invokerCopy = invoker; #endif return new DynamicProps(initialCopy, invokerCopy); } @@ -786,37 +748,37 @@ protected override Props Copy() } /// - /// This interface defines a class of actor creation strategies deviating from - /// the usual default of just reflectively instantiating the Actor - /// subclass. It can be used to allow a dependency injection framework to - /// determine the actual actor class and how it shall be instantiated. + /// This interface defines a class of actor creation strategies deviating from + /// the usual default of just reflectively instantiating the Actor + /// subclass. It can be used to allow a dependency injection framework to + /// determine the actual actor class and how it shall be instantiated. /// public interface IIndirectActorProducer { /// - /// This factory method must produce a fresh actor instance upon each - /// invocation. It is not permitted to return the same instance more than - /// once. + /// This method is used by to determine the type of actor to create. + /// The returned type is not used to produce the actor. /// - /// A fresh actor instance. - ActorBase Produce(); + /// The type of the actor created. + Type ActorType { get; } /// - /// This method is used by to determine the type of actor to create. - /// The returned type is not used to produce the actor. + /// This factory method must produce a fresh actor instance upon each + /// invocation. It is not permitted to return the same instance more than + /// once. /// - /// The type of the actor created. - Type ActorType { get; } + /// A fresh actor instance. + ActorBase Produce(); /// - /// This method is used by to signal the producer that it can - /// release it's reference. - /// - /// - /// To learn more about using Dependency Injection in .NET, see HERE. - /// + /// This method is used by to signal the producer that it can + /// release it's reference. + /// + /// To learn more about using Dependency Injection in .NET, see + /// HERE. + /// /// /// The actor to release void Release(ActorBase actor); } -} +} \ No newline at end of file From ca66c8c118c6ab3040a4aa3523d5bc53f91cc0f3 Mon Sep 17 00:00:00 2001 From: Aaron Stannard Date: Tue, 16 Mar 2021 16:56:36 -0500 Subject: [PATCH 02/11] refactor DefaultProducer --- src/core/Akka/Actor/Props.cs | 73 +++++++++++++++++++++++------------- 1 file changed, 47 insertions(+), 26 deletions(-) diff --git a/src/core/Akka/Actor/Props.cs b/src/core/Akka/Actor/Props.cs index 475855c05e9..630ed2bdac4 100644 --- a/src/core/Akka/Actor/Props.cs +++ b/src/core/Akka/Actor/Props.cs @@ -35,8 +35,8 @@ public class Props : IEquatable, ISurrogated { private const string NullActorTypeExceptionText = "Props must be instantiated with an actor type."; - private static readonly Deploy defaultDeploy = new Deploy(); - private static readonly object[] noArgs = { }; + private static readonly Deploy DefaultDeploy = new Deploy(); + private static readonly object[] NoArgs = { }; /// /// A pre-configured that doesn't create actors. @@ -45,17 +45,15 @@ public class Props : IEquatable, ISurrogated /// /// public static readonly Props None = null; - - private static readonly IIndirectActorProducer defaultProducer = new DefaultProducer(); - private Type inputType; - private Type outputType; - private readonly IIndirectActorProducer producer; + private Type _inputType; + private Type _outputType; + private readonly IIndirectActorProducer _producer; /// /// Initializes a new instance of the class. /// protected Props() - : this(defaultDeploy, null, noArgs) + : this(DefaultDeploy, null, NoArgs) { } @@ -64,7 +62,7 @@ protected Props() /// /// The object that is being cloned. protected Props(Props copy) - : this(copy.Deploy, copy.inputType, copy.SupervisorStrategy, copy.Arguments) + : this(copy.Deploy, copy._inputType, copy.SupervisorStrategy, copy.Arguments) { } @@ -80,7 +78,7 @@ protected Props(Props copy) /// This exception is thrown if is not instantiated with an actor type. /// public Props(Type type, object[] args) - : this(defaultDeploy, type, args) + : this(DefaultDeploy, type, args) { if (type == null) throw new ArgumentNullException(nameof(type), NullActorTypeExceptionText); @@ -97,7 +95,7 @@ public Props(Type type, object[] args) /// This exception is thrown if is not instantiated with an actor type. /// public Props(Type type) - : this(defaultDeploy, type, noArgs) + : this(DefaultDeploy, type, NoArgs) { if (type == null) throw new ArgumentNullException(nameof(type), NullActorTypeExceptionText); @@ -113,7 +111,7 @@ public Props(Type type) /// This exception is thrown if is not instantiated with an actor type. /// public Props(Type type, SupervisorStrategy supervisorStrategy, IEnumerable args) - : this(defaultDeploy, type, args.ToArray()) + : this(DefaultDeploy, type, args.ToArray()) { if (type == null) throw new ArgumentNullException(nameof(type), NullActorTypeExceptionText); @@ -131,7 +129,7 @@ public Props(Type type, SupervisorStrategy supervisorStrategy, IEnumerable is not instantiated with an actor type. /// public Props(Type type, SupervisorStrategy supervisorStrategy, params object[] args) - : this(defaultDeploy, type, args) + : this(DefaultDeploy, type, args) { if (type == null) throw new ArgumentNullException(nameof(type), NullActorTypeExceptionText); @@ -165,9 +163,27 @@ public Props(Deploy deploy, Type type, IEnumerable args) public Props(Deploy deploy, Type type, params object[] args) { Deploy = deploy; - inputType = type; - Arguments = args ?? noArgs; - producer = CreateProducer(inputType, Arguments); + _inputType = type; + Arguments = args ?? NoArgs; + _producer = CreateProducer(_inputType, Arguments); + } + + /// + /// Initializes a new instance of the class using a specified . + /// + /// + /// This API is meant for advanced use cases, such as Akka.DependencyInjection. + /// + /// The type of that will be used to instantiate + /// The configuration used to deploy the actor. + /// The type of the actor to create. + /// The arguments needed to create the actor. + public Props(IIndirectActorProducer producer, Deploy deploy, Type type, params object[] args) + { + Deploy = deploy; + _inputType = type; + Arguments = args ?? NoArgs; + _producer = producer; } /// @@ -178,9 +194,9 @@ public Type Type { get { - if (outputType == null) outputType = producer.ActorType; + if (_outputType == null) _outputType = _producer.ActorType; - return outputType; + return _outputType; } } @@ -208,9 +224,9 @@ public string Dispatcher /// public string TypeName { - get => inputType.AssemblyQualifiedName; + get => _inputType.AssemblyQualifiedName; //for serialization - private set => inputType = Type.GetType(value); + private set => _inputType = Type.GetType(value); } /// @@ -266,7 +282,7 @@ public ISurrogate ToSurrogate(ActorSystem system) private bool CompareInputType(Props other) { - return inputType == other.inputType; + return _inputType == other._inputType; } private bool CompareDeploy(Props other) @@ -320,7 +336,7 @@ public override int GetHashCode() var hashCode = Deploy != null ? Deploy.GetHashCode() : 0; // hashCode = (hashCode*397) ^ (SupervisorStrategy != null ? SupervisorStrategy.GetHashCode() : 0); // hashCode = (hashCode*397) ^ (Arguments != null ? Arguments.GetHashCode() : 0); - hashCode = (hashCode * 397) ^ (inputType != null ? inputType.GetHashCode() : 0); + hashCode = (hashCode * 397) ^ (_inputType != null ? _inputType.GetHashCode() : 0); return hashCode; } } @@ -512,7 +528,7 @@ public virtual ActorBase NewActor() var arguments = Arguments; try { - return producer.Produce(); + return _producer.Produce(); } catch (Exception e) { @@ -528,12 +544,13 @@ public virtual ActorBase NewActor() /// The newly created protected virtual Props Copy() { - return new Props(Deploy, inputType, Arguments) { SupervisorStrategy = SupervisorStrategy }; + return new Props(Deploy, _inputType, Arguments) { SupervisorStrategy = SupervisorStrategy }; } + [Obsolete("we should not be calling this method. Pass in an explicit IIndirectActorProducer reference isntead.")] private static IIndirectActorProducer CreateProducer(Type type, object[] args) { - if (type == null) return defaultProducer; + if (type == null) return DefaultProducer.Instance; if (typeof(IIndirectActorProducer).IsAssignableFrom(type)) return Activator.CreateInstance(type, args).AsInstanceOf(); @@ -551,7 +568,7 @@ internal void Release(ActorBase actor) { try { - if (producer != null) producer.Release(actor); + _producer?.Release(actor); } finally { @@ -609,6 +626,10 @@ protected override void OnReceive(object message) private class DefaultProducer : IIndirectActorProducer { + private DefaultProducer(){} + + public static readonly DefaultProducer Instance = new DefaultProducer(); + public ActorBase Produce() { throw new InvalidOperationException("No actor producer specified!"); From 8d2f0f411f929278f6e61f7ae946141f4385b676 Mon Sep 17 00:00:00 2001 From: Aaron Stannard Date: Wed, 17 Mar 2021 11:34:20 -0500 Subject: [PATCH 03/11] close #3599 - cleaned up Props methods to make IIndirectorActorProducer dependencies more explicit --- .../ServiceProvider.cs | 46 ++++++------------- src/core/Akka.Remote.Tests/RemotingSpec.cs | 25 +++++++--- src/core/Akka.Tests/Actor/PropsSpec.cs | 2 +- src/core/Akka/Actor/Props.cs | 27 +++++++---- .../Util/Reflection/ExpressionExtensions.cs | 8 ++-- 5 files changed, 55 insertions(+), 53 deletions(-) diff --git a/src/contrib/dependencyinjection/Akka.DependencyInjection/ServiceProvider.cs b/src/contrib/dependencyinjection/Akka.DependencyInjection/ServiceProvider.cs index 7ddfabb9d13..5340aa699b0 100644 --- a/src/contrib/dependencyinjection/Akka.DependencyInjection/ServiceProvider.cs +++ b/src/contrib/dependencyinjection/Akka.DependencyInjection/ServiceProvider.cs @@ -74,7 +74,7 @@ public static ServiceProvider For(ActorSystem actorSystem) /// A new instance which uses DI internally. public Props Props(params object[] args) where T : ActorBase { - return new ServiceProviderProps(Provider, args); + return new Props(typeof(T), args); } } @@ -99,51 +99,33 @@ public override ServiceProvider CreateExtension(ExtendedActorSystem system) } /// - /// This class represents a specialized that uses delegate invocation - /// to create new actor instances, rather than a traditional . + /// INTERNAL API /// - /// Relies on having an active implementation available + /// Used to create actors via the . /// - /// The type of the actor to create. - internal class ServiceProviderProps : Props where TActor : ActorBase + /// the actor type + internal sealed class ServiceProviderActorProducer : IIndirectActorProducer { private readonly IServiceProvider _provider; + private readonly object[] _args; - /// - /// Initializes a new instance of the class. - /// - /// The used to power this class - /// The constructor arguments passed to the actor's constructor. - public ServiceProviderProps(IServiceProvider provider, params object[] args) - : base(typeof(TActor), args) + public ServiceProviderActorProducer(IServiceProvider provider, Type actorType, object[] args) { _provider = provider; + _args = args; + ActorType = actorType; } - /// - /// Creates a new actor using the configured factory method. - /// - /// The actor created using the factory method. - public override ActorBase NewActor() + public ActorBase Produce() { - return ActivatorUtilities.CreateInstance(_provider, Arguments); + return (ActorBase)ActivatorUtilities.CreateInstance(_provider, ActorType, _args); } - #region Copy methods + public Type ActorType { get; } - /// - /// Creates a copy of the current instance. - /// - /// The newly created - protected override Props Copy() + public void Release(ActorBase actor) { - return new ServiceProviderProps(_provider, Arguments) - { - Deploy = Deploy, - SupervisorStrategy = SupervisorStrategy - }; + // no-op } - - #endregion } } diff --git a/src/core/Akka.Remote.Tests/RemotingSpec.cs b/src/core/Akka.Remote.Tests/RemotingSpec.cs index 519ef8e09be..136d9093d7f 100644 --- a/src/core/Akka.Remote.Tests/RemotingSpec.cs +++ b/src/core/Akka.Remote.Tests/RemotingSpec.cs @@ -411,8 +411,7 @@ public void Remoting_must_create_by_IndirectActorProducer() { try { - Resolve.SetResolver(new TestResolver()); - var r = Sys.ActorOf(Props.CreateBy>(), "echo"); + var r = Sys.ActorOf(Props.CreateBy(new TestResolver()), "echo"); Assert.Equal("akka.test://remote-sys@localhost:12346/remote/akka.test/RemotingSpec@localhost:12345/user/echo", r.Path.ToString()); } finally @@ -426,8 +425,7 @@ public void Remoting_must_create_by_IndirectActorProducer_and_ping() { try { - Resolve.SetResolver(new TestResolver()); - var r = Sys.ActorOf(Props.CreateBy>(), "echo"); + var r = Sys.ActorOf(Props.CreateBy(new TestResolver()), "echo"); Assert.Equal("akka.test://remote-sys@localhost:12346/remote/akka.test/RemotingSpec@localhost:12345/user/echo", r.Path.ToString()); r.Tell("ping", TestActor); ExpectMsg(("pong", TestActor), TimeSpan.FromSeconds(1.5)); @@ -902,11 +900,24 @@ protected override void OnReceive(object message) } } - class TestResolver : IResolver + class TestResolver : IIndirectActorProducer where TActor:ActorBase { - public T Resolve(object[] args) + public Type ActorType => typeof(TActor); + private readonly object[] _args; + + public TestResolver(params object[] args) + { + _args = args; + } + + public ActorBase Produce() + { + return (ActorBase)Activator.CreateInstance(ActorType, _args); + } + + public void Release(ActorBase actor) { - return Activator.CreateInstance(typeof(T), args).AsInstanceOf(); + } } diff --git a/src/core/Akka.Tests/Actor/PropsSpec.cs b/src/core/Akka.Tests/Actor/PropsSpec.cs index f2cab37e7dd..0fea33c30d1 100644 --- a/src/core/Akka.Tests/Actor/PropsSpec.cs +++ b/src/core/Akka.Tests/Actor/PropsSpec.cs @@ -36,7 +36,7 @@ public void Props_must_create_actor_by_producer() { TestLatch latchProducer = new TestLatch(); TestLatch latchActor = new TestLatch(); - var props = Props.CreateBy(latchProducer, latchActor); + var props = Props.CreateBy(new TestProducer(latchProducer, latchActor)); IActorRef actor = Sys.ActorOf(props); latchActor.Ready(TimeSpan.FromSeconds(1)); } diff --git a/src/core/Akka/Actor/Props.cs b/src/core/Akka/Actor/Props.cs index 630ed2bdac4..ab478a2638e 100644 --- a/src/core/Akka/Actor/Props.cs +++ b/src/core/Akka/Actor/Props.cs @@ -9,6 +9,7 @@ using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; +using Akka.Configuration; using Akka.Dispatch; using Akka.Routing; using Akka.Util; @@ -160,12 +161,9 @@ public Props(Deploy deploy, Type type, IEnumerable args) /// The type of the actor to create. /// The arguments needed to create the actor. /// This exception is thrown if is an unknown actor producer. - public Props(Deploy deploy, Type type, params object[] args) + public Props(Deploy deploy, Type type, params object[] args) : this(CreateProducer(type, args), deploy, args) { - Deploy = deploy; - _inputType = type; - Arguments = args ?? NoArgs; - _producer = CreateProducer(_inputType, Arguments); + } /// @@ -176,12 +174,11 @@ public Props(Deploy deploy, Type type, params object[] args) /// /// The type of that will be used to instantiate /// The configuration used to deploy the actor. - /// The type of the actor to create. /// The arguments needed to create the actor. - public Props(IIndirectActorProducer producer, Deploy deploy, Type type, params object[] args) + internal Props(IIndirectActorProducer producer, Deploy deploy, params object[] args) { Deploy = deploy; - _inputType = type; + _inputType = producer.ActorType; Arguments = args ?? NoArgs; _producer = producer; } @@ -372,7 +369,7 @@ public static Props Create(Expression> factory, /// The newly created . public static Props Create(params object[] args) where TActor : ActorBase { - return new Props(typeof(TActor), args); + return new Props(new ActivatorProducer(typeof(TActor), args), DefaultDeploy); } /// @@ -381,11 +378,23 @@ public static Props Create(params object[] args) where TActor : ActorBas /// The type of producer used to create the actor. /// The arguments needed to create the actor. /// The newly created . + [Obsolete("Do not use this method. Call CreateBy(IIndirectActorProducer, params object[] args) instead")] public static Props CreateBy(params object[] args) where TProducer : class, IIndirectActorProducer { return new Props(typeof(TProducer), args); } + /// + /// Creates an actor using a specified actor producer. + /// + /// The actor producer that will be used to create the underlying actor.. + /// The arguments needed to create the actor. + /// The newly created . + public static Props CreateBy(IIndirectActorProducer producer, params object[] args) + { + return new Props(producer, DefaultDeploy, args); + } + /// /// Creates an actor using a specified supervisor strategy. /// diff --git a/src/core/Akka/Util/Reflection/ExpressionExtensions.cs b/src/core/Akka/Util/Reflection/ExpressionExtensions.cs index 9e63529586c..4b5de2886a0 100644 --- a/src/core/Akka/Util/Reflection/ExpressionExtensions.cs +++ b/src/core/Akka/Util/Reflection/ExpressionExtensions.cs @@ -16,15 +16,15 @@ namespace Akka.Util.Reflection { /// - /// TBD + /// INTERNAL API /// public static class ExpressionExtensions { /// - /// TBD + /// Fetches constructor arguments from a . /// - /// TBD - /// TBD + /// The used typically to create an actor. + /// The constructor type and arguments public static object[] GetArguments(this NewExpression newExpression) { return newExpression.ParseExpressionArgs(); From 0e354de9af570119e1a4bc06e6689caafc139777 Mon Sep 17 00:00:00 2001 From: Aaron Stannard Date: Wed, 17 Mar 2021 11:46:00 -0500 Subject: [PATCH 04/11] close #4854 - removed and replaced ServiceProviderProps --- .../ServiceProvider.cs | 23 ++++--------------- 1 file changed, 4 insertions(+), 19 deletions(-) diff --git a/src/contrib/dependencyinjection/Akka.DependencyInjection/ServiceProvider.cs b/src/contrib/dependencyinjection/Akka.DependencyInjection/ServiceProvider.cs index 5340aa699b0..883730d0b27 100644 --- a/src/contrib/dependencyinjection/Akka.DependencyInjection/ServiceProvider.cs +++ b/src/contrib/dependencyinjection/Akka.DependencyInjection/ServiceProvider.cs @@ -47,21 +47,6 @@ public static ServiceProvider For(ActorSystem actorSystem) return actorSystem.WithExtension(); } - ///// - ///// Uses a delegate to dynamically instantiate an actor where some of the constructor arguments are populated via dependency injection - ///// and others are not. - ///// - ///// - ///// YOU ARE RESPONSIBLE FOR MANAGING THE LIFECYCLE OF YOUR OWN DEPENDENCIES. AKKA.NET WILL NOT ATTEMPT TO DO IT FOR YOU. - ///// - ///// The type of actor to instantiate. - ///// The delegate used to create a new instance of your actor type. - ///// A new instance which uses DI internally. - //public Props Props(Func producer) where T : ActorBase - //{ - // return new ServiceProviderProps(producer, Provider); - //} - /// /// Uses a delegate to dynamically instantiate an actor where some of the constructor arguments are populated via dependency injection /// and others are not. @@ -74,7 +59,7 @@ public static ServiceProvider For(ActorSystem actorSystem) /// A new instance which uses DI internally. public Props Props(params object[] args) where T : ActorBase { - return new Props(typeof(T), args); + return Akka.Actor.Props.CreateBy(new ServiceProviderActorProducer(Provider, args)); } } @@ -104,16 +89,16 @@ public override ServiceProvider CreateExtension(ExtendedActorSystem system) /// Used to create actors via the . /// /// the actor type - internal sealed class ServiceProviderActorProducer : IIndirectActorProducer + internal sealed class ServiceProviderActorProducer : IIndirectActorProducer where TActor:ActorBase { private readonly IServiceProvider _provider; private readonly object[] _args; - public ServiceProviderActorProducer(IServiceProvider provider, Type actorType, object[] args) + public ServiceProviderActorProducer(IServiceProvider provider, object[] args) { _provider = provider; _args = args; - ActorType = actorType; + ActorType = typeof(TActor); } public ActorBase Produce() From ae74ec59fd1566ffd634130d3e7848f45b3698ed Mon Sep 17 00:00:00 2001 From: Aaron Stannard Date: Wed, 17 Mar 2021 11:50:17 -0500 Subject: [PATCH 05/11] API approval --- src/core/Akka.API.Tests/CoreAPISpec.ApproveCore.approved.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/core/Akka.API.Tests/CoreAPISpec.ApproveCore.approved.txt b/src/core/Akka.API.Tests/CoreAPISpec.ApproveCore.approved.txt index 99b13c4c08c..ba68d6416f0 100644 --- a/src/core/Akka.API.Tests/CoreAPISpec.ApproveCore.approved.txt +++ b/src/core/Akka.API.Tests/CoreAPISpec.ApproveCore.approved.txt @@ -1408,8 +1408,11 @@ namespace Akka.Actor public static Akka.Actor.Props Create(Akka.Actor.SupervisorStrategy supervisorStrategy) where TActor : Akka.Actor.ActorBase, new () { } public static Akka.Actor.Props Create(System.Type type, params object[] args) { } + [System.ObsoleteAttribute("Do not use this method. Call CreateBy(IIndirectActorProducer, params object[] arg" + + "s) instead")] public static Akka.Actor.Props CreateBy(params object[] args) where TProducer : class, Akka.Actor.IIndirectActorProducer { } + public static Akka.Actor.Props CreateBy(Akka.Actor.IIndirectActorProducer producer, params object[] args) { } public bool Equals(Akka.Actor.Props other) { } public override bool Equals(object obj) { } public override int GetHashCode() { } From 8344974ba1db8a4b2bda80e4a4643a0849259aa4 Mon Sep 17 00:00:00 2001 From: Aaron Stannard Date: Wed, 17 Mar 2021 11:51:43 -0500 Subject: [PATCH 06/11] added comment to clarify important backwards-compat change --- src/core/Akka/Actor/Props.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core/Akka/Actor/Props.cs b/src/core/Akka/Actor/Props.cs index ab478a2638e..860f69d2da6 100644 --- a/src/core/Akka/Actor/Props.cs +++ b/src/core/Akka/Actor/Props.cs @@ -161,7 +161,8 @@ public Props(Deploy deploy, Type type, IEnumerable args) /// The type of the actor to create. /// The arguments needed to create the actor. /// This exception is thrown if is an unknown actor producer. - public Props(Deploy deploy, Type type, params object[] args) : this(CreateProducer(type, args), deploy, args) + public Props(Deploy deploy, Type type, params object[] args) + : this(CreateProducer(type, args), deploy, args) // have to preserve the "CreateProducer" call here to preserve backwards compat with Akka.DI.Core { } From 7b6b2e414527026901f313fa68ec597a28598fb3 Mon Sep 17 00:00:00 2001 From: Aaron Stannard Date: Wed, 17 Mar 2021 12:27:19 -0500 Subject: [PATCH 07/11] fix regression on Props.Create --- src/core/Akka/Actor/Props.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/Akka/Actor/Props.cs b/src/core/Akka/Actor/Props.cs index 860f69d2da6..ac6ec52b888 100644 --- a/src/core/Akka/Actor/Props.cs +++ b/src/core/Akka/Actor/Props.cs @@ -370,7 +370,7 @@ public static Props Create(Expression> factory, /// The newly created . public static Props Create(params object[] args) where TActor : ActorBase { - return new Props(new ActivatorProducer(typeof(TActor), args), DefaultDeploy); + return new Props(new ActivatorProducer(typeof(TActor), args), DefaultDeploy, args); } /// From bcff0267fe5c5d7c2df376fa61378caa6b566ecb Mon Sep 17 00:00:00 2001 From: Aaron Stannard Date: Wed, 17 Mar 2021 13:21:39 -0500 Subject: [PATCH 08/11] eliminate one more source of reflection --- src/core/Akka/Actor/Props.cs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/core/Akka/Actor/Props.cs b/src/core/Akka/Actor/Props.cs index ac6ec52b888..01f9a47bcfc 100644 --- a/src/core/Akka/Actor/Props.cs +++ b/src/core/Akka/Actor/Props.cs @@ -162,7 +162,7 @@ public Props(Deploy deploy, Type type, IEnumerable args) /// The arguments needed to create the actor. /// This exception is thrown if is an unknown actor producer. public Props(Deploy deploy, Type type, params object[] args) - : this(CreateProducer(type, args), deploy, args) // have to preserve the "CreateProducer" call here to preserve backwards compat with Akka.DI.Core + : this(CreateProducer(type, args), deploy, SupervisorStrategy.DefaultStrategy, args) // have to preserve the "CreateProducer" call here to preserve backwards compat with Akka.DI.Core { } @@ -175,13 +175,15 @@ public Props(Deploy deploy, Type type, params object[] args) /// /// The type of that will be used to instantiate /// The configuration used to deploy the actor. + /// The supervisor strategy to use. /// The arguments needed to create the actor. - internal Props(IIndirectActorProducer producer, Deploy deploy, params object[] args) + internal Props(IIndirectActorProducer producer, Deploy deploy, SupervisorStrategy strategy, params object[] args) { Deploy = deploy; _inputType = producer.ActorType; Arguments = args ?? NoArgs; - _producer = producer; + _producer = producer; + SupervisorStrategy = strategy; } /// @@ -359,7 +361,7 @@ public static Props Create(Expression> factory, var args = newExpression.GetArguments(); - return new Props(typeof(TActor), supervisorStrategy, args); + return new Props(new ActivatorProducer(typeof(TActor), args), DefaultDeploy, supervisorStrategy, args); } /// @@ -370,7 +372,7 @@ public static Props Create(Expression> factory, /// The newly created . public static Props Create(params object[] args) where TActor : ActorBase { - return new Props(new ActivatorProducer(typeof(TActor), args), DefaultDeploy, args); + return new Props(new ActivatorProducer(typeof(TActor), args), DefaultDeploy, SupervisorStrategy.DefaultStrategy, args); } /// @@ -393,7 +395,7 @@ public static Props CreateBy(params object[] args) where TProducer : /// The newly created . public static Props CreateBy(IIndirectActorProducer producer, params object[] args) { - return new Props(producer, DefaultDeploy, args); + return new Props(producer, DefaultDeploy, SupervisorStrategy.DefaultStrategy, args); } /// From 485700a91743f7ac05c3948c2891198bfd829bc5 Mon Sep 17 00:00:00 2001 From: Aaron Stannard Date: Wed, 17 Mar 2021 14:11:37 -0500 Subject: [PATCH 09/11] fixed SupervisorStrategy copying --- src/core/Akka/Actor/Props.cs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/core/Akka/Actor/Props.cs b/src/core/Akka/Actor/Props.cs index 01f9a47bcfc..383c7241059 100644 --- a/src/core/Akka/Actor/Props.cs +++ b/src/core/Akka/Actor/Props.cs @@ -177,13 +177,12 @@ public Props(Deploy deploy, Type type, params object[] args) /// The configuration used to deploy the actor. /// The supervisor strategy to use. /// The arguments needed to create the actor. - internal Props(IIndirectActorProducer producer, Deploy deploy, SupervisorStrategy strategy, params object[] args) + internal Props(IIndirectActorProducer producer, Deploy deploy, params object[] args) { Deploy = deploy; _inputType = producer.ActorType; Arguments = args ?? NoArgs; _producer = producer; - SupervisorStrategy = strategy; } /// @@ -361,7 +360,7 @@ public static Props Create(Expression> factory, var args = newExpression.GetArguments(); - return new Props(new ActivatorProducer(typeof(TActor), args), DefaultDeploy, supervisorStrategy, args); + return new Props(new ActivatorProducer(typeof(TActor), args), DefaultDeploy, args){ SupervisorStrategy = supervisorStrategy }; } /// @@ -372,7 +371,7 @@ public static Props Create(Expression> factory, /// The newly created . public static Props Create(params object[] args) where TActor : ActorBase { - return new Props(new ActivatorProducer(typeof(TActor), args), DefaultDeploy, SupervisorStrategy.DefaultStrategy, args); + return new Props(new ActivatorProducer(typeof(TActor), args), DefaultDeploy, args); } /// @@ -395,7 +394,7 @@ public static Props CreateBy(params object[] args) where TProducer : /// The newly created . public static Props CreateBy(IIndirectActorProducer producer, params object[] args) { - return new Props(producer, DefaultDeploy, SupervisorStrategy.DefaultStrategy, args); + return new Props(producer, DefaultDeploy, args); } /// @@ -406,7 +405,7 @@ public static Props CreateBy(IIndirectActorProducer producer, params object[] ar /// The newly created . public static Props Create(SupervisorStrategy supervisorStrategy) where TActor : ActorBase, new() { - return new Props(typeof(TActor), supervisorStrategy); + return new Props(new ActivatorProducer(typeof(TActor), NoArgs), DefaultDeploy, NoArgs){ SupervisorStrategy = supervisorStrategy }; } /// @@ -556,7 +555,7 @@ public virtual ActorBase NewActor() /// The newly created protected virtual Props Copy() { - return new Props(Deploy, _inputType, Arguments) { SupervisorStrategy = SupervisorStrategy }; + return new Props(_producer, Deploy, Arguments) { SupervisorStrategy = SupervisorStrategy }; } [Obsolete("we should not be calling this method. Pass in an explicit IIndirectActorProducer reference isntead.")] From 5ac83ce15491dbb2384159438308123abd30b19b Mon Sep 17 00:00:00 2001 From: Aaron Stannard Date: Wed, 17 Mar 2021 14:31:10 -0500 Subject: [PATCH 10/11] added specs to validate that Akka.DependencyInjection Props propagate values correctly --- ...ctorServiceProviderPropsWithScopesSpecs.cs | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/contrib/dependencyinjection/Akka.DependencyInjection.Tests/ActorServiceProviderPropsWithScopesSpecs.cs b/src/contrib/dependencyinjection/Akka.DependencyInjection.Tests/ActorServiceProviderPropsWithScopesSpecs.cs index 5d44eb52654..506d98c18e1 100644 --- a/src/contrib/dependencyinjection/Akka.DependencyInjection.Tests/ActorServiceProviderPropsWithScopesSpecs.cs +++ b/src/contrib/dependencyinjection/Akka.DependencyInjection.Tests/ActorServiceProviderPropsWithScopesSpecs.cs @@ -9,6 +9,7 @@ using System.Linq; using Akka.Actor; using Akka.Configuration; +using Akka.Routing; using Akka.TestKit; using FluentAssertions; using Microsoft.Extensions.DependencyInjection; @@ -162,6 +163,37 @@ public void ActorsWithNonDiDependenciesShouldStart() deps1Single.Should().Be(deps2Single); } + [Fact(DisplayName = "Props created via the ServiceProvider should support the standard Props copying methods")] + public void ServiceProvider_Props_should_support_copying() + { + // + var spExtension = ServiceProvider.For(Sys); + var arg1 = "foo"; + var arg2 = "bar"; + var props = spExtension.Props(arg1, arg2).WithRouter(new RoundRobinPool(10).WithSupervisorStrategy(new OneForOneStrategy( + ex => + { + TestActor.Tell(ex); + return Directive.Restart; + }))); + + // create a scoped round robin pool using the props from Akka.DependencyInjection + var scoped1 = Sys.ActorOf(props, "scoped1"); + + // validate that non-DI'd arguments were passed to actor constructor arguments correctly + scoped1.Tell("fetch"); + ExpectMsg().Should().Be(arg1); + ExpectMsg().Should().Be(arg2); + + // validate that this is a router + scoped1.Tell(GetRoutees.Instance); + ExpectMsg().Members.Count().Should().Be(10); + + // validate that the router's supervision strategy has been enforced + scoped1.Tell(new Crash()); + ExpectMsg(); + } + public class Crash { } public class FetchDependencies { } From 6c7808fa59efb3ec0e36eaa1ad4dd5248416576b Mon Sep 17 00:00:00 2001 From: Aaron Stannard Date: Wed, 17 Mar 2021 14:43:45 -0500 Subject: [PATCH 11/11] fixed Props copy error --- src/core/Akka/Actor/Props.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/Akka/Actor/Props.cs b/src/core/Akka/Actor/Props.cs index 383c7241059..e06d8d97015 100644 --- a/src/core/Akka/Actor/Props.cs +++ b/src/core/Akka/Actor/Props.cs @@ -162,7 +162,7 @@ public Props(Deploy deploy, Type type, IEnumerable args) /// The arguments needed to create the actor. /// This exception is thrown if is an unknown actor producer. public Props(Deploy deploy, Type type, params object[] args) - : this(CreateProducer(type, args), deploy, SupervisorStrategy.DefaultStrategy, args) // have to preserve the "CreateProducer" call here to preserve backwards compat with Akka.DI.Core + : this(CreateProducer(type, args), deploy, args) // have to preserve the "CreateProducer" call here to preserve backwards compat with Akka.DI.Core { }