POCO in the Entity Framework 4: Part 2 – Complex Types, Deferred Loading and Explicit Loading

 


The information in this post is out of date.

Visit msdn.com/data/ef for the latest information on current and past releases of EF.


 

In my post last week on the POCO Experience in Entity Framework, I covered the fundamentals of POCO support in Entity Framework 4.0. In this post, I’ll cover a few more aspects related to POCO.

Complex Types

Complex Types are supported in POCO just like they are with regular EntityObject based entities. All you have to do is declare your complex types as POCO classes and then use them for declaring the complex type based properties on your POCO entities.

As an example, here’s an InventoryDetail complex type to represent a part of my Product entity:

 public class 

InventoryDetail

 {
    public 

Int16

  UnitsInStock { 

get

 ; 

set

 ; }
    public 

Int16

  UnitsOnOrder { 

get

 ; 

set

 ; }
    public 

Int16

  ReorderLevel { 

get

 ; 

set

 ; }
} 

My Product class now has been modified to include a property of this type to group the inventory detail fields:

 public class 

Product

 {
    public int ProductID { 

get

 ; 

set

 ; }
    public string ProductName { 

get

 ; 

set

 ; }
    public int SupplierID { 

get

 ; 

set

 ; }
    public string QuantityPerUnit { 

get

 ; 

set

 ; }
    public decimal UnitPrice { 

get

 ; 

set

 ; }
    public 

InventoryDetail

  InventoryDetail { 

get

 ; 

set

 ; }
    public bool Discontinued { 

get

 ; 

set

 ; }
    public 

Category

  Category { 

get

 ; 

set

 ; }
}

You can then do everything you are used to doing with Complex Types. Here’s how I use it in a query:

var

  outOfStockProducts = 

from

  c in context.Products
                         where c.InventoryDetail.UnitsInStock == 0
                         

select

  c;

 

As you can see, Complex Type support with POCO is really straightforward to use. There are a couple of things you need to keep in mind when using complex types support with POCO:

  1. You must define your Complex Type as a class. Structs are not supported.
  2. You cannot use inheritance with your complex type classes.

While we are on the topic of Complex Types, I thought I’d mention one other thing: Did you know that the Entity Framework designer in Visual Studio 2010 supports complex type declarations?

In Visual Studio 2008, you had to manually add the Complex Type declaration to the CSDL. That is all history with the Complex Type support in the designer in Visual Studio 2010.

image

And what’s cool is that because Visual Studio 2010 supports Multi-Targeting, you can use this capability when building applications that target .NET Framework 3.5, using Entity Framework 3.5 as well!

Deferred/Lazy Loading

In my sneak preview post on Deferred Loading two weeks ago, I mentioned that there is now Deferred Loading support in Entity Framework. It comes as no surprise then that the default code-generated entity types out of the box based on EntityObject will offer Deferred Loading. If you are wondering whether Deferred Loading is supported with POCO objects, then I think you will be happy to know that you can get Deferred Loading with POCO as well.

There are two things you need to do in order to get Deferred Loading support with POCO entities:

  1. Declare the property that you would like to load lazily as virtual. These properties can be any collection type that implements ICollection<T> or they can be a reference representing a 1/0..1 relationship.

For instance, here’s a part of the updated Category entity class which I have modified to support Deferred Loading:

 public class 

