Akka.net: IS there a simple example using asp.net core with Dependency injection and actors with constructor dependencies?

Created on 18 May 2018  路  10Comments  路  Source: akkadotnet/akka.net

IS there a simple example using asp.net core with standard asp.net core Dependency injection and actors with constructor dependencies?

Most helpful comment

@paulvanbladel I've been playing around with this setup. Perhaps it may help you some:

Startup.cs

```c#
public IServiceProvider ConfigureServices(IServiceCollection services)
{
services.AddMvc().AddControllersAsServices();
services.AddSignalR();

var builder = new ContainerBuilder(); // autofac
builder.Populate(services);

var actorSystem = CreateActorSystem();
builder.RegisterInstance(actorSystem)
            .ExternallyOwned();
builder.RegisterType<CaptureActorFactory>()
    .As<ICaptureActorFactory>() //<<<--- This is what's injected into Controller ctor
    .SingleInstance();
builder.RegisterType<AkkaManager>()
    .WithAttributeFiltering()
    .SingleInstance();

ApplicationContainer = builder.Build();

return new AutofacServiceProvider(ApplicationContainer);

}

### abstract ActorFactory with a static dictionary of lazy actor IActorRef
```c#
public abstract class ActorFactoryBase
{
    private static readonly Dictionary<object, Lazy<IActorRef>> _Actors = 
        new Dictionary<object, Lazy<IActorRef>>();

    public ActorFactoryBase(ActorSystem actorSystem, IServiceProvider provider)
    {
        ActorSystem = actorSystem;
        ServiceProvider = provider;

        CreateDefaultsFromConfig();
    }

    protected ActorSystem ActorSystem { get; }

    protected IServiceProvider ServiceProvider { get; }

    protected void Register(object key, Func<IActorRef> actorFactory)
    {
        _Actors.Add(key, new Lazy<IActorRef>(actorFactory));
    }

    protected IActorRef Resolve(object key)
    {
        if (!_Actors.ContainsKey(key))
            throw new Exception($"No actor found for key '{key}'.");

        return _Actors[key].Value;
    }

    private void CreateDefaultsFromConfig()
    {
        Register(ActorNames.ListenerRouter, () => ActorSystem.ActorOf(Props.Empty.WithRouter(FromConfig.Instance), ActorNames.ListenerRouter));
    }
}

autofac can create the CaptureActorFactory

This is a poor-mans DI. Register all your related actor funcs. Setup properties to easily resolve them. Here you have access to your ActorSystem and your IServiceProvider if you need to resolve non Akka dependencies with your own DI container.
```c#
public class CaptureActorFactory : ActorFactoryBase, ICaptureActorFactory
{
public CaptureActorFactory(ActorSystem actorSystem, IServiceProvider provider)
: base(actorSystem, provider)
{
Register(ActorNames.CaptureRepository, () =>
ActorSystem.ActorOf(Props.Create(() =>
new CaptureRepository()), ActorNames.CaptureRepository));

    Register(ActorNames.CaptureManager, () => 
        ActorSystem.ActorOf(Props.Create<CaptureManager>(() => 
            new CaptureManager(Resolve(ActorNames.ListenerRouter), Resolve(ActorNames.CaptureRepository))), ActorNames.CaptureManager));
}

public IActorRef CaptureManager => Resolve(ActorNames.CaptureManager);

public IActorRef CaptureRepository => Resolve(ActorNames.CaptureRepository);

}

### inject your custom factory for your actors in your controller or signalr hub
```c#
public class CaptureHub : Hub
{
    private readonly IActorRef _captureManager;

    public CaptureHub(ICaptureActorFactory captureActorFactory)
    {
        _captureManager = captureActorFactory.CaptureManager;
    }

    public Task TryCapture(string someValue)
    {
        _captureManager.Tell(new InitiateCaptureRequest(someValue), ActorRefs.Nobody);

        return Task.CompletedTask;
    }
}

I'd so much rather be able to inject an IActorRef into a ctor and have it resolved via some kind of named or keyed resolution. e.g. Autofac supports ctor([KeyFilter(<someActorKey>)]IActorRef someActor

The code above is rather simple, but so is my test application. Dependency Lifecycle is not taken into consideration as static Dictionary with Lazy<T> will make all my actors singletons. For now that's what I need, but future consideration may be to separate a singleton dictionary and a transient one.

All 10 comments

No there is not. Asp.net core DI is not supported atm.

@Danthar I'm confused, there seems to be a nuget package akk.net.DI.core.
Maybe Core is meant here like "common" for the other DI containers like autofac?

Any idea about timeframe for support for .net core?
image

Thats is, as u surmised, just a supporting package for building akka.di integrations on top of. It contains nothing more then a few base classes.

We have no timeframe for supporting DI as it is implemented in .netcore

Wish I had found this issue yesterday. Spent all day racking my brain trying to get Akka to work nicely with autofac / aspnet core. Would love to see this supported.

@Cephei join the club :) I spent almost a whole week.

@paulvanbladel I've been playing around with this setup. Perhaps it may help you some:

Startup.cs

```c#
public IServiceProvider ConfigureServices(IServiceCollection services)
{
services.AddMvc().AddControllersAsServices();
services.AddSignalR();

var builder = new ContainerBuilder(); // autofac
builder.Populate(services);

var actorSystem = CreateActorSystem();
builder.RegisterInstance(actorSystem)
            .ExternallyOwned();
builder.RegisterType<CaptureActorFactory>()
    .As<ICaptureActorFactory>() //<<<--- This is what's injected into Controller ctor
    .SingleInstance();
builder.RegisterType<AkkaManager>()
    .WithAttributeFiltering()
    .SingleInstance();

ApplicationContainer = builder.Build();

return new AutofacServiceProvider(ApplicationContainer);

}

### abstract ActorFactory with a static dictionary of lazy actor IActorRef
```c#
public abstract class ActorFactoryBase
{
    private static readonly Dictionary<object, Lazy<IActorRef>> _Actors = 
        new Dictionary<object, Lazy<IActorRef>>();

