如何注入两个演员通过DI



我想做的是把两个演员(妈妈演员和爸爸演员)传递给孩子演员。因为使用actor引用而不是actor是最佳实践,所以我使用IActorRef将mummy actor和daddy actor通过DI注入,并带有命名参数。但是我得到"mummyActor不是唯一的"错误。知道怎么解吗?

using System;
using System.Threading.Tasks;
using Akka.Actor;
using Akka.DI.AutoFac;
using Akka.DI.Core;
using Autofac;
using Autofac.Core;

namespace Akka.DI.AutoFac.ExampleConsole {
    public class DaddyActor : ReceiveActor {
        public DaddyActor() {
            Receive<DoneEatingMessage>(m => {
                Console.WriteLine("Kid finished eating. So what? ~ Dad");
            });
        }
    }

    public class MummyActor : ReceiveActor {
        public MummyActor() {
            Receive<DoneEatingMessage>(m => {
                Console.WriteLine("Kid finished eating. Time to clean up! ~Mummy");
            });
        }
    }
    public class KidActor : ReceiveActor {
        private IService _service;
        private IActorRef _mummyActor;
        private IActorRef _daddyActor;
        public KidActor(IService service, IActorRef mummyActor, IActorRef daddyActor) {
            this._service = service;
            this._mummyActor = mummyActor;
            this._daddyActor = daddyActor;
            Receive<EatMessage>(m=>{
                var food = service.GetFood();
                Console.WriteLine("Kid eat this food {0}", food);
                _mummyActor.Tell(new DoneEatingMessage());
            });
        }

    }
    public class EatMessage{    }
    public class DoneEatingMessage { }
    public interface IService {
        string GetFood();
    }
    public class FoodService : IService {
        public string GetFood() {
            return "banana";
        }
    }
    class Program {
        static ActorSystem _actorSystem;
        static void Main(string[] args) {
            var builder = new Autofac.ContainerBuilder();
            builder.RegisterType<FoodService>().As<IService>();
            builder.RegisterType<MummyActor>().InstancePerDependency();
            builder.RegisterType<DaddyActor>().InstancePerDependency();
            builder.Register(c => _actorSystem.ActorOf(_actorSystem.DI().Props<DaddyActor>(), "daddyActor"))
                .Named<IActorRef>("daddyActorRef")
                .AsSelf();
            builder.Register(c => _actorSystem.ActorOf(_actorSystem.DI().Props<MummyActor>(), "mummyActor"))
                .Named<IActorRef>("mummyActorRef")
                .AsSelf();

            builder.RegisterType<KidActor>()
                .WithParameter(
                  new ResolvedParameter(
                       (pi, ctx) => pi.ParameterType == typeof(MummyActor),
                       (pi, ctx) => ctx.ResolveNamed<IActorRef>("mummyActorRef")
                    )
                )
                .WithParameter(
                  new ResolvedParameter(
                       (pi, ctx) => pi.ParameterType == typeof(DaddyActor),
                       (pi, ctx) => ctx.ResolveNamed<IActorRef>("daddyActorRef")
                    )
                )
                .InstancePerDependency();
            var container = builder.Build();
            _actorSystem = ActorSystem.Create("ActorDISystem");
            var propsResolver = new AutoFacDependencyResolver(container, _actorSystem);

            var kidActorProps = _actorSystem.DI().Props<KidActor>();
            var kidActor = _actorSystem.ActorOf(kidActorProps, "kidActor");
            kidActor.Tell(new EatMessage());
            Console.WriteLine("Holah");
            Console.ReadLine();
        }
    }
}

问题是MummyActor和addyactor类型不是IActorRef的实例。所以你不能在创建KidActor时使用这些类型。

我对AutoFac不是很熟悉,但我能让它像这样工作:

