Skip to content

Latest commit

 

History

History
195 lines (156 loc) · 6.72 KB

di-core.md

File metadata and controls

195 lines (156 loc) · 6.72 KB
uid title
di-core
DI Core

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);
}