Dependency Injection in Akka .NET

If you’ve read yesterday’s article, “Actor Logging with Akka .NET“, you’ll know that Akka .NET is pretty flexible in terms of the logging frameworks it supports. It uses an adapter so you can pretty much plug in whatever logging framework you prefer.

Akka .NET’s approach to dependency injection is no different. Instead of imposing a particular framework or using its own, Akka .NET lets you plug in a selection of popular third party dependency injection frameworks and use them via a common interface.

The source code for this article is available at the Gigi Labs BitBucket repository.

Example Application

To show how to work with dependency injection (DI), we’ll set up a simple example. No need to get fancy here. All we need is a way to create a parent actor (directly from the ActorSystem) and child actor (from the parent actor). These should have a dependency injected in the constructor.

Here’s the interface for our dependency. I told you, nothing fancy.

    public interface ILazyAss
    {
        void DoNothing();
    }

Here’s the concrete implementation that implements this interface.

    public class SheriffBlake : ILazyAss
    {
        public void DoNothing()
        {
            
        }
    }

Before we start setting up our ActorSystem, we need to install the Akka NuGet package:

Install-Package Akka

Now, we need our actors. We don’t really care what they do, as long as they take the ILazyAss dependency so that we can show later that the DI works. Also, the parent actor creates a child actor – currently not using DI.

    public class ParentActor : ReceiveActor
    {
        public ParentActor(ILazyAss lazyAss)
        {
            Console.WriteLine("Parent Actor created!");

            var childActor = Context.ActorOf(
                Props.Create<ChildActor>(lazyAss), "ChildActor");
        }
    }

    public class ChildActor : ReceiveActor
    {
        public ChildActor(ILazyAss lazyAss)
        {
            Console.WriteLine("Child Actor created!");
        }
    }

We can now set up our ActorSystem:

        static void Main(string[] args)
        {
            using (var actorSystem = ActorSystem.Create("MyActorSystem"))
            {
                var lazyAss = new SheriffBlake();
                var actor = actorSystem.ActorOf(
                    Props.Create<ParentActor>(lazyAss), "ParentActor");

                Console.WriteLine("Press ENTER to exit...");
                Console.ReadLine();
            }
        }

Let us now run the code as is and see that it works:

akkanetdi-nodioutput

Setting up DI with Ninject

Like with logging, DI in Akka .NET involves installing the adapter NuGet package for the framework you want, and then configuring it as needed. I’m going to show how to do this with Ninject, but this is an arbitrary choice. Consult the Akka .NET Dependency Injection documentation for more information about how to set up other dependency injection frameworks. Packages for other frameworks not listed there also exist in NuGet.

akkanetdi-diadapters

So first, install the package you need:

Install-Package Akka.DI.Ninject

Next, we set up our IoC container, configure the dependencies we need to resolve, and set it up to work with the ActorSystem:

        static void Main(string[] args)
        {
            var container = new StandardKernel();
            container.Bind<ILazyAss>().To<SheriffBlake>();

            using (var actorSystem = ActorSystem.Create("MyActorSystem"))
            {
                var resolver = new NinjectDependencyResolver(container, actorSystem);

                var lazyAss = new SheriffBlake();
                var actor = actorSystem.ActorOf(
                    Props.Create<ParentActor>(lazyAss), "ParentActor");

                Console.WriteLine("Press ENTER to exit...");
                Console.ReadLine();
            }
        }

We now need to stop directly creating the dependency, and rely on the DI framework instead.

The catch to do this is that you need this funky DI() extension method, for which you need the following using statement:

using Akka.DI.Core;

This DI() extension method can be used from actor systems as well as actor contexts to create actors. Let’s change our actor system to use this, and get rid of the direct creation of our lazy sheriff:

        static void Main(string[] args)
        {
            var container = new StandardKernel();
            container.Bind<ILazyAss>().To<SheriffBlake>();

            using (var actorSystem = ActorSystem.Create("MyActorSystem"))
            {
                var resolver = new NinjectDependencyResolver(container, actorSystem);

                var actor = actorSystem.ActorOf(
                    actorSystem.DI().Props<ParentActor>(), "ParentActor");

                Console.WriteLine("Press ENTER to exit...");
                Console.ReadLine();
            }
        }

Similarly, we can rewire our ParentActor to create the ChildActor via DI (remember to add the using to get access to the DI() extension method):

    public class ParentActor : ReceiveActor
    {
        public ParentActor(ILazyAss lazyAss)
        {
            Console.WriteLine("Parent Actor created!");

            var childActorProps = Context.DI().Props<ChildActor>();
            var childActor = Context.ActorOf(childActorProps, "ChildActor");
        }
    }

And voilĂ :

akkanetdi-dioutput