builder.RegisterType<KidActor>()
.WithParameter(
  new ResolvedParameter(
       (pi, ctx) => pi.Name == "mummyActor",
       (pi, ctx) => ctx.ResolveNamed<IActorRef>("mummyActorRef")
    )
)
.WithParameter(
  new ResolvedParameter(
       (pi, ctx) => pi.Name == "daddyActor",
       (pi, ctx) => ctx.ResolveNamed<IActorRef>("daddyActorRef")
    )
)
.InstancePerDependency();

我使用参数的名称进行检查。然而,我认为这种解决方案可能相当危险,特别是如果您重命名参数。

你可以做的另一件事是将这些实例的创建委托给具有特定方法的服务/工厂,该服务通过DI注入。

下面是我做了一些重构后得到的结果:

public class DaddyActor : ReceiveActor
{
    public DaddyActor()
    {
        Receive<DoneEatingMessage>(m =>
        {
            Console.WriteLine("Kid finished eating. So what? ~ Dad");
        });
    }
}
public class MummyActor : ReceiveActor
{
    public MummyActor()
    {
        Receive<DoneEatingMessage>(m =>
        {
            Console.WriteLine("Kid finished eating. Time to clean up! ~Mummy");
        });
    }
}
public class KidActor : ReceiveActor
{
    private IService _service;
    private IActorRef _mummyActor;
    private IActorRef _daddyActor;
    public KidActor(IService service, IParentFactory parentFactory)
    {
        this._service = service;
        this._mummyActor = parentFactory.CreateMother(Context.System);
        this._daddyActor = parentFactory.CreateFather(Context.System);
        Receive<EatMessage>(m =>
        {
            var food = service.GetFood();
            Console.WriteLine("Kid eat this food {0}", food);
            _mummyActor.Tell(new DoneEatingMessage());
            _daddyActor.Tell(new DoneEatingMessage());
        });
    }
}
public class EatMessage { }
public class DoneEatingMessage { }
public interface IService
{
    string GetFood();
}
public class FoodService : IService
{
    public string GetFood()
    {
        return "banana";
    }
}
public interface IParentFactory
{
    IActorRef CreateMother(ActorSystem actorSystem);
    IActorRef CreateFather(ActorSystem actorSystem);
}
public class ParentFactory : IParentFactory
{
    public IActorRef CreateFather(ActorSystem actorSystem)
    {
        return actorSystem.ActorOf(actorSystem.DI().Props<DaddyActor>(), "daddyActor");
    }
    public IActorRef CreateMother(ActorSystem actorSystem)
    {
        return actorSystem.ActorOf(actorSystem.DI().Props<MummyActor>(), "mummyActor");
    }
}
class Program
{
    static ActorSystem _actorSystem;
    static void Main(string[] args)
    {
        var builder = new Autofac.ContainerBuilder();
        builder.RegisterType<FoodService>().As<IService>();
        builder.RegisterType<ParentFactory>().As<IParentFactory>();
        builder.RegisterType<MummyActor>().InstancePerDependency();
        builder.RegisterType<DaddyActor>().InstancePerDependency();
        builder.RegisterType<KidActor>().InstancePerDependency();
        var container = builder.Build();
        _actorSystem = ActorSystem.Create("ActorDISystem");
        var propsResolver = new AutoFacDependencyResolver(container, _actorSystem);
        var kidActorProps = _actorSystem.DI().Props<KidActor>();
        var kidActor = _actorSystem.ActorOf(kidActorProps, "kidActor");
        kidActor.Tell(new EatMessage());
        Console.WriteLine("Holah");
        Console.ReadLine();
        _actorSystem.AwaitTermination();
    }
}

希望对你有帮助。

另一个解决方案是同时注入Mummy和Daddy的路径并使用Context。在Kid内部的ActorSelection中找到它们。这更适合远程/集群情况,以及有循环引用链的情况。

妈妈和爸爸的演员是每个孩子的演员创建。mummy和daddy演员要么需要唯一命名,要么注册为singleton。

最新更新