By Tony Sneed, Author, Instructor
[Disclaimer: The content of this article is based on beta software and is subject to change.]
Last summer Microsoft released the first version of the ADO.NET Entity Framework as part of .NET 3.5 Service Pack 1. It builds on the concept of Dr. Peter Chen’s Entity Relationship Model by introducing a conceptual model, called the Entity Data Model (EDM), which sits on top of the database schema to represent the data in a way that is closer to how objects are modeled in the real world. This higher level of abstraction can help reduce the impedance mismatch that exists between the worlds of relational data and object oriented programming. However, the Entity Framework is much more than another Object-Relational Mapping (ORM) tool. In fact, you can query the EDM using a database-agnostic dialect of SQL called Entity SQL and use old-style data readers to fetch results without materializing them as objects.
The first ORM released by Microsoft was LINQ to SQL, which shipped with .NET 3.5 and Visual Studio 2008. It’s a single-purpose ORM that only works against SQL Server (2000 or later), so Microsoft has decided to throw most of its weight behind the Entity Framework, because it is built on the ADO.NET provider model and can connect to any database for which an Entity-Framework-enabled provider exists (including Oracle, Sybase, and other major DBMS’s).
While the first version of the Entity Framework provided a richer mapping experience, including different ways to model inheritance, it lacked many features required by real-world applications, especially those built on the tenets of service-orientation. For example, classes generated by the entity model designer were tightly coupled to the Entity Framework, which made them difficult to use in n-tier applications where entities should be completely unaware of how they will be persisted.
Coming of Age
With version 4.0 of the .NET Platform, Microsoft will release its second version of the Entity Framework, which will include a raft of new features, making it a viable option for use in real-world applications. (As of this writing, the first Community Technology Preview is available for Entity Framework 4.0, which runs on .NET 4.0 Beta 1 with Visual Studio 2010.)
1. Persistence Ignorance: You can define your own POCO’s (Plain Old CLR Objects) that are decoupled from any specific persistence technology. This allows you to swap out one data access stack for another should the need arise.
2. T4 Code Generation: EF 4 will ship with a number of T4 code-generation templates which you can customize or replace with your own. (T4 is a code-generation technology built into Visual Studio 2008 or later.)
3. Lazy Loading: In addition to eager and explicit loading, related entities can be loaded automatically on demand. For example, with an Order class that has an OrderDetails property, marking this property as virtual will cause order details to be loaded from the database automatically when the OrderDetails property is enumerated.
4. POCO Change-Tracking: EF4 will support two models for tracking changes on POCO’s. By default EF will take a snapshot of the original state of your objects and then compare it to the current version when saving changes. Alternatively, you can define properties as virtual so that their state is continually tracked and kept in sync with the object state manager.
5. Better N-Tier Support with Self-Tracking Entities: The first CTP for EF4 includes a T4 template for generating entities that track their own changes on the client, which are then serialized when sent across service boundaries and saved to the database.
6. Model-First Development: Create a model for your entities, then have Visual Studio 2010 generate DDL to create a database with matching tables and relations.
7. Code-Only Development: Write classes and have EF infer a conceptual model (no edmx file!). You can even generate DDL from the dynamic model to create the database and tables.
In addition to these features, EF 4 will include a number of other improvements, such as singularization and pluralization of entity names (for example, when creating a model for the Northwind sample database), an enhanced entity model designer, complex type support, inclusion of foreign keys in the entity model, additional functions and operators for LINQ queries, testability improvements with IObjectSet<T>, and better readability / performance for generated SQL.
A closer look at POCO
Persistence ignorance has become an important concept for developing applications that may need to change persistence technologies at some point in the future. Entity Framework 4.0 now supports POCO out of the box, including the ability to track and persist changes to entities. All you need to do is create your own entity classes with properties that match those defined in your entity data model. For example, you can start by adding a new ADO.NET Entity Data Model to a project in Visual Studio 2010 and selecting the Products and Categories tables from the Northwind database.
Turn off automatic entity generation by clearing the Custom Tool property of the model edmx file, then simply add Category and Product classes to your solution. Note that the Category entity has a Products property, which you can define as a List<Product>. (Although not yet available in the CTP, the final release of EF 4 should allow you to define unidirectional relationships between entities.) Without any further effort on your part, EF will use your POCO classes, creating a snapshot of each entity’s original state so that updates will take place when you save changes.
While snapshot change-tracking may be sufficient for developers who desire a pure POCO experience, there is some additional overhead when saving entity changes because EF must compare the original and current snapshots to detect what changes have taken place. (You can also manually inform the object state manager of changes by calling ObjectContext.DetectChanges.) If, on the other hand, you wish to keep track of changes as they occur, you can define properties on your POCO classes as virtual. This will cause EF to create dynamic proxies using reflection that subclass your POCO types in order to notify the object state manager of changes in real-time.
Similarly, when you mark a navigation property as virtual, EF generates dynamic proxies to enable on-demand (a.k.a. lazy) loading of related entities. In fact, the CTP ships with a T4 template to generate POCO classes based on the entity data model you’ve defined. The template not only generates virtual navigation properties, but also defines a special FixupCollection for these properties that modifies the other side of an association when the value of a navigation property is changed. For example, when the Category of a Product is changed (say from Beverages to Condiments), the Products properties of both Beverages and Condiments are updated to reflect the change.
Better N-Tier Support
One of the most common reasons for using POCO’s is to conceal details of object persistence behind a data access layer. Similarly, POCO’s are often passed across tiers as Data Transfer Objects (DTO’s) in a service-oriented application. One problem that has vexed distributed application developers is how to pass changes from one tier to another so they can be persisted in a disconnected manner. At first, the Entity Framework team decided to provide a low-level API for applying changes to detached objects, requiring you to roll your own change-tracking mechanism, but much of the feedback they received included requests for a built-in change-tracking mechanism and an end-to-end architecture for building n-tier apps that use the Entity Framework. In response to this feedback, the team delivered a T4 template for Self-Tracking Entities, which allows each entity to keep track of its own change state, so that you can pass it to a remote service where the entire object graph is persisted.
Each generated class implements the IObjectWithChangeTracker interface, which has a ChangeTracker property, as well as methods to mark the entity as Unchanged, Modified, Added or Deleted. ObjectChangeTracker not only records the object state but also maintains original values of reference properties as well as objects that have been added to or deleted from collection properties. This means that the client can make changes to an Order by adding or removing OrderDetails, and the state of the Order and OrderDetails can be sent to the service for persistence. Change state is serialized because each entity is marked with a [DataContract] attribute and the ChangeTracker is marked with a [DataMember] attribute.
Self-Tracking Entities allow the client to track change state on entities without needing to reference the Entity Framework assembly, System.Data.Entity. The change-tracking code is auto-generated by the T4 template, which produces the ObjectChangeTracker class and a generic FixupChangeTrackingCollection class that does relationship fix-up and implements ICollection to keep track of additions and deletions. Deleted items are cached so they can be removed later when changes are persisted on the service side.
Your service would typically expose a method for retrieving an object graph such as Order and OrderDetails, and a method for updating the object graph. In the update method, you would call ApplyChanges<T> on the appropriate entity set. ApplyChanges is an extension method residing in the Microsoft.Data.Entity.CTP assembly, which you bring into scope by adding a using directive for the Microsoft.Data.Entity namespace. This method accepts an object graph as well as a pointer to a method that returns an IEntityChangeTracker, which is responsible for informing the object state manager of these changes. The Self-Tracking Entities T4 template also generates a EntityChangeTrackerAdapter class that implements IEntityChangeTracker by translating the persistence-neutral ObjectState enum (Added, Deleted, Modified, Unchanged) to the equivalent EF-specific EntityState enum values. The code in your service-side update method could look something like this:
public Order UpdateOrder(Order order)
using (NorthwindEntities ctx = new NorthwindEntities())
The nice part about POCO and N-Tier support is the use of T4 templates for code generation, which you can customize or replace altogether. This greatly reduces the amount of code you would otherwise have to write. In addition to these and other features, there are numerous bug fixes and improvements that provide a richer and more rewarding development experience, making the Entity Framework 4.0 an attractive candidate when selecting an ORM tool for building distributed line of business applications.