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:

Customer customer = await session.GetAsync<Customer>(1);

Get a "shadow" entity with only Id initialized:

Customer customer = await session.LoadAsync<Customer>(1);

Get a list of entities via a LINQ query:

List<Customer> customers = await session.Query<Customer>().ToListAsync();

Another one:

 Customer customer = await session
    .Query<Customer>()
    .Where(x => x.Name.Contains("Customer 1"))
    .SingleOrDefaultAsync();

Load it using an HQL query:

Customer customer = await session.CreateQuery("from Customer where Id = :Id")
    .SetParameter("Id", 1)
    .UniqueResultAsync<Customer>();

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:

Task<Customer> task1 = session.GetAsync<Customer>(1);
Task<Customer> task2 = session.GetAsync<Customer>(2);

Customer[] customers = await Task.WhenAll(task1, task2);

Do this instead:

Customer customer1 = await session.GetAsync<Customer>(1);
Customer customer2 = await session.GetAsync<Customer>(2);

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:

using (ISession session = sessionFactory.OpenSession())
using (ITransaction transaction = session.BeginTransaction())
{
    Customer customer = await session.GetAsync<Customer>(1);
    customer.Name = "Customer 3";
    await session.SaveOrUpdateAsync(customer);
    await transaction.CommitAsync();
}

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:

List<Order> orders = await customer.Orders
    .AsQueryable()
    .Where(x => x.Amount > 10)
    .ToListAsync();

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.

Subscribe


I don't post everything on my blog. Don't miss smaller tips and updates. Sign up to my mailing list below.

Comments


comments powered by Disqus