Migrating from LINQ to SQL to Entity Framework: Deferred Loading
Last week, we covered Eager Loading and how to migrate LINQ to SQL based code that took advantage of Eager Loading. This week, we discuss Deferred Loading.
Generally speaking, queries only retrieve the objects you request, and not their related objects. Deferred Loading (or Lazy Loading) allows you to automatically load objects on demand if and when you attempt to access the object that is not yet loaded (you might do this by trying to access a property that allows you to get to the object you are interested in, for instance).
Automatic Deferred Loading has its advantages and disadvantages – automatic deferred loading that is on by default means more productivity and less code to write. However, it also means less control over when and how you hit the database. In LINQ to SQL, Automatic Deferred Loading is enabled by default, but it can be disabled by using the following code fragment. For more information, see Deferred versus Immediate Loading (LINQ to SQL).
db.DeferredLoadingEnabled = false;
In the example below, you are really only querying for objects from the Products table – however, because Deferred Loading is enabled, you can access prod.SalesOrderDetail (which automatically fetches the associated SalesOrderDetail values from the database, and surfaces the results as SalesOrderDetail objects for the particular product)
var products = from prod in db.Products
where prod.Color == "Blue"
select prod;
foreach (Product prod in products)
{
foreach (SalesOrderDetail detail in prod.SalesOrderDetail)
{
// do something with detail
}
}
The Entity Framework does not provide an automatic mechanism for deferred loading. However, a simple addition to the code above achieves the same result.
var products = from prod in db.Product
where prod.Color == "Blue"
select prod;
foreach (Product prod in products)
{
// avoids unnecessary queries
if (!prod.SalesOrderDetail.IsLoaded)
{
prod.SalesOrderDetail.Load();
}
foreach (SalesOrderDetail detail in prod.SalesOrderDetail)
{
// do something with detail
}
}
For more information, see Navigation Properties. Also see Jaroslaw Kowalski’s blog series Transparent Lazy Loading for Entity Framework which discusses the EFLazyClassGen samples available on CodeGallery. EFLazyClassGen could also be extended to support a Link<T> style deferred loading of individual properties.
In our next post, we will look at Stored Procedures. Stay tuned and let us know what you think!
- The ADO.NET Team
Comments
Anonymous
October 16, 2008
The comment has been removedAnonymous
October 19, 2008
Very good argument Stephen. You should write an article about this instead of a comment to someone else's.Anonymous
October 21, 2008
Thanks Gregory, I did write an article about it on my personal site back when I was still angry over the whole thing, but I was a little harsher in that article (strong language warning): http://reddnet.net/code/ado-net-entity-framework-impressive-powerful-useless/ I do make a point to express the same concerns whenever I see the topic come up and it seems like it might be relevant to the discussion. I might complain loudly, but I am confident that the data team at Microsoft is hearing this same message, and I am confident that they will fix this issue up in the next major release. But just in case, I keep complaining :PAnonymous
November 03, 2008
In 2001 we first demonstrated Object Relational Mapping (ORM) technology for .NET – ObjectSpaces . HoweverAnonymous
January 05, 2009
There is no way we'll use this if we have to check everything to see if a property is loaded or not. The team better make some changes to that or this thing is DOA. I can't believe they would make such a huge mistake.Anonymous
January 09, 2009
I believe eager and lazy loading should and need to be possible by developers expressing their intent prior to accessing attributes of objects. I want and need to have Fetching Strategies as per the NHibernate implementation. Using fetching strategies I have the option of injecting them and easily distributing and re-distributing them. I demonstrated this with LINQ to SQL here : http://www.simonsegal.net/blog/2008/09/16/linq-to-sql-going-poco-and-more/ and you could do the same by wrapping the .Includes extension method however it doesn't correlate well with lazy loading which causes a friction in developing a satisfying outcome. I would prefer my code not to be tightly coupled to my fetching requirements over time immemorial as they are by sprinking them explicitly throughout using .Includes() and .IsLoaded().Anonymous
January 15, 2009
The main reason for disabling lazy loading is the risk of loosing control over when and how you hit the database. Inexperienced developers are protected against making database call in an unwanted manner. So why is it that more Linq2Sql developers don't have more problems with lazy loading even though they statistically would be more inexperienced users? I think it is because if you if you do the error of calling Linq to .... code unaware of what physical storage that lies behind you would get into performance problems very soon and people learn quickly how to avoid this. You cannot be ignorant of what is being called behind the scenes when doing linq queries. That is one of the many reasons I like Linq2Sql over Entity Framework and even NHibernate since it clearly displays the database scheme you are querying. The database is usually the biggest performance factor in a normal web app so every developer should know the database they are querying.Anonymous
March 04, 2009
An even bigger problem with not having Lazy Loading implemented is that there are times you can call the ".Include()" method in your LINQ, and then by the time LINQ gets around to generating your result, it forgets that you've asked it to include the related objects. For instance, if you use the new {} syntax at the end of your query, none of the navigations that you've asked in the middle of your query are remembered. See here for instance: https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=347543 ///<Not working!> var q2 = from p in ent.Products.Include("Categories") where p.ProductID == 1 select new { Product = p, SupplierCity = p.Suppliers.City }; var prod2 = q2.First(); //comment: no eager loading -> prod2.Product.CategoriesReferense.IsLoaded == false ///</Not working!> Whatever else this does, it certainly violates the principle of least astonishment. It also creates headaches in a number of places, for instance, if you're trying to bind to the results of a query in WPF, and don't have the luxury of checking .IsLoaded() before accessing an object's navigation properties. This definitely needs to get fixed.Anonymous
May 21, 2009
There is no way we'll use this if we have to check everything to see if a property is loaded or not. The team better make some changes to that or this thing is DOA. I can't believe they would make such a huge mistake.Anonymous
July 07, 2009
There is no way we'll use this if we have to check everything to see if a property is loaded or not. The team better make some changes to that or this thing is DOA. I can't believe they would make such a huge mistake.Anonymous
July 23, 2009
The main reason for disabling lazy loading is the risk of loosing control over when and how you hit the database. Inexperienced developers are protected against making database call in an unwanted manner. So why is it that more Linq2Sql developers don't have more problems with lazy loading even though they statistically would be more inexperienced users? I think it is because if you if you do the error of calling Linq to .... code unaware of what physical storage that lies behind you would get into performance problems very soon and people learn quickly how to avoid this. You cannot be ignorant of what is being called behind the scenes when doing linq queries. That is one of the many reasons I like Linq2Sql over Entity Framework and even NHibernate since it clearly displays the database scheme you are querying. The database is usually the biggest performance factor in a normal web app so every developer should know the database they are querying.Anonymous
July 23, 2009
The comment has been removedAnonymous
June 15, 2010
Good information and good way your blog post. Good luck blogger man.