Search Results for

    Show / Hide Table of Contents

    Dependency Injection

    As of Akka.NET v1.4.15 we recommend to Akka.NET users adopt the Akka.DependencyInjection library, which integrates directly with Microsoft.Extensions.DependencyInjection and deprecates the previous Akka.DI.Core and Akka.DI.* libraries.

    You can install Akka.DependencyInjection via NuGet:

    PS> Install-Package Akka.DependencyInjection
    

    Akka.DependencyInjection allows users to pass in an IServiceProvider into the ActorSystem before the latter is created, via a new kind of programmatic configuration Setup that was introduced in Akka.NET v1.4

    Integrating with Microsoft.Extensions.DependencyInjection

    Many .NET applications begin with a Startup class that uses the Microsoft.Extensions.DependencyInjection to build an IServiceCollection that contains 1 or more service bindings:

    public void ConfigureServices(IServiceCollection services)
    {
        // set up a simple service we're going to hash
        services.AddScoped<IHashService, HashServiceImpl>();
    
        // creates instance of IPublicHashingService that can be accessed by ASP.NET
        services.AddSingleton<IPublicHashingService, AkkaService>();
    
        // starts the IHostedService, which creates the ActorSystem and actors
        services.AddHostedService(sp => (AkkaService)sp.GetRequiredService<IPublicHashingService>());
    
    }
    

    In this instance, we're going to run Akka.NET behind the scenes as a Microsoft.Extensions.Hosting.IHostedService and in the process, we will pass the IServiceProvider that contains our DI bindings to it:

    public class AkkaService : IPublicHashingService, IHostedService
    {
        private ActorSystem _actorSystem;
        public IActorRef RouterActor { get; private set; }
        private readonly IServiceProvider _sp;
    
        public AkkaService(IServiceProvider sp)
        {
            _sp = sp;
        }
    
        public async Task StartAsync(CancellationToken cancellationToken)
        {
            var hocon = ConfigurationFactory.ParseString(await File.ReadAllTextAsync("app.conf", cancellationToken));
            var bootstrap = BootstrapSetup.Create().WithConfig(hocon);
            var di = DependencyResolverSetup.Create(_sp);
            var actorSystemSetup = bootstrap.And(di);
            _actorSystem = ActorSystem.Create("AspNetDemo", actorSystemSetup);
    

    To incorporate our IServiceProvider into our ActorSystem, we will use a Akka.DependencyInjection.ServiceProviderSetup class to combine the IServiceProvider with the HOCON configuration we're going to use.

    Once the ActorSystem is created, we can easily access the IServiceProvider by calling ServiceProvider.For(ActorSystem):

    // props created via IServiceProvider dependency injection
    var hasherProps = DependencyResolver.For(_actorSystem).Props<HasherActor>();
    RouterActor = _actorSystem.ActorOf(hasherProps.WithRouter(FromConfig.Instance), "hasher");
    

    From there, we want to call ServiceProvider.Props to create a set of Props for our actor, whose arguments can be populated via the services registered with the IServiceProvider:

    public class HasherActor : ReceiveActor
    {
        private readonly ILoggingAdapter _log = Context.GetLogger();
        private readonly IServiceScope _scope;
        private readonly IHashService _hashService;
    
        public HasherActor(IServiceProvider sp)
        {
            _scope = sp.CreateScope();
            _hashService = _scope.ServiceProvider.GetRequiredService<IHashService>();
    
            Receive<string>(str =>
            {
                var hash = _hashService.Hash(str);
                Sender.Tell(new HashReply(hash, Self));
            });
        }
    
        protected override void PostStop()
        {
            _scope.Dispose();
    
            // _hashService should be disposed once the IServiceScope is disposed too
            _log.Info("Terminating. Is ScopedService disposed? {0}", _hashService.IsDisposed);
        }
    }
    
    Note

    Akka.DependencyInjection is not going to manage the lifecycle of your dependencies for you. Keep reading.

    Managing Lifecycle Dependencies with Akka.DependencyInjection

    Akka.DependencyInjection allows Akka.NET developers to mix and match injected dependencies along with non-injected dependencies - for instance:

    public class NonDiArgsActor : ReceiveActor
    {
        private readonly AkkaDiFixture.ISingletonDependency _singleton;
        private readonly IServiceScope _scope;
        private AkkaDiFixture.ITransientDependency _transient;
        private AkkaDiFixture.IScopedDependency _scoped;
        private string _arg1;
        private string _arg2;
    
        public NonDiArgsActor(AkkaDiFixture.ISingletonDependency singleton, IServiceProvider sp, string arg1, string arg2)
        {
            _singleton = singleton;
            _scope = sp.CreateScope();
            _arg1 = arg1;
            _arg2 = arg2;
    
            Receive<FetchDependencies>(_ =>
            {
                Sender.Tell(new CurrentDependencies(new AkkaDiFixture.IDependency[] { _transient, _scoped, _singleton }));
            });
    
            Receive<string>(_ =>
            {
                Sender.Tell(_arg1);
                Sender.Tell(_arg2);
            });
    
            Receive<Crash>(_ => throw new ApplicationException("crash"));
        }
    
        protected override void PreStart()
        {
            _scoped = _scope.ServiceProvider.GetService<AkkaDiFixture.IScopedDependency>();
            _transient = _scope.ServiceProvider.GetRequiredService<AkkaDiFixture.ITransientDependency>();
        }
    
        protected override void PostStop()
        {
            _scope.Dispose();
        }
    }
    

    In this case, the actor accepts:

    1. A singleton scoped service, injected directly via the constructor;
    2. A IServiceProvider instance, which is also populated directly via the DI system; and
    3. Two string arguments that are not provided via dependency injection.

    Here's how Akka.DependencyInjection is used to instantiate this actor via Props:

    var spExtension = DependencyResolver.For(Sys);
    var arg1 = "foo";
    var arg2 = "bar";
    var props = spExtension.Props<NonDiArgsActor>(arg1, arg2);
    
    // create a scoped actor using the props from Akka.DependencyInjection
    var scoped1 = Sys.ActorOf(props, "scoped1");
    

    The ServiceProvider.Props method will accept additional arguments that can be used to instantiate the actor in addition to the arguments that will be provided via your dependency injection container.

    Akka.DependencyInjection does not manage the lifecycle of your dependencies automatically - in fact, Akka.NET recommends that you follow Microsoft's own dependency injection guidelines:

    Use the factory pattern to create an instance outside of the parent scope. In this situation, the app would generally have a Create method that calls the final type's constructor directly. If the final type has other dependencies, the factory can:

    • Receive an IServiceProvider in its constructor.
    • Use ActivatorUtilities.CreateInstance to instantiate the instance outside of the container, while using the container for its dependencies.

    A best practice for working with Akka.NET actors and Microsoft.Extensions.DependencyInjection:

    1. Always provide an IServiceProvider into the constructor of your actors, to use Microsoft's so-called "factory" pattern;
    2. Always use the IServiceProvider to create an IServiceScope;
    3. Use the IServiceScope to create your dependencies; and
    4. Dispose your IServiceScope inside the PostStop method of your actor to ensure orderly disposal of any transient or scoped dependencies your actor may have used.

    You can see an example of an actor that follows this pattern below:

    public class MixedActor : ReceiveActor
    {
        private readonly AkkaDiFixture.ISingletonDependency _singleton;
        private readonly IServiceScope _scope;
        private AkkaDiFixture.ITransientDependency _transient;
        private AkkaDiFixture.IScopedDependency _scoped;
    
        public MixedActor(AkkaDiFixture.ISingletonDependency singleton, IServiceProvider sp)
        {
            _singleton = singleton;
            _scope = sp.CreateScope();
    
            Receive<FetchDependencies>(_ =>
            {
                Sender.Tell(new CurrentDependencies(new AkkaDiFixture.IDependency[] { _transient, _scoped, _singleton }));
            });
    
            Receive<Crash>(_ => throw new ApplicationException("crash"));
        }
    
        protected override void PreStart()
        {
            _scoped = _scope.ServiceProvider.GetService<AkkaDiFixture.IScopedDependency>();
            _transient = _scope.ServiceProvider.GetRequiredService<AkkaDiFixture.ITransientDependency>();
        }
    
        protected override void PostStop()
        {
            _scope.Dispose();
        }
    }
    

    Akka.DI - Deprecated Akka.NET DI Support

    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 integration DI directly into your Akka.NET actors.

    If your actor has a constructor that takes parameters then those need to be part of the Props as well, as described above. But there are cases when a factory method must be used, for example when the actual constructor arguments are determined by a dependency injection framework.

    The basic functionality is provided by a DependencyResolver class, that can create Props using the DI container.

    // Create your DI container of preference
    var someContainer = ... ;
    
    // Create the actor system
    var system = ActorSystem.Create("MySystem");
    
    // Create the dependency resolver for the actor system
    IDependencyResolver resolver = new XyzDependencyResolver(someContainer, system);
    

    When creating actorRefs straight off your ActorSystem instance, you can use the DI() Extension.

    // Create the Props using the DI extension on your ActorSystem instance
    var worker1Ref = system.ActorOf(system.DI().Props<TypedWorker>(), "Worker1");
    var worker2Ref = system.ActorOf(system.DI().Props<TypedWorker>(), "Worker2");
    

    Creating Child Actors Using DI

    When you want to create child actors from within your existing actors using Dependency Injection you can use the Actor Content extension just like in the following example.

    // For example in the PreStart...
    protected override void PreStart()
    {
        var actorProps = Context.DI().Props<MyActor>()
            .WithRouter(/* options here */);
        
        var myActorRef = Context.ActorOf(actorProps, "myChildActor");
    }
    
    Note

    There is currently still an extension method available for the actor Context. Context.DI().ActorOf<>. However this has been officially deprecated and will be removed in future versions.

    Notes

    Warning

    You might be tempted at times to use an IndirectActorProducer which always returns the same instance, e.g. by using a static field. This is not supported, as it goes against the meaning of an actor restart, which is described here: What Restarting Means.

    When using a dependency injection framework, there are a few things you have to keep in mind.

    When scoping actor type dependencies using your DI container, only TransientLifestyle or InstancePerDependency like scopes are supported. This is due to the fact that Akka explicitly manages the lifecycle of its actors. So any scope which interferes with that is not supported.

    This also means that when injecting dependencies into your actor, using a Singleton or Transient scope is fine. But having that dependency scoped per HttpWebRequest for example won't work.

    Techniques for dependency injection and integration with dependency injection frameworks are described in more depth in the Using Akka with Dependency Injection guideline.


    Currently the following Akka.NET Dependency Injection plugins are available:

    AutoFac

    In order to use this plugin, install the Nuget package with Install-Package Akka.DI.AutoFac, then follow the instructions:

    // Create and build your container
    var builder = new Autofac.ContainerBuilder();
    builder.RegisterType<WorkerService>().As<IWorkerService>();
    builder.RegisterType<TypedWorker>();
    var container = builder.Build();
    
    // Create the ActorSystem and Dependency Resolver
    var system = ActorSystem.Create("MySystem");
    system.UseAutofac(container);
    

    CastleWindsor

    In order to use this plugin, install the Nuget package with Install-Package Akka.DI.CastleWindsor, then follow the instructions:

    // Create and build your container
    var container = new WindsorContainer();
    container.Register(Component.For<IWorkerService>().ImplementedBy<WorkerService>());
    container.Register(Component.For<TypedWorker>().Named("TypedWorker").LifestyleTransient());
    
    // Create the ActorSystem and Dependency Resolver
    var system = ActorSystem.Create("MySystem");
    var propsResolver = new WindsorDependencyResolver(container, system);
    

    Ninject

    In order to use this plugin, install the Nuget package with Install-Package Akka.DI.Ninject, then follow the instructions:

    // Create and build your container
    var container = new Ninject.StandardKernel();
    container.Bind<TypedWorker>().To(typeof(TypedWorker));
    container.Bind<IWorkerService>().To(typeof(WorkerService));
    
    // Create the ActorSystem and Dependency Resolver
    var system = ActorSystem.Create("MySystem");
    var propsResolver = new NinjectDependencyResolver(container,system);
    

    Other Frameworks

    Support for additional dependency injection frameworks may be added in the future, but you can easily implement your own by implementing an Actor Producer Extension.

    In this article
    • githubEdit this page
    Back to top
    Contribute
    • Project Chat
    • Discussion Forum
    • Source Code
    Support
    • Akka.NET Support Plans
    • Akka.NET Observability Tools
    • Akka.NET Training & Consulting
    Maintained By
    • Petabridge - The Akka.NET Company
    • Learn Akka.NET