NHibernate 5: async IO bound operations support



This news went mostly unnoticed but that’s actually a big one, at least for those of us who uses NHibernate as their primary ORM: NHibernate 5 now supports async IO bound operations.

NHibernate 5: async IO bound operations support

This feature is one of the few that NHibernate lacked and other ORMs (Entity Framework, Dapper) didn’t. It’s great to see that this gap is closed now.

If you wonder what’s the deal with async IO bound operations, here’s a quick explanation. When doing IO bound work (such as reading from a file, sending a request via the Internet, etc.), you don’t want to consume threads of your server’s thread pool because this work doesn’t actually involve CPU. Executing such operations on a thread means this thread would just sit idle and wait until the IO operations are finished. To avoid this, you need to delegate this work to the appropriate driver, register a callback with the IO Completion Port, release the working thread, and resume processing on it only after the work is completed and the callback is called. This allows you to increase the application’s throughput and avoid such nasty things as thread starvation (or at least delay it).

The communication with the database falls into this category as your application talks to it via the server’s network adapter. And so it’s a good idea to use async operations for that too.

Entity Framework has been supporting this feature for quite a long time already. Now you can do that with NHibernate as well. All operations that involve IO work have received an async counterpart.

Here’s how you can load an entity from the database:

Get a “shadow” entity with only Id initialized:

Get a list of entities via a LINQ query:

Another one:

Load it using an HQL query:

Note that this feature is not intended for parallelism, only non-parallel asynchrony, so make sure to await each call before issuing a new call using the same session.

In other words, don’t do this:

Do this instead:

A quick side note about parallelism versus asynchrony. Parallel execution is a subset of asynchronous execution: every parallel execution is asynchronous, but not every asynchronous execution is parallel.

Executing something asynchronously corresponds to “without blocking the caller”. It can be achieved by starting this work immediately, which means it will be done in parallel, or by waiting until the caller is finished with their stuff, which means the work will be executed sequentially, either by the same or another thread. Either way, the job will not block the caller and thus we can say that it will be executed asynchronously.

Finally, here’s an example of updating an entity:

Querying entity collections

There’s another interesting feature in this release. Now you can query an entity’s collection without fully loading it, like so:

NHibernate will generate a query which only retrieves the customer’s orders that fit the criteria. Pretty nice.

Conclusion

Great and long-awaited addition to NHibernate. All kudos to Frédéric Delaporte and Alexander Zaytsev aka hazzik.

Share




  • Luka Cetina

    Hi, I have a question regarding this…
    If I do this:
    1. Add an entity async
    2. Get the entity async
    3. Delete the entity async
    The NHibernate Profiler tells me one single Session is beeing used by multiple threads.
    Did they make session object thread safe to allow async or is it a problem of NHibernate Profiler not knowing I’m using async methods?

    • Luka Cetina

      Actually it was a problem in my code. I did change UnitOfWork (from your PS course) code to use Async methods from NH 5.0.

      • http://enterprisecraftsmanship.com/ Vladimir Khorikov

        You mean changed it use async methods but didn’t await those methods?

        • Luka Cetina

          No, I have put await in wrong place. But this was not the issue. The issue was in NHibernate Profiler not tracking Async state. I reported it to Ayende and he created an issue for this: http://issues.hibernatingrhinos.com/issue/UberProf-413 and it will be resolved in version 5.0 of NH Profiler.

        • Luka Cetina

          Anyway can you maybe update your samples (unit of work and repository) to use async methods?

  • Luka Cetina

    Can ypu maybe update your code samples (UnitOfWork and Repository) to use Async methods?

    • http://enterprisecraftsmanship.com/ Vladimir Khorikov

      Do you mean the code from my latest course, https://github.com/vkhorikov/AnemicDomainModel ?

      • Luka Cetina

        Yes, exactly that. It’s awesome

        • http://enterprisecraftsmanship.com/ Vladimir Khorikov

          That should be a pretty simple change, replacing all calls with async ones and making the controller actions return Task of IActionResult instead of just IActionResult.

          • Luka Cetina

            Oh I have done that. I only suggested it for others. Although the course is about another thing.

  • Adrien

    Regarding parallelism, what would you recommend then ? Manually creating somes tasks without using async methods and wait for them to finish ?
    Should we use async inside those parrallel tasks ?

    • Luka Cetina

      Well depends what you want to do. You can do await Task.Run(). Or you can use futures if you want to execute multiple nhibernate queries

    • http://enterprisecraftsmanship.com/ Vladimir Khorikov

      Just make sure that at any time, no two queries using the same session are executed. If you need parallelism, you’d need to create 2 sessions.

  • Andriy

    I always wanted to ask, do you ever use plain sql?

    • http://enterprisecraftsmanship.com/ Vladimir Khorikov

      I do. Mainly in simple scenarios where the use of an ORM is not justified or in read-only scenarios where I need highly optimized SQL queries.

  • Dimitar Lichev

    Hi, the NHibernate documentation on transactions states that ISession is not thread-safe and async work means releasing the thread to the pool and getting another upon task completion, what are your thoughts on this?

    • http://enterprisecraftsmanship.com/ Vladimir Khorikov

      “Not thread-safe” most likely means that you can’t work with a session in parallel from two threads. It doesn’t mean you can’t work with it from two threads back-to-back, in sequence. You will loose the synchronization context when switching from thread to thread in the async scenario but it’s not an issue as this context is not used in DB access scenarios anyway.