A while ago I wrote about creating Actor System and top-level actors. Sadly it was stored in a static field. And keyword static is … let’s say it’s not one of my favourites. Today I’ll show you how I’m doing the exact same thing but in Dependency Injection container.
One of my favourite IoC containers is Ninject and I’m using it almost everywhere. In my pet-project, I’m using it with Nancy FX, but every code sample is also usable with ASP.NET MVC.
First of all, configuration and creation of Actor System should be moved out of any static helper class we’ve had before.
1 2 3 4 5 6 7 8 |
public class ActorSystemContainer { public ActorSystem System { get; private set; } public ActorSystemContainer(ActorSystem actorSystem) { System = actorSystem; } } |
It’s rather simple class. Since we’re going full abstraction let’s extract System property to an interface. It’s possible to skip this part, but if you’re going to inject this container anywhere, you really should have an interface for that.
1 2 3 4 |
public interface IContainActorSystem { ActorSystem System {get;} } |
My ActorSystemContainer have only one top-level actor and I’m not planning to include more of them anytime soon, so I’m creating it in the constructor. If you have more of them, you should probably think about something better than that.
1 2 3 4 5 6 7 8 9 10 |
public class ActorSystemContainer : IContainActorSystem { public ActorSystem System { get; private set; } public IActorRef UsersManagerActorRef { get; private set; } public ActorSystemContainer(ActorSystem actorSystem) { System = actorSystem; UsersManagerActorRef = System.ActorOf(UsersManagerActor.Props, "UsersManagerActorName"); } } |
Now our class is almost ready. All we’ll need is some kind of interface that will allow us to expose our IActorRef of our actors and inject them in this interface. In my case, it’s really simple because I have only one top-level actor but in a lot of cases, you’ll have more of them. And all of them are exposed as IActorRef with no generic parameter or any other way to identify referenced actor type at all. So there is no way to inject IActorRef somewhere, send a message to it and trust our IoC will give us a reference to an actor of a type we’re going to need. So let’s create yet another interface to solve this little problem.
1 2 3 4 |
public interface IKnowActor<TActor> where TActor : ActorBase { IActorRef Ref { get; } } |
A generic parameter, in this case, is just a marker with constraint to ensure that it will always be of a type that is also an actor. Why we’re doing that? It’s quite simple. In C# we can implement interfaces explicitly and by doing that we’re allowed to declare methods and properties with exactly the same signature (return type, parameters and name). How it’ll look inside of our container?
1 2 3 4 5 6 |
public class ActorSystemContainer : IContainActorSystem, IKnowActor<UsersManagerActor> { public ActorSystem System { get; private set; } private readonly IActorRef usersManagerActorRef; IActorRef IKnowActor<UsersManagerActor>.Ref => usersManagerActorRef; } |
So now every one of our IActorRef to an actor created inside of a container is paired with an explicitly implemented getter. We can have as many of them as we want and they all will differ only by generic parameter.
How will we inject our IActorRef and send a message to it now? As soon as we’ll bind it in our IoC(we’ll get there soon!) we should be able to do something like that.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
public class SomeClass { private readonly IKnowActor<UsersManagerActor> usersManagerActorContainer; public SomeClass(IKnowActor<UsersManagerActor> usersManagerActorContainer) { this.usersManagerActorContainer = usersManagerActorContainer; } public void SendMessage(string msgText) { usersManagerActorContainer.Ref.Tell(new SomeMesage(msgText)); } } |
As you can see it really is pretty simple and as long as you know the type of actor you want to send a message to, everything will go great. And to be honest I think you should always know about the destination of your message (unless you’re using publisher/subscriber pattern).
And how to create and configure Actor System in our Dependency Injection container? In Ninject and any other DI I’ve worked with there is always a possibility to bind something in the scope of a singleton. Which means a lifetime of instances of class bounded this way is the same as kernel or container itself. In Ninject we should call InSingletonScope() method during binding.
1 2 3 4 5 6 |
//... other bindings kernel.Bind<ActorModel.Contracts.IContainActorSystem>() .To<ActorModel.ActorSystemContainer>() .InSingletonScope() .WithConstructorArgument("actorSystem", ActorSystem.Create(Me20.ActorModel.ActorPathsHelper.ActorSystemName)); |
And just a few lines below we need to bind each of our top-level actors references like that.
1 |
kernel.Bind<IKnowActor<UsersManagerActor>>().ToMethod(ctx => ctx.Kernel.Get<ActorModel.ActorSystemContainer>()); |
Not that I’m using ToMethod() method here. I’m doing that because if I’ll just bind an interface to type Ninject would create a new instance of ActorSystemContainer each time we would inject IKnowActor anywhere. Doing things this way ensures that every time we’ll get a single instance from our IoC.
As you can see it really isn’t hard. I’m using this for some time now and it gets the job done. If you using any other way of doing things like that or have any ideas how to do this – please share your thoughts in comments.