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 { } diff --git a/src/contrib/dependencyinjection/Akka.DependencyInjection/ServiceProvider.cs b/src/contrib/dependencyinjection/Akka.DependencyInjection/ServiceProvider.cs index 7ddfabb9d13..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 ServiceProviderProps(Provider, args); + return Akka.Actor.Props.CreateBy(new ServiceProviderActorProducer(Provider, args)); } } @@ -99,51 +84,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 where TActor:ActorBase { 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, object[] args) { _provider = provider; + _args = args; + ActorType = typeof(TActor); } - /// - /// 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.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() { } 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 c4f8cb507f8..e06d8d97015 100644 --- a/src/core/Akka/Actor/Props.cs +++ b/src/core/Akka/Actor/Props.cs @@ -9,238 +9,110 @@ using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; -using System.Reflection; +using Akka.Configuration; 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, - }; - } + private static readonly Deploy DefaultDeploy = new Deploy(); + private static readonly object[] NoArgs = { }; /// - /// 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(); - - /// - /// 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 Type _inputType; + private Type _outputType; + private readonly IIndirectActorProducer _producer; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// protected Props() - : this(defaultDeploy, null, noArgs) + : this(DefaultDeploy, null, NoArgs) { } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// 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) { } /// - /// 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) + : this(DefaultDeploy, type, args) { if (type == null) throw new ArgumentNullException(nameof(type), NullActorTypeExceptionText); } /// - /// 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) + : this(DefaultDeploy, type, NoArgs) { if (type == null) throw new ArgumentNullException(nameof(type), NullActorTypeExceptionText); } /// - /// 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()) + : this(DefaultDeploy, type, args.ToArray()) { if (type == null) throw new ArgumentNullException(nameof(type), NullActorTypeExceptionText); @@ -249,17 +121,16 @@ 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) + : this(DefaultDeploy, type, args) { if (type == null) throw new ArgumentNullException(nameof(type), NullActorTypeExceptionText); @@ -268,15 +139,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,37 +155,52 @@ 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. - public Props(Deploy deploy, Type type, params object[] args) + /// 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 + { + + } + + /// + /// 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 supervisor strategy to use. + /// The arguments needed to create the actor. + internal Props(IIndirectActorProducer producer, Deploy deploy, params object[] args) { Deploy = deploy; - inputType = type; - Arguments = args ?? noArgs; - producer = CreateProducer(inputType, Arguments); + _inputType = producer.ActorType; + Arguments = args ?? NoArgs; + _producer = producer; } /// - /// 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; - } - return outputType; + 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 +213,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,46 +358,58 @@ 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(new ActivatorProducer(typeof(TActor), args), DefaultDeploy, args){ SupervisorStrategy = supervisorStrategy }; } /// - /// 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. /// 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, args); } /// - /// 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. /// 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 supervisor strategy. + /// 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. /// /// The type of the actor to create. /// The supervisor strategy used to manage the actor. /// 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 }; } /// - /// 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 +424,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 +501,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,65 +523,130 @@ 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 { - 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); + 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); } } /// - /// 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 }; + return new Props(_producer, Deploy, 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.Instance; + + 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 + { + _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) { - } } private class DefaultProducer : IIndirectActorProducer { + private DefaultProducer(){} + + public static readonly DefaultProducer Instance = new DefaultProducer(); + 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 +657,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 +693,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 +703,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 +722,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 +734,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 +744,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 +761,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 +780,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 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();