Akka.net DI - Как ввести двух актеров через DI?
То, что я пытаюсь сделать, это передать двух актеров (актер-мумия и актер-папочка) актеру-пацану. Поскольку рекомендуется использовать ссылку на актера вместо актера, я использовал IActorRef для актера-мамы и актера-папочки, который вводится через DI с именованным параметром. Но я получаю сообщение "mummyActor is not unique". Есть идеи как это решить?
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();
}
}
}
3 ответа
Дело в том, что типы 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();
}
}
Я надеюсь, что это поможет вам.
Альтернативное решение состоит в том, чтобы ввести путь как Маме, так и Папе и использовать Context.ActorSelection внутри Kid, чтобы найти их в системе. Это лучше подходит для удаленной / кластерной ситуации и в тех случаях, когда у вас есть циклическая цепочка ссылок.
Актеры мамы и папы создаются для каждого актера ребенка. Актеры мамы и папы должны иметь уникальное имя или быть зарегистрированы как синглтоны.