Skip to main content

NHibernate–Lazy loading without a proxy

Yes, it is 2023 and yes I'm still using NHibernate at some of my projects(sorry old habits die hard).

One of the things that is really handy but can bite you in the foot as easily, is lazy loading.

What is lazy loading?

Lazy loading is used to delay the retrieval of related data from a database until it is actually accessed or requested by the application. ORM frameworks, like NHibernate or Entity Framework, map database tables to objects in your programming language, making it easier to work with relational data in an object-oriented manner.

When an ORM employs lazy loading for related data, it means that the ORM does not fetch all the associated data immediately when you query for the main entity. Instead, it loads the related data from the database only when you explicitly access or request that data. This approach helps improve performance by reducing the initial amount of data fetched from the database and minimizing the number of database queries.

Here's an example to illustrate how lazy loading works in the context of ORM:

Let's say you have two entities, Author and Book, with a one-to-many relationship (an author can have multiple books). With lazy loading enabled, if you retrieve an Author object from the database, the ORM might only fetch the author's information and not load the associated books immediately. Only when you access the books property of the Author object will the ORM issue an additional query to fetch the books associated with that author.

Benefits of lazy loading in ORM:

  1. Reduced Data Transfer: Lazy loading minimizes the amount of data transferred from the database to the application initially, which can lead to faster query execution and lower network traffic.

  2. Efficient Resource Usage: Since related data is loaded only when needed, resources like memory and network connections are used more efficiently.

  3. Improved Performance: Loading related data on-demand can lead to faster initial query execution times, which is especially beneficial in scenarios where not all related data is required for every operation.

However, lazy loading can also introduce some challenges:

  1. N+1 Query Problem: If lazy loading is used excessively and not managed properly, it can lead to the "N+1 query problem," where fetching a collection of entities results in N additional queries to load their related data. This can cause performance issues.

  2. Unintentional Overhead: Developers need to be mindful of when and how they access related data to avoid triggering unnecessary database queries and performance bottlenecks.

  3. Complexity: Managing lazy loading and ensuring that related data is loaded appropriately can add complexity to the application code.

To address these challenges, many ORM frameworks provide options for customizing lazy loading behavior, like specifying eager loading (loading related data along with the main entity) or utilizing batch loading techniques to minimize the number of queries. Developers should carefully consider the trade-offs between eager and lazy loading based on their specific application's requirements and performance considerations.

How lazy loading works in NHibernate

In NHibernate lazy loading is enabled by default. To make this work NHibernate uses out-of-the-box a proxy object. This proxy objects sits in between your object and an associated relation(for example between the Author and its Books).

Remark: To make this work NHibernate requires that all properties and methods in your object are declared as virtual if lazy loading is enabled.

Here is an example in NHibernate:

Lazy loading without a proxy

Although the usage of proxies is the default way in NHibernate it is a leaky abstraction and can result to unexpected behavior. For example as explained in this post, the following code will not work as expected:

We are checking the type of what is returned but this is not the object itself but instead a proxy instance.

To avoid this issue, you can change the lazy loading behavior in NHibernate to not using a proxy but fetch the actual object the first time you access the property.

This is how to do it using the conformist mapping:

Or using Fluent NHibernate:

Popular posts from this blog

Podman– Command execution failed with exit code 125

After updating WSL on one of the developer machines, Podman failed to work. When we took a look through Podman Desktop, we noticed that Podman had stopped running and returned the following error message: Error: Command execution failed with exit code 125 Here are the steps we tried to fix the issue: We started by running podman info to get some extra details on what could be wrong: >podman info OS: windows/amd64 provider: wsl version: 5.3.1 Cannot connect to Podman. Please verify your connection to the Linux system using `podman system connection list`, or try `podman machine init` and `podman machine start` to manage a new Linux VM Error: unable to connect to Podman socket: failed to connect: dial tcp 127.0.0.1:2655: connectex: No connection could be made because the target machine actively refused it. That makes sense as the podman VM was not running. Let’s check the VM: >podman machine list NAME         ...

Cache stampede: when our cache turned against us

While investigating some performance issues, we ran into an ASP.NET Core API that cached a fairly expensive aggregation query for 60 seconds. Under normal load, that was fine: one request rebuilds the cache, everyone else reads from it. Under peak load, dozens of requests would arrive in that same expiry window, all see a cache miss, and all fire the same expensive query in parallel. The database didn't like that. That was the moment when our caching layer stopped helping and started hurting. A burst of requests comes in at the same time, all miss the cache, and all go hammer the database or the downstream API at once. That's a cache stampede . The cache was supposed to protect our backend, and for a few hundred milliseconds it did the opposite. Why this happens IMemoryCache.GetOrCreate (and its async sibling) looks like it protects you, but it doesn't add any locking on its own. Look at the naive version: public async Task<Report> GetReportAsync(string key) ...

A complex system designed from scratch never works

A few years ago, I worked as an architect on a big mainframe rewrite. I still count it as one of my failures. Not because the technology was wrong, but because I couldn't convince the management team to simplify the approach. Years later, the organization is still struggling to get the new system up and running. I left the project at the time, because I couldn't put my name behind an approach that would take very long and cost a lot of money without a working system to show for it along the way. Gall’s Law That memory keeps coming back to me, because it's a textbook case of Gall's Law playing out in real life. Gall's Law , from John Gall's Systemantics , states it plainly: A complex system that works is invariably found to have evolved from a simple system that worked. A complex system designed from scratch never works, and it cannot be patched to make it work. You have to start over with a simple system that works. What does that mean in practice,...