Udostępnij za pośrednictwem


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

Comments

  • Anonymous
    May 28, 2009
    The comment has been removed

  • Anonymous
    May 28, 2009
    There is an issue in NHibernate dynamic proxies with inheritance. Lets say we have a Customer entity which references an Account entity, Account can be SavingsAccount or ChequeAccount via Inheritance. When following the lazily-loaded Account association, the actual target type cannot be determined easily as the proxy is subclassing the Account entity type instead of the actual Account type (Savings or Cheque) so a standard "is" will not work. Has this been considered?

  • Anonymous
    May 28, 2009
    @Peter: You might have missed: 2 - You cannot use inheritance with your complex type classes

  • Anonymous
    May 28, 2009
    Why Defer Loading in Entity Framework isn

  • Anonymous
    May 28, 2009
    IPOCO still makes me chortle. Just out of curiosity, why are you guys calling it  "deferred loading", when the rest of the world has settled on "lazy" (and his holiness the Reverend Martin Fowler even formalized the pattern name)?  Is there a subtle difference in the two?

  • Anonymous
    May 28, 2009
    @Joe, There is actually no subtle conceptual difference between "deferred loading" and "lazy loading". Deferred and lazy are being used as synonyms. The main reason we tend to use deferred internally and in the documentation is that the name of the API that you use to turn it on and off is “DeferredLoadingEnabled”. This flag was named like that after the homologous flag in LINQ to SQL, which has had this feature since .NET 3.5. I can assure you we are listening to the feedback on this. Thanks, Diego

  • Anonymous
    May 28, 2009
    +1 for calling it by the established name, lazy loading. In case you missed Ayende's post, have you considered the valid issues he raises here: http://ayende.com/Blog/archive/2009/05/29/why-defer-loading-in-entity-framework-isnrsquot-going-to-work.aspx This road has already been followed by NHibernate and found to lead to a dead end. Please consider the points Ayende raises.

  • Anonymous
    May 29, 2009
    Is inheritance support planned for a future release? Lack of inheritence support would be quite limiting in DDD scenarios. If inheritance is not supported the entity model will not be as 'rich' and the need for a complex persistence framework is somewhat reduced. This might limit the use of entity framework to only trivial applications.

  • Anonymous
    May 29, 2009
    Faisal What is the behaviour when I mix and match .Includes() with context.ContextOptions.DeferredLoadingEnabled = true ? Will the eager and lazy fetching work independently across the single ObjectContext in the same query? Thanks, Simon

  • Anonymous
    May 29, 2009
    Thank you for submitting this cool story - Trackback from progg.ru

  • Anonymous
    May 29, 2009
    I have actually a question on statement: "In order to enable creation of proxy instances for adding/attaching, you can use the CreateObject factory method on ObjectContext for creating entity instances" I've successfully newed POCO with all properties declared as virtual just with new operator. Context.AddObject worked for me like a charm!

  • Anonymous
    May 29, 2009
    Persistence ignorance means you can switch your persistence layer from EF to something else without affecting your model. But what the EF proposes for using with POCO is all contrary to PI. First it wants me to create a model from the DB and it imposes rules like marking a property virtual to achieve lazy loading. This may not be favorably to my model. I guess MS is going all in the wrong direction.  

  • Anonymous
    May 29, 2009
    @Peter and Karl, We don't support inheritance of complex types, but I think Peter is referring to entity types, for which we support inheritance. On the other side, I am not sure I understand Peter’s question correctly, so I am just going to try to explain how inheritance interacts with proxies: Let’s say you have Account and SavingsAccount and ChequeAccount that derive from it. EF can create proxy types for all the concrete types of the hierarchy. For instance, let’s assume that Account is abstract, then it will create SavingsAccount’ and ChequeAccount’. SavingsAccount’ is a SavingAccount and an Account, since it derives from SavingsAccount. ChequeAccount’ is a ChequeAccount and an Account, since it derives from ChequeAccount. Now, let’s assume that Account is not abstract. Then EF can also create a proxy type for Account, Account’. In the case you have two instances of Account’ and SavingsAccount’, if you try to establish if the type of the first instance is assignable from the type of the other instance, the answer will be no. They both derive from a common root, but they are on different branches of the inheritance tree. Thanks, Diego

  • Anonymous
    May 29, 2009
    @Hemanshu, EF in .NET 4.0 works with pure POCO objects, using a snapshot based change detection mechanism. It is true that in Visual Studio 2008 you could only create the model from the database or start with a model from scratch. But the EDM designer in Visual Studio 2010 also supports creating the database schema from your model.   In that case, you can have non-virtual properties and use the new keyword to get new instance. If you specify properties as virtual we can subclass your entity types and provide with services that you probably wouldn’t want to code yourself inside your entities (precisely because they need to use the persistence infrastructure). Even in that case, we support object graphs that are a mix of proxies and non-proxies: objects that came from server queries, objects that you instantiated yourself using new or the CreateObject<T> factory method, can be connected, and we will use a mix of snapshot and notification based change tracking. Lazy loading is an interesting example because it means that navigation properties in your model acquire query capabilities, which clearly belong inthe persistence infraestructure. If you had to write that code yourself, you would need to somehow invoke the persistence framework from inside your entity. I would like to understand what your expectations are. How would you get lazy loading to work and still preserve persistence ignorance of your code without something like a proxy? Thanks, Diego

  • Anonymous
    May 29, 2009
    @sergejus, Glad to hear that non-proxy POCO objects work like a charm for you :). Change trackig proxies optimize the change tracking mechanism by enabling change notifications and removing the need to do a snapshot comparison to detect whether an entity has been modified. But regular POCO entities work as well, even mixed with proxies in the same object graph. The overhead of detecting changes using snapshot is not that big in many scenarios. Thanks, Diego

  • Anonymous
    May 29, 2009
    @Simon.Segal Yes, eager loading and lazy loading can be mixed in the same context. You specify eager loading at the ObjectQuery level using Include and lazy loading at the ObjectContext level. Eager loading will load all the requiered paths in a single shot and you can continue loading from there lazyly as you navigate your object graph. Thanks, Diego

  • Anonymous
    May 30, 2009
    Thank you for adding the lambda expression support for LoadProperty! L2S made great use of generics instead of strings which not only provides type-safety, but allows easy refactoring.

  • Anonymous
    May 30, 2009
    9efish.感谢你的文章 - Trackback from 9eFish

  • Anonymous
    May 31, 2009
    I have an existing model and want to provide lazy loading for a child. Is it possible to do that without marking it virtual. I am currently using proxy objects to do the same.

  • Anonymous
    May 31, 2009
    @Hemanshu, what kind of proxy object are you using now? In Entity Framework, proxy objects derive from the original POCO class and therefore require proerties to be virtual in order to override their behavior and enable things like lazy loading.

  • Anonymous
    May 31, 2009
    For all the children (IList) I attach a proxy IList which uses the lazy load pattern to provide deferred loading...

  • Anonymous
    June 01, 2009
    ASP.NET/ASP.NET AJAX/ASP.NET MVC/jQuery Creating a Simple .NET 4.0 Web App using Visual Studio 2010 ScottGu's May 30 Links: ASP.NET, ASP.NET MVC, Visual Studio Oslo About the &quot;Oslo&quot; May 2009 CTP Channel 9 - Chris Sells: On the History of DevCon,

  • Anonymous
    June 01, 2009
    Here is an example of the issue Peter mentioned, I have also run into this with NHibernate. class Customer {    public virtual Account Account { get; set; }    ... } class Account { ... } class SavingsAccount : Account { ... } class CheckingAccount : Account { ... } ... if (customer.Account is SavingsAccount) {    // This won't work with defered loading, the proxy will have already subclassed the Account type }

  • Anonymous
    June 01, 2009
    Hi Can you, please, write a complete sample of generic repository and unit of work together, using EF.4.0

  • Anonymous
    June 04, 2009
    I too need a complete repository and unit of work example covering all CRUD operations using EF.4.0

  • Anonymous
    June 07, 2009
    Can you map enumerated properties to string fields in the EF model?

  • Anonymous
    June 08, 2009
    Daily tech links for .net and related technologies - June 9, 2009 Web Development A view from the cloud

  • Anonymous
    June 09, 2009
    Taz, For NHibernate: if your modify your domain model so that each domain entity implements an appropriate interface and then change your if statement to something like   if (customer.Account is ISavingsAccount) then you'll find it works. Does a similar approach work for the EF?

  • Anonymous
    June 10, 2009
    In my last post on POCO , I mentioned the fact that there are two types of change tracking possibilities

  • Anonymous
    June 10, 2009
    In my last post on POCO , I mentioned the fact that there are two types of change tracking possibilities

  • Anonymous
    June 10, 2009
    Is it possible to use Include method to define preloading with POCO classes?

  • Anonymous
    June 16, 2009
    If you have been watching this blog, you know that I have been discussing the various aspects of POCO

  • Anonymous
    June 17, 2009
    If you have been watching this blog, you know that I have been discussing the various aspects of POCO

  • Anonymous
    July 07, 2009
    Why is inheritance not allowed with complex types in Entity Framework .Net 4.0? What I would like to know is what is the design intent behind such a constraint?

  • Anonymous
    July 16, 2009
    <i>You need to only use CreateObject when dealing with proxy based entities.</> I am not an english speaker, so can you elaborate on this one? e.g. when creating new product inside context, say: using ctx { Product p = new Product () { Name = peugeot, Description = French brand car...}; } later on I'll save it to DB. When do I need to use createObject instead of newing up an object? sorry for my english, but please be more verbose on this. EFv4 looks awesome !!!

  • Anonymous
    July 19, 2009
    For all the children (IList) I attach a proxy IList which uses the lazy load pattern to provide deferred loading...

  • Anonymous
    August 11, 2009
    Regarding using ObjectContext.CreateObject<T> I noticed that i can get same effect by normal instantiation using new! Why should I consider using CreateObject when creating POCO instances that will be used with EF 4?

  • Anonymous
    August 11, 2009
    Ok I figured out something, CreateObject must be used with Proxy Enabled classes in instantiation. But Even without using it I was able to use AddObject method. The only difference I noticed is that after saving when modifying the object the entity state doesn't change. Any justification and more clarification on this point?

  • Anonymous
    August 31, 2009
    I am wondering how the "deferred" loading works with WCF-like applications? I probably would not want this enabled for SOA/WCF type applications, correct?

  • Anonymous
    September 13, 2009
    Hi, I am trying this in a WCF context. All works fine, but only if the Products list in Category is not virtual? Also if I query for all Categories and Include their Products, Products fails to serizalize due to the Category parent property, cyclic problem? Am I missing something here? kr, Michel

  • Anonymous
    October 03, 2009
    The comment has been removed

  • Anonymous
    December 02, 2009
    Your Article is based on Beta 1 and is therefore complete outdated. Poco-generation works completely different now. You should not waste peoples time and mark this article with Beta 1.

  • Anonymous
    January 12, 2010
    Hi, nice post. But the DeferredLoadingEnabled property doesn't exist in the ContextOptions and so the lazy loading doesn't work!? Any solution for that? Thanks, mrG

  • Anonymous
    April 21, 2011
    Hi there. Let me thank you for you good tutorials first. Well as I was reading, I came to this: "You cannot use inheritance with your complex type classes." which I could not really understand. What if I had a ProductOutOfProduction which extended Product and wanted to add the InventoryDetail property to it only. Would it not work?? (Can not try it now, Im on holidays). 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 bool Discontinued { get; set; }    public Category Category { get; set; } } public class ProductOutOfProduction  : Product {    public InventoryDetail InventoryDetail { get; set; } } Thanks, Marco

  • Anonymous
    April 24, 2011
    Thanks is a good post, and informative. Found that adding complex type via visual studio, just added the type, but I still had to edit the xml by hand to include its properties and mapping. (VS 2010)

  • Anonymous
    May 25, 2011
    Naive question -- what is this "beveragesCategory"? And object for the class. This would mean it is loading per each record of the table, not entire table, right?

  • Anonymous
    May 25, 2015
    Be your own boss with revoluza dot com