Entity Framework and Lazy Loading

Entity Framework and Lazy Loading

( Apr 28 2008 - 10:10:40 AM)

Microsoft's Entity Framework is a new, powerful tool bringing data modeling, O/RM (object relational mapping) functionality and more. One expected feature of major ORMs is 'Lazy Loading'. Learn how the Entity Framework provides this functionality in a different way. This article will explain the design reasons behind why EF is different than what you would expect, as well as how to achieve the lazy-load functionality you're looking for.

It is important to realize that that ORMs are not a new concept. There are a lot of excellent ORMs out there for Ruby, Python and even for the .Net framework (NHibernate). As a result, when you "jump in" to EF, you may run into an 'issue' similar to what I did (with lazy loading not working the way I thought it would). Because I was used to LINQ to SQL, I was expecting implicit lazy loading, and therefore I thought that EF was broken. This misconception was wrong, and I'll explain more as we go on.

Before I go into detail about the difference in design between the Entity Framework and LINQ to SQL (or other ORMs like NHibernate), I want to show you the code that threw me for a loop. The scenario is simple, I had a "Customers" table and an "Orders" table in my SQL database. I used the LINQ to SQL designer and then the Entity Framework designer (Entity Data Model designer, or EDM). Both designers easily generated my c# code for me that will interact with my database.

My SQL tables had only a few records in there. The "Customers" table had one record, and the "Orders" table had three records that were linked to the customer. So, here's the code I wrote where I initially misjudged EF:

// First, using LINQ to SQL
L2SDataContext linqToSqlContext = new L2SDataContext();

this.MyDataGrid1.DataSource = linqToSqlContext.Customers.First().Orders;

// 3 records in my data grid!!!

this.MyDataGrid1.DataBind();

The above code worked exaclty as I thought it would. The first customer object was recieved from the database, and then when I accessed that customer's orders, LINQ to SQL went back to the database and got the orders. Now, here is what I thought was the same thing using the Entity Framework. Notice how many rows were in my datagrid:

// Using the Entity Framework
EFEntities entityFrameworkContext = new EFEntities();

this.MyDataGrid2.DataSource = entityFrameworkContext.Customers.First().Orders;

// 0 records in my data grid???

this.MyDataGrid2.DataBind();

And that was it. Something so simple as that caused me to spiral into a dark room of confusion. In fact, there are people out there who have 'given up' on EF right there. The problem here is not with the Entity Framework, but with my lack of understanding the underlying design of EF.

Why EF Didn't Lazy Load

As I mentioned before, the Entity Framework supplies ORM functionality, which includes 'lazy loading' of sorts. Actually, EF never claims to follow the Lazy Loading design pattern as is commonly understood. Instead, they provide "deferred" loading capabilities. As a side point, lazy loading, lazy initialization, deferred loading, on-demand loading and just-in-time loading all mean the same thing.

In the above example, LINQ to SQL *automatically* went back to the database and loaded the orders for that first customer. The team behind EF didn't want this *automatic* behavior happening. The reason behind this decision is simple: When architecting a larger project, it is highly important for developers to clearly understand when they are accessing certain resources, such as the database.

As a result, they require an *explicit* call to the ".Load" method of the deferred object. Or, you could "eagerly" load the properties in the initial call using the ".Include" method. Example:

// Explicitly include the orders from the database in one shot.
this.MyDataGrid2.DataSource = entityFrameworkContext.Customers
   .Include( "Orders").First().Orders;

// Or you can...


// Explicitly load the orders after retrieving the customer.

var customer = entityFrameworkContext.Customers.First();

customer.Orders.Load();

this.MyDataGrid2.DataSource = customer.Orders;

At first, I strongly disliked this design decision. However after talking to a few very smart people about it (Julie Lerman, Elisa Flasko - MSFT and Jonathan Carter - MSFT) I've been shown the intelligence behind the decision.

The Need for Automatic Lazy Loading in Entity Framework

While the reasoning above is good, it does not meet all design scenarios. Here is an example where you would want the automatic lazy loading, and then I'll show you how to achieve this functionality right there in EF!

Take this scenario as an example. Let's say we have a team of developers working on a SharePoint 2007 project. The end result will be a configurable web application where the end user will be able to add and remove web parts himself for display reasons. We are going to assume the following:

  • The lead developer built a static class called "BusinessObjects", and exposed a few properties, one being "CurrentCustomer".
  • Web part "CustomerBasicInfo" will display the current customer's name.
  • Web part "CustomerAvailableAddresses" will display a list of all addresses the customer has on record.
  • Web part "CustomerBillingHistory" will display a grid of any orders the customer has made in the past.

In this scenario, it is vital that the "BusinessObjects.CurrentCustomer" property does not eagerly load the ".Addresses" and ".Orders" of the customer object. Think about it, if the end user doesn't have web parts on his screen that utilizes that data, it would be a huge waste to download that data.

Also, it would be inappropriate for the developers of those web parts to call ".Load" on those objects. What if someone else loaded the objects? The developers of these web parts should simply create a "view" to the business object, not provide their own tracking code.

We have talked about the reasons why EF requires an explicit call to load these deferred objects, but now we see a scenario where we want implicit loading. Which brings us to our next section:

Configuring Lazy Loading in Entity Framework

As I promised, I will now show how you can achieve implicit lazy loading using the Entity Framework. The code I'm about to show you is automatically created by the EDM designer if you were to point to a database and have it generate the EDM from there. Then, I'll add one simple line of code (which I've already shown you) that will make everything work just the way you'd expect it.

Here's the EDM designer's generated code:

public EntityCollection<Orders> Orders
{
    get
   {
        return ((IEntityWithRelationships)( this)).RelationshipManager
       .GetRelatedCollection<Orders>( "EFTestDBModel.FK_Orders_Customers", "Orders");
   }
}

That code is the "Orders" property on my Customer object. As I mentioned above, this is generated by the EDM. Now, if I want to put implicit lazy loading in here, I do one simple step:

public EntityCollection<Orders> Orders
{
    get
   {
        var result = ((IEntityWithRelationships)( this)).RelationshipManager
           .GetRelatedCollection<Orders>( "EFTestDBModel.FK_Orders_Customers", "Orders");

       // I'll just explicitly call the Load method myself.

        if (result.IsLoaded == false)
       {
           result.Load();
       }

        return result;
   }
}

This solution is simple. You get all the great power of the Entity Framework, the Entity Data Model designer, Entity SQL (and I could go on) and you can perform Lazy Loading the way you want to. Again, the decision to not automatically load deferred objects was a decided upon, debated and ultimately well made choice. But now you can see how easy it is to enable automatic lazy loading using Microsoft's Entity Framework.

Conclusion

You may not like my solution here, but EF is not being forced on anyone. As I mentioned above, there is LINQ to SQL, NHibernate and many other choices. Now that I understand the design reasons, and because I know the other great features of EF, I'll be yet another adopter of the product.

你可能感兴趣的:(framework)