POCO in the Entity Framework 4: Part 1 - The Experience

 


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.


 

Last week I mentioned in the sneak preview on POCO that support for POCO entities is one of the new capabilities we have added to Entity Framework 4.0. This week, I’d like to go into the details of POCO support in Entity Framework 4.0.

There’s quite a bit to discuss here, including:

  • Overall POCO experience in Entity Framework 4.0
  • Change Tracking in POCO
  • Relationship Fix-up
  • Complex Types
  • Deferred (Lazy) Loading and Explicit Loading
  • Best Practices

In this post, I will focus primarily on the overall experience so that you can get started with POCO in Entity Framework 4.0 right away. I’d like to use a simple example that we can walk through so you can see what it feels like to use POCO in Entity Framework 4.0. I will use the Northwind database, and  we’ll continue to build on this example in subsequent posts.

Step 1 – Create the Model, turn off default Code Generation

While POCO allows you to write your own entity classes in a persistence ignorant fashion, there is still the need for you to “plug in” persistence and EF metadata so that your POCO entities can be materialized from the database and persisted back to the database. In order to do this, you will still need to either create an Entity Data Model using the Entity Framework Designer or provide the CSDL, SSDL and MSL metadata files exactly as you have done with Entity Framework 3.5. So first I’ll generate an EDMX using the ADO.NET Entity Data Model Wizard.

image

  1. Create a class library project for defining your POCO types. I named mine NorthwindModel. This project will be persistence ignorant and will not have a dependency on the Entity Framework.
  2. Create a class library project that will contain your persistence aware code. I named mine NorthwindData. This project will have a dependency on Entity Framework (System.Data.Entity) in addition to a dependency on the NorthwindModel project.
  3. Add New Item to the NorthwindData project and add an ADO.NET Entity Data Model called Northwind.edmx (doing this will automatically add the dependency to the Entity Framework).
  4. Go through “Generate from Database” and build a model for the Northwind database.
  5. For now, select Categories and Products as the only two tables you are interested in adding to your Entity Data model.

Now that I have my Entity Data model to work with, there is one final step before I start to write code : turn off code generation. After all you are interested in POCO – so remove the Custom Tool that is responsible for generating EntityObject based code for Northwind.edmx. This will turn off code generation for your model.

image

We are now ready to write our POCO entities.

Step 2 – Code up your POCO entities

I am going to write simple POCO entities for Category and Product. These will be added to the NorthwindModel project. Note that what I show here shouldn’t be taken as best practice and the intention here is to demonstrate the simplest case that works out of the box. We will extend and customize this to our needs as we go forward and build on top of this using Repository and Unit of Work patterns later on.

Here’s sample code for our Category entity:

     public class 

Category

     {
        public int CategoryID { get; set; }
        public string CategoryName { get; set; }
        public string Description { get; set; }
        public byte[] Picture { get; set; }
        public 

List<Product

 > Products { get; set; }
    }

Note that I have defined properties for scalar properties as well as navigation properties in my model. The Navigation Property in our model translates to a List<Product>.

Similarly, Product entity can be coded like this:

     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 

Int16

  UnitsInStock { get; set; }
        public 

Int16

 UnitsOnOrder { get; set; }
        public 

Int16

  ReorderLevel { get; set; }
        public bool Discontinued { get; set; }
        public 

Category

  Category { get; set; }
    }

In this case, since the relationship allows only one Category that a Product can belong to, we have a reference to a Category (unlike the List<T> collection we have for modeling the Navigation Property in Category).

Step 3 – Code up your Entity Framework Context

The last thing I have to do in order to pull all of this together is to provide a context implementation (much like the ObjectContext implementation you get when you use default code generation). The context is the glue that brings persistence awareness into your application, and it will allow you to compose queries, materialize entities as well as save changes back to the database. The context will be a part of the NorthwindData project.

For simplicity, I will include our context into the same class library that has our entity types – but when we discuss patterns such as Repository and Unit of Work, we will set it up such that you have a pure POCO class library without any persistence concerns.

Here’s a simple context implementation for our scenario:

 public class 

NorthwindContext : ObjectContext

     {   
        public NorthwindContext() : base

("name=NorthwindEntities",

  

"NorthwindEntities"

 )  
        {
            _categories = CreateObjectSet<

Category

 >();
            _products = CreateObjectSet<

Product

 >();
        }

        public 

ObjectSet<Category

 > Categories
        {
            get 
            { 
                return _categories; 
            }
        }
        private 

ObjectSet<Category

 > _categories;

        public 

ObjectSet

 <

Product

 > Products
        {
            get
            {
                return _products;
            }
        }
        private 

ObjectSet

 <

Product

 > _products;
    }

Note that ObjectSet<T> is a more specialized ObjectQuery<T> that we introduced in Entity Framework 4.0.

That’s it – now you have pure POCO entities with a simple context that will allow you to write queries like this:

NorthwindContext

  db = new 

NorthwindContext

 ();

    

var

  beverages = 

from

  p in db.Products
                    where p.Category.CategoryName == "Beverages"
                    

select

  p;

The entities that are materialized are pure POCO entities, and you can make and persist changes much like you would with regular EntityObject or IPOCO entities. You get all the services provided by Entity Framework - the only difference is that you are using pure POCO entities.

