ASP.NET Core
The Bridge
When deploying Akka.NET in ASP.NET Core, one major concern is how to expose actor
in an ASP.NET Core controllers. We will design an interface
for this!
namespace Akka.AspNetCore
{
public interface IActorBridge
{
void Tell(object message);
Task<T> Ask<T>(object message);
}
}
Akka.NET with IHostedService
With the IActorBridge
created, next is to host Akka.NET with IHostedService
which will also implement the IActorBridge
:
using Akka.Actor;
using Akka.DependencyInjection;
namespace Akka.AspNetCore
{
public class AkkaService : IHostedService, IActorBridge
{
private ActorSystem _actorSystem;
private readonly IConfiguration _configuration;
private readonly IServiceProvider _serviceProvider;
private IActorRef _actorRef;
private readonly IHostApplicationLifetime _applicationLifetime;
public AkkaService(IServiceProvider serviceProvider, IHostApplicationLifetime appLifetime, IConfiguration configuration)
{
_serviceProvider = serviceProvider;
_applicationLifetime = appLifetime;
_configuration = configuration;
}
public async Task StartAsync(CancellationToken cancellationToken)
{
var bootstrap = BootstrapSetup.Create();
// enable DI support inside this ActorSystem, if needed
var diSetup = DependencyResolverSetup.Create(_serviceProvider);
// merge this setup (and any others) together into ActorSystemSetup
var actorSystemSetup = bootstrap.And(diSetup);
// start ActorSystem
_actorSystem = ActorSystem.Create("akka-universe", actorSystemSetup);
_actorRef = _actorSystem.ActorOf(Worker.Prop(), "heavy-weight-word");
// add a continuation task that will guarantee shutdown of application if ActorSystem terminates
//await _actorSystem.WhenTerminated.ContinueWith(tr => {
// _applicationLifetime.StopApplication();
//});
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
_actorSystem.WhenTerminated.ContinueWith(_ => {
_applicationLifetime.StopApplication();
});
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
await Task.CompletedTask;
}
public async Task StopAsync(CancellationToken cancellationToken)
{
// strictly speaking this may not be necessary - terminating the ActorSystem would also work
// but this call guarantees that the shutdown of the cluster is graceful regardless
await CoordinatedShutdown.Get(_actorSystem).Run(CoordinatedShutdown.ClrExitReason.Instance);
}
public void Tell(object message)
{
_actorRef.Tell(message);
}
public Task<T> Ask<T>(object message)
{
return _actorRef.Ask<T>(message);
}
}
}
Interaction Between Controllers and Akka.NET
using Microsoft.AspNetCore.Mvc;
// For more information on enabling Web API for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860
namespace Akka.AspNetCore.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class AkkaController : ControllerBase
{
private readonly ILogger<AkkaController> _logger;
private readonly IActorBridge _bridge;
public AkkaController(ILogger<AkkaController> logger, IActorBridge bridge)
{
_logger = logger;
_bridge = bridge;
}
[HttpGet]
public Task<IEnumerable<string>> Get()
{
return _bridge.Ask<IEnumerable<string>>("get");
}
// POST api/<AkkaController>
[HttpPost]
public void Post([FromBody] string value)
{
_bridge.Tell(value);
}
}
}
Wire up Akka.NET and ASP.NET Core
We need to replace the default IHostedService
with AkkaService
and also inject IActorBridge
.
using Akka.AspNetCore;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
// creates instance of IPublicHashingService that can be accessed by ASP.NET
builder.Services.AddSingleton<IActorBridge, AkkaService>();
// starts the IHostedService, which creates the ActorSystem and actors
builder.Services.AddHostedService<AkkaService>(sp => (AkkaService)sp.GetRequiredService<IActorBridge>());
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseAuthorization();
app.MapControllers();
app.Run();
Note
Visit our site's blog post for Best Practices for Integrating Akka.NET with ASP.NET Core and SignalR