    public ActorFactoryBase(ActorSystem actorSystem, IServiceProvider provider)
    {
        ActorSystem = actorSystem;
        ServiceProvider = provider;

        CreateDefaultsFromConfig();
    }

    protected ActorSystem ActorSystem { get; }

    protected IServiceProvider ServiceProvider { get; }

    protected void Register(object key, Func<IActorRef> actorFactory)
    {
        _Actors.Add(key, new Lazy<IActorRef>(actorFactory));
    }

    protected IActorRef Resolve(object key)
    {
        if (!_Actors.ContainsKey(key))
            throw new Exception($"No actor found for key '{key}'.");

        return _Actors[key].Value;
    }

    private void CreateDefaultsFromConfig()
    {
        Register(ActorNames.ListenerRouter, () => ActorSystem.ActorOf(Props.Empty.WithRouter(FromConfig.Instance), ActorNames.ListenerRouter));
    }
}

autofac can create the CaptureActorFactory

This is a poor-mans DI. Register all your related actor funcs. Setup properties to easily resolve them. Here you have access to your ActorSystem and your IServiceProvider if you need to resolve non Akka dependencies with your own DI container.
```c#
public class CaptureActorFactory : ActorFactoryBase, ICaptureActorFactory
{
public CaptureActorFactory(ActorSystem actorSystem, IServiceProvider provider)
: base(actorSystem, provider)
{
Register(ActorNames.CaptureRepository, () =>
ActorSystem.ActorOf(Props.Create(() =>
new CaptureRepository()), ActorNames.CaptureRepository));

    Register(ActorNames.CaptureManager, () => 
        ActorSystem.ActorOf(Props.Create<CaptureManager>(() => 
            new CaptureManager(Resolve(ActorNames.ListenerRouter), Resolve(ActorNames.CaptureRepository))), ActorNames.CaptureManager));
}

public IActorRef CaptureManager => Resolve(ActorNames.CaptureManager);

public IActorRef CaptureRepository => Resolve(ActorNames.CaptureRepository);

}

### inject your custom factory for your actors in your controller or signalr hub
```c#
public class CaptureHub : Hub
{
    private readonly IActorRef _captureManager;

    public CaptureHub(ICaptureActorFactory captureActorFactory)
    {
        _captureManager = captureActorFactory.CaptureManager;
    }

    public Task TryCapture(string someValue)
    {
        _captureManager.Tell(new InitiateCaptureRequest(someValue), ActorRefs.Nobody);

        return Task.CompletedTask;
    }
}

I'd so much rather be able to inject an IActorRef into a ctor and have it resolved via some kind of named or keyed resolution. e.g. Autofac supports ctor([KeyFilter(<someActorKey>)]IActorRef someActor

The code above is rather simple, but so is my test application. Dependency Lifecycle is not taken into consideration as static Dictionary with Lazy<T> will make all my actors singletons. For now that's what I need, but future consideration may be to separate a singleton dictionary and a transient one.

@Cephei
Daniel, Amazing !
Thank you so much for sharing

Just a small update:

1. Ensure your aspnet ApplicationContainer does not dispose of your ActorSystem.

```c#
var actorSystem = CreateActorSystem();
builder.RegisterInstance(actorSystem)
.ExternallyOwned(); // don't dispose this instance!

.... Configure() ....
appLifetime.ApplicationStopped.Register(() => ApplicationContainer.Dispose());
// won't dispose your ActorSystem if it's IDisposable.

### 2. Make your abstract ActorFactoryBase only create your defaults once. You will likely have multiple factories that implement this; thus the ctor will call CreateDefaults multiple times.
```c#
public abstract class ActorFactoryBase
{
    private static readonly SemaphoreSlim _defaultsMutex = new SemaphoreSlim(1, 1);

    private static volatile bool _defaultsCreated = false;

    private static readonly Dictionary<object, Lazy<IActorRef>> _Actors = 
        new Dictionary<object, Lazy<IActorRef>>();        

    public ActorFactoryBase(ActorSystem actorSystem, IServiceProvider provider)
    {
        ActorSystem = actorSystem;
        ServiceProvider = provider;

        EnsureOnlyOnce(CreateDefaults);
    }

    protected ActorSystem ActorSystem { get; }

    protected IServiceProvider ServiceProvider { get; }

    protected void Register(object key, Func<IActorRef> actorFactory)
    {
        _Actors.Add(key, new Lazy<IActorRef>(actorFactory));
    }

    protected IActorRef Resolve(object key)
    {
        if (!_Actors.ContainsKey(key))
            throw new Exception($"No actor found for key '{key}'.");

        return _Actors[key].Value;
    }

    private void CreateDefaults()
    {
        Register(ActorNames.Router, () => 
            ActorSystem.ActorOf(Props.Empty.WithRouter(FromConfig.Instance), ActorNames.Router));
    }

    private void EnsureOnlyOnce(Action createDefaults)
    {
        if (_defaultsCreated)
            return;

        _defaultsMutex.Wait();
        if (!_defaultsCreated)
        {
            createDefaults();
            _defaultsCreated = true;
        }
        _defaultsMutex.Release();
    }
}

@Cephei
I still did not get your solution, i want to pass the repository to actor ctor, i'm struggling with that.

so how about a .net core worker application ? Is Integration of worker application quiet simple with akka.net ?

Was this page helpful?
0 / 5 - 0 ratings