There are many possibilities here as far as how we can improve on this simplified example. Before we get into that however, I would like to get some basic questions out of the way.

Do I need an Entity Data Model before I can use POCO?

Yes – POCO support in Entity Framework 4.0 simply removes the need of having persistence specific concerns in your entity classes. There is still the need for you to have a CSDL/SSDL/MSL (collectively EDMX) metadata so that the Entity Framework is able to use your entities along with the metadata in order to enable data access. There is a separate effort that we are working on that will allow you to do true “code-first” development without the need to have a predefined EDMX model. A community preview of this feature will be released to the web in the coming months. We will roll this into the product the first chance we get. As always, your feedback will be helpful.

Do I have to always hand-craft these entities and the context?

No – there is a very powerful and flexible code generation mechanism in Entity Framework 4.0 that is based on T4 as Alex blogged about here. You can provide your own templates that allow you to build your entities in a way that you see fit. We are also working on providing standard out of the box templates that will generate POCO entities for you.

How is metadata mapped when using POCO entities?

In Entity Framework 3.5, both EntityObject and IPOCO based entities relied on the use of mapping attributes that were meant for decorating and mapping the entity types and properties back to the corresponding elements in the Conceptual model. Entity Framework 4.0 introduces convention based mapping for allowing mapping of Entity Types, Properties, Complex Types and Relationships back to the conceptual model without the need for explicit decoration. The simple rule here is that Entity Type names, Property names and Complex Types names used in your POCO classes must match those defined by the conceptual model. Namespace names are ignored and don’t have to match between the class definition and the conceptual model.

Do I need to have public getters and setters for all properties in my entity classes?

You can use any access modifier on your POCO type’s properties as long as none of the mapped properties are virtual and as long as you don’t require partial trust support. When running in partial trust, there are specific requirements on the visibility of access modifiers on your entity classes. We will be documenting the complete set of access modifiers that are supported when partial trust is involved.

What types of collections are supported for collection based navigation properties?

Any collection that is an ICollection<T> will be supported. If you don’t initialize the field with a concrete type that is an ICollection<T> type, then an List<T> type will be provided upon materialization.

Can I have uni-directional relationships? For instance – I would like to have a Category property in my Product class, but I don’t want to have a Products collection in my Category class.

Yes – this is supported. The only restriction here is that your entity types have to reflect what is defined by the model. If you are not interested in having the navigation property for one side of the relationship, then remove it from the model completely.

Is Deferred (Lazy) Loading supported with POCO?

Yes – Deferred (Lazy) loading is supported with POCO through the use of proxy types that are used to provide automatic lazy loading behavior on top of your POCO classes. This is something that we’ll cover when we get to deferred loading – until then know that eager loading via the use of “Include” is also supported, like so:

var

  beverageCategory = (

from

  c in context.Categories
.Include("Products")
                        where c.CategoryName == "Beverages"
                        

select

  c).Single();

If I were to use Deferred (Lazy) Loading, I don’t have to do this – we’ll discuss that when we discuss proxies.

Fixing up Relationships

There are two types of fix-ups to be aware of: 1)Fix-up during query, and 2) Fix-up during changes to your entities/relationships

Fix-up during Query

Fix-up during query happens when you load related entities using separate queries on the same ObjectContext. For instance, if I were to query for a category instance Beverages, and later query for a product instance Chai (that is in Category Beverages), I would want chai.Category to point to the instance of the Beverages category without additional work.

This is automatic and works today.

Fix-up during changes to your entities/relationships

This is the relationship fix-up between two entities when I add/remove an entity that is related to another entity or alter relationships. Think of this as the case when I create a new product called “Diet Chai” and want to associate it with the Beverages category .

In Beta1 of Entity Framework 4.0, you do not get automatic relationship fix-up in this case with POCO entities. When dealing with relationships that change between entities, you will have to make sure that your POCO types include the logic to manage fix-up on both ends of the relationship correctly.

For instance, in my Northwind example, I have defined the following method on Category to support adding / removing of Orders.

         public void AddProduct(

Product

  p)
        {
            if (Products == null)
            {
                Products = new 

List<Product

 >();
            }

            if (!Products.Contains(p))
            {
                Products.Add(p);
            }            
            
            p.Category = this;
        }

In general, it is good to use a pattern like this to support adding/removing related items rather than using the relationship collection to add/remove related entities. You also have options of making the getter/setter for the actual EF backed collection private/internal to support more fine grained access – but as mentioned earlier, some of this depends on the requirements as to whether you require partial trust support or not.

So we have covered quite a bit of ground in this “quick look” at the overall POCO experience. There’s quite a lot more to talk about – and I will be continuing this discussion early next week. In the meantime, take a look at the complete solution (attached) if you are interested in the sample code covering all the concepts we covered here. Please keep in mind that you must have a local instance of Northwind database installed on the machine where you are running this sample.

In my next post we will look at Complex Types, Change Tracking, Proxies, Lazy Loading and Eager Loading. Until then, please look through the MSDN Documentation on POCO here for more details on POCO support in Entity Framework 4.0.

Let us know what you think!

Faisal Mohamood
Program Manager, Entity Framework

NorthwindPocoSamplePart1.zip