Akka.NET is quite opinionated in terms of persistence. Paraphrasing Henry Ford’s famous quote: You can persist your data any way you want in Akka.NET, so long as you use event sourcing. But what if you don’t want to? Perhaps the problem you are trying to solve doesn’t overlap with this particular mental model, and yet you think you can still gain a lot by leveraging Akka. Then you have to bake your own solution. In this blog post, I would like to show you one way of integrating Akka.NET with an ORM. To keep things on the bleeding edge, we will use Entity Framework Core.
DbContext in Akka.NET - naive solution
If you are new to Akka.NET, you may wonder, why using Entity Framework - or another ORM of your choice - with Actors is worth writing an article about? It should be as simple as that. You want to persist the state of your actor to database? Just give me a reference to
DbContext, and I am ready to go. Right? Well, technically yes, but…
Let’s consider the following example:
At first glance, this code might look fine. We just took
BooksManagerActor from How to integrate Akka.NET and ASP.NET Core example app and make it operate on
DbContext instead of an in-memory collection. This code works - you can try it if you like to - but has some fundamental flaws.
The first flaw - the one that I would like to dissect in this article - is in the way
BookstoreContext is obtained by our actor. If you’ve ever worked with ASP.NET, you’ve probably written dozens of services or controllers which take this kind of dependency to perform their work. In ASP.NET it’s perfectly fine to use
DbContext this way because
DbContext’s lifecycle scope is defined on the boundaries of a single web request. In the Akka.NET world, however, things are quite different. Due to the nature of actors which can - in theory - live forever, each dependency you inject will live as long as your actor lives.
BooksManagerActor is configured to sit in memory until our app is running, and the same applies to
DbContext it uses - it effectively becomes a singleton. This is dangerous because
DbContext was not designed to be a long living object. It caches data and gets stale pretty soon. Moreover, it is not thread-safe, so if you decide to pass it to child actors, you are just asking for troubles.
There’s no doubt that we have to find a better way of managing
DbContext dependency, if we want to use Entity Framework with our actors. One possible solution would be to create an instance of
DbContext whenever we want to perform some database operation.
It should work, and it might actually solve our problem, but it is not a particularly elegant solution, to be fair. To make this possible we have to override
OnConfiguring method of
DbContext, and therefore make it dependent on concrete database provider implementation. This is a code smell, which directly violates the Dependency Inversion Principle.
Uncle Bob wouldn’t be happy, and besides, we would still have to find a way how to feed our
BookstoreContext with a connection string. Not to mention that this design would make testing of our actors into a nightmare.
Scoped Lifetime to the rescue
DbContext available for our actors and leverage its full capabilities, e.g., context pooling or built-in configuration, we will use interface
IServiceScopeFactory. ASP.NET Core hosting middleware uses this interface under the hood to create a scope for DI injection, and wraps each request in using statement:
To use Entity Framework safely in your actors, you should apply the same pattern. By passing
IServiceScopeFactory to your actors, and using it to create scope inside actors’ receive functions, you would have control over the lifecycle of your dependencies, including
In this example, we are creating
IServiceScope exposes scoped
IServiceProvider which allows us to resolve
BookstoreContext to perform our database work. By default,
DbContext is registered with scoped lifecycle in DI container. This way we don’t have to explicitly dispose it when we don’t need it anymore.
IServiceScope takes care of all the necessary clean up work on our behalf.
With DbContext pooling enabled this cleanup work results in returning DbContext to the pool for further usage.
The downside of this solution is that it is quite cumbersome, especially if you decide to delegate database operations to actors which reside at the bottom of your actor hierarchy. You will have to pass
IServiceScopeFactory all the way down. Wouldn’t it be great if we have a mechanism to enhance our actors with the option to create a service scope on demand? Actually, we have this mechanism, and it is called Akka Extensions.
Akka Extensions is a very simple, yet powerful concept. It is a general extensibility point you can use if you want to provide new features to Akka. It is heavily utilized by packages like
Akka.Cluster. In our example we will use is to fit our actor with
To build Akka Extension, you have to implement 2 interfaces:
The first one is used to signify an object as an
ActorSystem extension. As a marker interface, it doesn’t define any methods or properties for us to implement, but this is the place where we put our logic in.
So let’s define what
ServiceScopeExtension should do:
Then we need to implement
IExtensionId contact. This is the interface containing methods for creating, registering and retrieving extensions withing
ActorSystem. It may seem a bit complicated, luckily, to make our lives easier, Akka team prepared a basic implementation which takes care of all the intricacies for us. All we have to do is to inherit from
ExtensionIdProvider<T> and override one method:
These two components are theoretically all we need to use our extension. However, if you look at the code you need to write to actually use it, then… Let’s say that Akka Extension API is not particularly user-friendly. It has some rough edges. That’s why it is considered a good practice to use C# extension methods to give it a better look and feel. In our case, we need two extension methods: the first one to initialize extension, and the second to create
Here they are:
In order to make our extension available at runtime, we have to go to the
Startup.cs file and change the code we use to register our
Finally, we are ready to use our extension inside
If you are using
DbContext in every
Receive block (like it is in our example), you may be tempted to override
AroundReceive method from
ActorBase class to reduce the amount of boilerplate code.
As a result, your
Receive functions may look like this:
Don’t do it! If you try to run the app with this changes applied, you will see that it doesn’t work anymore. It’s because of the way how
ReceiveAsync is implemented. Every time your
ReceiveAsync handler is invoked, Akka wraps it into a separate
Task. To enforce one message at a time processing strategy, actor’s mailbox is suspended until the task completes. It’s a bit like cheating, but it works for most of the time. At least until you start playing with
AroundReceive, which isn’t an async method and simply returns before your handler does its async work.
So, we have completed the refactoring effort to find a safe way of using
DbContext inside our actors. In this blog post, you learned what is the most common problem you may face when trying to use Entity Framework Core in Akka.NET application. We solved it by utilizing
IServiceScopeFactory and building Akka Extension around it.
In the next article, we will explore this subject further, and create our own Entity Framework Core based version of Persistent Actor. Stay tuned.
You can find the source code of the example application on GitHub. If you have any questions or some points are not clear to you, please, don’t hesitate to leave me a comment below!
Subscribe to Havret on Software
Get the latest posts delivered right to your inbox