2015-08-31 8 views
3

Что я пытаюсь сделать, так это передать двух актеров (актера мумии и актера-папы) на роль актера. Поскольку лучше использовать ссылку актера вместо актера, я использовал IActorRef как для мумий-актера, так и для актера-папы, который должен быть введен через DI с именованным параметром. Но я получаю ошибку «mummyActor is not unique». Любая идея, как его решить?Akka.net DI - Как ввести двух актеров через DI?

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(); 
     } 
    } 
} 
+0

Вы решили проблему? У меня такая же проблема ... –

+0

Мы решили пойти с Service Locator, так как наш проект - это просто POC. Но я думаю, что предложение @Julien Fiaffé тоже приятно. –

+0

Отзывы –

ответ

0

Создатели мумии и папы создаются для каждого актера. Мумий и папы актеры либо должны быть однозначно названы или зарегистрированы как одиночные.

+0

Да. Я думаю, что этот код, который я использую с «WithParameter», ошибочен. Я дал уникальное имя (ref: Named («daddyActorRef»)), но я не уверен, как ссылаться на них из параметра. Что мне нужно изменить? –

4

Дело в том, что типы MummyActor и DaddyActor не являются экземплярами 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(); 
    } 
} 

Я надеюсь, что это поможет.

+0

Использование фабрики для встраивания актеров скрывает фактические зависимости самого актера, то есть вам нужно прочитать логику конструктора, чтобы узнать, на что влияет фактический актер, на который зависит KidActor. Сохранение их в конструкторе лучше и для защиты от переименования аргументов конструктора я обычно имею набор единичных тестов против контейнера, подтверждающий, что участники могут быть разрешены. – nrjohnstone

+0

. Другая проблема с использованием «фабрики» - это заставить ее выглядеть например, KidActor фактически создает актеров матери и отца и тем самым становится их руководителем (родителем), который, как я считаю, не имеет места – nrjohnstone

1

Альтернативным решением было бы ввести путь как Мумии, так и Папы и использовать Context.ActorSelection внутри Kid, чтобы найти их в системе. Это лучше подходит для удаленной/кластерной ситуации и в случаях, когда у вас циклическая цепочка ссылок.