EF 4 – Implementing Lazy Loading For My POCO
In my last post, I provided a simple end-to-end example where I used the Entity Framework designer to generate an Entity Data Model (EDM), I turned off the default code generation and implemented my own POCOs. Everything worked fine, but at the end of the post I pointed out that I no longer had the Lazy Loading capability I had with the designer generated objects.
Get the Sample Code
You can download the sample code here.
Lazy Loading for POCOs
At this point, I have a few POCOs which, by their nature, are very simple classes, as well as my context object. See the Customer class below:
public class Customer { public string CustomerID { get; set; } public string CompanyName { get; set; } List<Order> orders = new List<Order>(); public List<Order> Orders { get { return orders; } set { orders = value; } } }
The behavior that I am looking for is: if the Orders for a Customer are accessed and they haven’t been loaded (don’t exist in the context) yet, a call is automatically made to load the Orders for that Customer. Were you implementing this lazy loading functionality yourself, you might imagine adding some code to the getter for Orders. This code would check to see if the Orders were loaded and, if not, make a call to load them.
That would certainly work, but your class would no longer be a POCO, would it? You would have likely imposed some sort of dependency on the framework or at least implemented some data access logic. What you really want is the framework to provide the lazy loading feature set, while maintaining *no* dependencies on any framework from your POCOs.
Lazy Loading Via Dynamically Generated Proxy
So, we have reached a bit of an impasse. We want to call our lazy loading logic from our getter in our POCO, but by doing that, we would have bound it to the framework and have turned our POCO into a DOOF (Dependant On Other Framework – yeah, I just made that up). This is where dynamically generated proxies come in. If you opt in, the framework will dynamically generate a type that derives from your POCO type. This derived type can override certain properties and “inject” new functionality like lazy loading.
In POCO Proxies Part 1 on the ADO.NET Team Blog, they illustrated a simple example of how lazy loading functionality could be injected. Below is a screenshot from that example. Notice that the Orders property is overridden and that a call to the lazy loading functionality (DoLazyLoading() in this simple example) was injected.
Implementing Lazy Loading for your POCOs
Most of the steps required for implementing lazy loading for your POCO via dynamically generated proxy should be pretty straight forward from the above. However for clarity sake, I want to be pretty explicit on what you need to do to enable this behavior:
- Ensure proxy creation is on. The context.ContextOptions.ProxyCreationEnabled property must be true. It is true by default.
- Lazy loading must be enabled. The context.ContextOptions.LazyLoadingEnabled property myst be true. You can set this in the default constructor of your context object. Notice the constructor in my context object below. I set LazyLoadingEnabled to true, as well as explicitly turned on ProxyCreationEnabled explicitly.
public NorthwindContext() : base("name=NorthwindContext") { this.ContextOptions.ProxyCreationEnabled = true; this.ContextOptions.LazyLoadingEnabled = true; }
- You have to be able to implement your POCO in a dynamically generated assembly. Make sure your class is not sealed and is marked public.
- Declare any navigation properties that you want to implement lazy loading with as virtual. See the code below for my customer object. I have marked the Orders navigation property as virtual.
namespace NorthwindModel { public class Customer { public string CustomerID { get; set; } public string CompanyName { get; set; } List<Order> orders = new List<Order>(); public virtual List<Order> Orders { get { return orders; } set { orders = value; } } } }
That is it. You can now run the sample application and you automatically get lazy loading functionality. Let’s have a look. As you can see below, the objects were, in fact, loaded.
Having a look at sql profiler, you can see that 4 queries in all were run. The first was the query to return all of the customers (3). Tthe next 3 were called via lazy loading.
Each time we accessed the Orders from a customer, the lazy loading functionality was called.
(see this post for a discussion of when to use eager vs. lazy loading)
Reference Properties Benefit From Lazy Loading, As Well
Lest you assume that lazy loading is just for collections, it works for reference properties, as well. To illustrate, I have added an Orders ObjectSet to my NorthwindContext:
public class NorthwindContext : ObjectContext { …
public ObjectSet<Order> Orders { get { if (orders == null) { orders = base.CreateObjectSet<Order>(); } return orders; } } private ObjectSet<Order> orders; }
I then changed the code in Program.cs to target my new Orders ObjectSet:
using (NorthwindContext nw = new NorthwindContext()) { var query = from o in nw.Orders where (o.OrderID == 10682 || o.OrderID == 10856 || o.OrderID == 10355) select o; foreach (Order order in query) { Console.WriteLine("ID: {0}", order.OrderID); Console.WriteLine("Date: {0}", order.OrderDate.ToString()); if (order.Customer != null) Console.WriteLine("Customer: {0}", order.Customer.CompanyName); Console.WriteLine(""); } }
Running this code, you will see the following output:
By simply marking the Customer property in my Order POCO as virtual, I will get lazy loading:
namespace NorthwindModel { public class Order { public int OrderID { get; set; } public string CustomerID { get; set; } public DateTime OrderDate { get; set; } public virtual Customer Customer { get; set; } } }
Taking a look at sql profiler, you will see that additional queries were run to return customer information that was not already loaded in the context. Notice that only 2 queries were run via lazy loading. This is because the customer for order id 10682 is the same as the customer for order id 10856. It only had to be loaded into the context once.
Summary
The Entity Framework provides powerful lazy loading functionality for POCOs, while maintaining their integrity. If you are interested in a more in-depth discussion of this functionality, I would suggest having a look at the following post: http://blogs.msdn.com/adonet/archive/2009/12/22/poco-proxies-part-1.aspx.

Email Me