Category

 
{
    public int CategoryID { 

get

 ; 

set

 ; }
    public string CategoryName { 

get

 ; 

set

 ; }
    public string Description { 

get

 ; 

set

 ; }
    public byte[] Picture { 

get

 ; 

set

 ; }
    public virtual 

List

 <

Product

 > Products { 

get

 ; 

set

 ; }
    ...

     2.   Enable deferred loading on the context:

 context.ContextOptions.DeferredLoadingEnabled = true;

 

That’s it. You will now get automatic Deferred Loading for your POCO types without having to do anything else.

So how exactly does this work and what’s going on under the covers?

The reason why this works is because when I marked my collection property as virtual, this allowed the Entity Framework to provide a proxy instance for my POCO type at runtime, and it is this proxy that does automatic deferred loading. The proxy instance is based on a type that derives from my own POCO entity class - so all functionality you have provided is preserved. From a developer point of view, this allows you to write persistence ignorant code even when deferred loading might be a requirement.

If you were to inspect the actual instance in the debugger, you will see that the underlying type for the instance is different from the original type that I declared:

image

While the Entity Framework does its best to provide automatic deferred loading with minimal friction, this is something you need to be aware of when dealing with manual creation of instances that you want to then add or attach, or when you serialize/deserialize instances.

Manual instantiation of Proxy instances for POCO entities

In order to enable creation of proxy instances for adding/attaching, you can use the CreateObject factory method on ObjectContext for creating entity instances:

Category

  category = context.CreateObject<

Category

 >();

 

Try to keep this in mind and use CreateObject when creating instances that you want to then use with the Entity Framework.

More Efficient Change Tracking with “Change Tracking Proxies”

The standard POCO entities we have talked about until now rely on snapshot based change tracking – i.e. the Entity Framework will maintain snapshots of before values and relationships of the entities so that they can be compared with current values later during Save. However, this comparison is relatively expensive when compared to the way change tracking works with EntityObject based entities.

There is another type of proxy that will allow you to get better performance out of change tracking with POCO entities.

If you are familiar with IPOCO, you know that IEntityWithChangeTracker was one of the interfaces you were required to implement to provide change notifications to the Entity Framework.

Change Tracking proxies subclass your POCO entity class to provide you with this capability during runtime without requiring you to implement the IPOCO interfaces yourself.

In many ways, you get the best of both worlds with this approach: You get persistence ignorance with POCO classes and you get the performance of EntityObject / IPOCO when it comes to change tracking.

To get change tracking proxies, the basic rule is that your class must be public, non-abstract or non-sealed. Your class must also implement public virtual getters/setters for all properties that are persisted. Finally, you must declare collection based relationship navigation properties as ICollection<T> only. They cannot be a concrete implementation or another interface that derives from ICollection<T> (a difference from the Deferred Loading proxy)

Here’s an example of my POCO class for Product that will give me more efficient proxy based change tracking at runtime:

 public class 

Product

 {
    public virtual int ProductID { 

get

 ; 

set

 ; }
    public virtual string ProductName { 

get

 ; 

set

 ; }
    public virtual int SupplierID { 

get

 ; 

set

 ; }
    public virtual string QuantityPerUnit { 

get

 ; 

set

 ; }
    public virtual decimal UnitPrice { 

get

 ; 

set

 ; }
    public virtual 

InventoryDetail

  InventoryDetail { 

get

 ; 

set

 ; }
    public virtual bool Discontinued { 

get

 ; 

set

 ; }
    public virtual 

Category

 Category { 

get

 ; 

set

 ; }
}

Once again, keep in mind that you must use CreateObject for creating proxy instances you want to then add or attach to the context. But pure POCO entities that don’t rely on proxies and proxy based entities can work together. You need to only use CreateObject when dealing with proxy based entities.

What if I want both Deferred Loading and better change tracking for the same POCO type?

The two are not mutually exclusive and you don’t have to choose between a Deferred Loading proxy and a Change Tracking proxy. If you want Deferred Loading and efficient change tracking, you just have to follow the rules for Change Tracking via proxies, and enable Deferred Loading. Change Tracking proxies will give you deferred loading if deferred loading is enabled.

Explicit Loading

All this deferred loading capability is great – but there are plenty of you out there that want to be in full control of how you load related entities. You might even choose to go the route of pure POCO without having to resort to any of the automatic proxy generation done for you.

This is a perfectly acceptable (and preferable in many cases) and you can use explicit loading of relationships and be in complete control of how you query for data from the database.

There are two options for doing Explicit Load with POCO:

One is to use ObjectContext.LoadProperty and specify the name of the navigation property you want to load:

 context.LoadProperty(beveragesCategory, "Products");

This gets the job done – but it isn’t very type safe as you can see. If I don’t have the right name of the navigation property here, I will get a runtime exception.

Some of you might actually prefer this:

 context.LoadProperty(beveragesCategory, c => c.Products);

I can use a lambda expression to specify the property that I want to load explicitly, and this is offers more type safety.

So that’s about everything I planned on covering in the second post. There’s more to come however – in the final part of this series, we’ll go over some of the things to be aware of when dealing with pure POCO (non proxy) instances and keeping things in sync between your object graph and the Object State Manager. We will also cover variations of SaveChanges that you might want to be aware of.

In the meantime, check out the sample code that I have updated to cover some of the things we have talked about in this post.

Faisal Mohamood
Program Manager, Entity Framework

NorthwindPocoSamplePart2.zip