Lazy Loading Explained

It took me a while to understand Entity Framework (EF) and lazy loading, but now that I do I wanted to put it down somewhere so I don't forget it. Maybe this will benefit someone else with a similar issue.

When you create entities with EF you are creating self-tracking objects. What this means is that the objects (entities) track their own changes so that EF can later persist those changes back to the data store. EF entities also contain a feature known as lazy loading. What lazy loading does is allow the data to be retrieved from the data store when the collection of objects is iterated. For instance if you have an entity called BlogPost and it has a navigation property called Comments which contains a collection of Comment entities, that Comments property is not actually populated until the moment you iterate on it or read from it.

EFDataContext db = new EFDataContext();
// Get blog post from data store.
BlogPost blogPost = db.BlogPosts.Single(x => x.Id == 1);
// Blog post contains a reference to a collection of Comments. Let's loop through them.
foreach (Comment comment in blogPost.Comments)
    // Only just now as we started looping through this collection did the collection populate with data.
    // That is called lazy loading. Had we not looped this collection, it technically would not have existed.

This concept applies to any IEnumerable<EntityType> that you create in Entity Framework. The situation I ran into the other day happened while playing with function imports. It ended up that we didn't need an entity, but actually needed a complex type. However, while we were still trying to make it work with an Entity we noticed we were getting a funky error when trying to iterate over the collection of custom entity types that were returned by the function import.

"The result of a query cannot be enumerated more than once."

It took us a while to realize that the collection we were iterating over was being lazy loaded. This meant that it was trying to pull the data from the data store, but because it was a function import it could not perform that operation. The solution was to add a .ToList() onto the end of the collection we were enumerating. What this does is it creates a list object in memory and populates it with the results of the function import. Now that we had an in-memory collection we could iterate it as many times as we wanted without any fear of lazy loading.

It's that simple, but keep in mind that adding .ToList() is not always a good practice, especially for large collections as it pulls all that data into memory.

Happy coding!