Akka.DI.Core
Warning
As of Akka.NET v1.4.15, Akka.DI.Core and all of the libraries that implement it are deprecated. Going forward Akka.NET users are encouraged to use the Akka.DependencyInjection library instead, which uses the Microsoft.Extensions.DependencyInjection interfaces to integrate DI directly into your Akka.NET actors.
Actor Producer Extension library is used to create a Dependency Injection Container for the Akka.NET framework.
What Is It?
Akka.DI.Core is an ActorSystem extension library for the Akka.NET framework that provides a simple way to create an Actor Dependency Resolver that can be used an alternative to the basic capabilities of Props when you have actors with multiple dependencies.
How Do You Create an Extension?
- Create a new class library
- Reference your favorite IoC Container, the Akka.DI.Core, and of course Akka
- Create a class and implement the
IDependencyResolver
Let's walk through the process of creating one for the CastleWindsor container.
You need to create a new project named Akka.DI.CastleWindsor with all the necessary references including Akka.DI.Core, Akka, and CastleWindsor.
Name the initial class WindsorDependencyResolver
.
public class WindsorDependencyResolver : IDependencyResolver
{
// we'll implement IDependencyResolver in the following steps
}
Add a constructor and private fields.
private IWindsorContainer _container;
private ConcurrentDictionary<string, Type> _typeCache;
private ActorSystem _system;
public WindsorDependencyResolver(IWindsorContainer container, ActorSystem system)
{
if (system == null) throw new ArgumentNullException("system");
if (container == null) throw new ArgumentNullException("container");
_container = container;
_typeCache = new ConcurrentDictionary<string, Type>(StringComparer.InvariantCultureIgnoreCase);
_system = system;
_system.AddDependencyResolver(this);
}
You have defined three private fields
IWindsorContainer _container
is a reference to the CastleWindsor container.ConcurrentDictionary<string, Type> _typeCache
is a thread safe map that contains actor name/type associations.ActorSystem _system
is a reference to the ActorSystem.
First you need to implement GetType
. This is a basic implementation and
is just for demonstration purposes. Essentially this is used by the extension
to get the type of the actor from it's type name.
public Type GetType(string actorName)
{
_typeCache.
TryAdd(actorName,
actorName.GetTypeValue() ??
_container.Kernel
.GetAssignableHandlers(typeof(object))
.Where(handler => handler.ComponentModel.Name.Equals(actorName, StringComparison.InvariantCultureIgnoreCase))
.Select(handler => handler.ComponentModel.Implementation)
.FirstOrDefault());
return _typeCache[actorName];
}
Secondly you need to implement the CreateActorFactory
method which will be
used by the extension to create the actor. This implementation will depend upon
the API of the container.
public Func<ActorBase> CreateActorFactory(Type actorType)
{
return () => (ActorBase)container.Resolve(actorType);
}
Thirdly, you implement the Create<TActor>
which is used register the
Props
configuration for the referenced actor type with the ActorSystem
.
This method will always be the same implementation.
public Props Create<TActor>() where TActor : ActorBase
{
return system.GetExtension<DIExt>().Props(typeof(TActor).Name);
}
Lastly, you implement the Release method which, in this instance, is very simple. This method is used to remove the actor from the underlying container.
public void Release(ActorBase actor)
{
this.container.Release(actor);
}
Note: For further details on the importance of the release method please read the following blog post.
The resulting class should look similar to the following:
public class WindsorDependencyResolver : IDependencyResolver
{
private IWindsorContainer container;
private ConcurrentDictionary<string, Type> typeCache;
private ActorSystem system;
public WindsorDependencyResolver(IWindsorContainer container, ActorSystem system)
{
if (system == null) throw new ArgumentNullException("system");
if (container == null) throw new ArgumentNullException("container");
this.container = container;
typeCache = new ConcurrentDictionary<string, Type>(StringComparer.InvariantCultureIgnoreCase);
this.system = system;
this.system.AddDependencyResolver(this);
}
public Type GetType(string actorName)
{
typeCache.TryAdd(actorName, actorName.GetTypeValue() ??
container.Kernel
.GetAssignableHandlers(typeof(object))
.Where(handler => handler.ComponentModel.Name.Equals(actorName, StringComparison.InvariantCultureIgnoreCase))
.Select(handler => handler.ComponentModel.Implementation)
.FirstOrDefault());
return typeCache[actorName];
}
public Func<ActorBase> CreateActorFactory(Type actorType)
{
return () => (ActorBase)container.Resolve(actorType);
}
public Props Create<TActor>() where TActor : ActorBase
{
return system.GetExtension<DIExt>().Props(typeof(TActor));
}
public void Release(ActorBase actor)
{
this.container.Release(actor);
}
}
Now, with the preceding class, you can do something like the following example:
// Setup CastleWindsor
IWindsorContainer container = new WindsorContainer();
container.Register(Component.For<IWorkerService>().ImplementedBy<WorkerService>());
container.Register(Component.For<TypedWorker>().Named("TypedWorker").LifestyleTransient());
// Create the ActorSystem
using (var system = ActorSystem.Create("MySystem"))
{
// Create the dependency resolver
IDependencyResolver resolver = new WindsorDependencyResolver(container, system);
// Register the actors with the system
system.ActorOf(system.DI().Props<TypedWorker>(), "Worker1");
system.ActorOf(system.DI().Props<TypedWorker>(), "Worker2");
// Create the router
IActorRef router = system.ActorOf(Props.Empty.WithRouter(new ConsistentHashingGroup(config)));
// Create the message to send
TypedActorMessage message = new TypedActorMessage
{
Id = 1,
Name = Guid.NewGuid().ToString()
};
// Send the message to the router
router.Tell(message);
}