UPDATE: I created a new version of the client-side T4 template for generating trackable DTO’s. This completely decouples the client from the service by producing POCO classes based on the service metadata, instead of on the entity data model used by the service. I have also updated the client T4 template to accept a max received message size parameter.
WEBINAR: On March 30, 2010, I conducted an online seminar on Trackable DTOs. The screencast video is now available to be streamed or downloaded. The slides and code for the presentation are also available.
Download updated code for this post here.
Not long ago my friend and colleague Richard Blewett wrote a blog post on Self-Tracking Entities in EF4, in which he questioned the service-orientation of Self-Tracking Entities in EF4. While STE’s are placed in an assembly that does not reference the Entity Framework, the way in which change state is preserved in an STE is overly complex because it tries make it easier for EF to transmit those changes to the ObjectStateManager on the service side. If you look closely at the ObjectChangeTracker class that is generated for the client, you’ll see that it maintains metadata for navigation properties with items that have been added or removed, as well as original values and extended properties. Requiring a Java client to implement all that is asking an awful lot, and it couples the client too tightly to the service implementation.
About a year ago I wrote an article for MSDN Magazine on how to track change-state on the client and transmit it to a service for persistence using LINQ to SQL, Entity Framework, or some other data access stack. Each entity has an ObjectState property indicating its change state (Unchanged, Added, Modified, Deleted), which is sent to the service where it is used to perform inserts, updates and deletes against a database. The beauty of this approach is that it allows multiple changes to be sent to a service in a single round trip, where they can all be saved in a single transaction. The classic example of this is an Order with Order Details that have been added, modified or removed. An UpdateOrder method in the Data Access Layer can simply read the ObjectState property to perform corresponding inserts, updates and deletes.
Fast forward to Entity Framework 4.0 and Visual Studio 2010. This week I implemented Trackable Data Transfer Objects with EF4 using the same basic architecture that I wrote about in the article. I use two sets of T4 templates (a code-generation technology built into Visual Studio): One set is used by the Data Access Layer on the service side to generate both the ObjectContext container class and POCO classes that serve as DTO’s but have an ObjectState property. On the client side there is an assembly containing another T4 template that generates DTO’s that also have an ObjectState property. On the client-side, DTO’s have a Tracking property (to turn change-tracking on and off) and navigation properties that are of type ChangeTrackingCollection<T>. This collection is placed in a separate ClientChangeTracker assembly and marks entities as Modified when they change, or as Added or Deleted when they are added to the collection or removed from it.
The goal of this design is to keep change state as minimal as possible: a simple ObjectState enum, which is exposed as a data contract by the service and can be easily implemented by a non-.NET client. The client-side DTO’s are generated by a T4 template that consumes the service metadata, which results in a clean separation between the client and any persistence concerns (see UPDATE section above).
On the service-side there is a ServiceChangeTracker assembly that has a TrackingHelper class with an ApplyChanges methods that extends ObjectContext by walking an object graph and informing the ObjectStateManager of entity state based on the ObjectState property. When a DAL method invokes SaveChanges on the ObjectContext, inserts, updates and deletes are persisted to the database in the scope of a single transaction. The DAL can then call AcceptChanges in TrackingHelper to restore objects to an Unchanged state and return the updated object to the client with database-calculated values, such as identity and concurrency fields.
Trackable DTO’s match entities defined in the conceptual model, which allows us to leverage POCO support in EF4 to avoid creating two sets of classes and manually copying data between them. As such they represent an approach that combines the simplicity of self-tracking entities with the flexibility of DTO’s, without the extra baggage and complexity of STE’s. The use of two separate sets of T4 templates allows us to decouple client DTO’s from the service DTO’s, applying the rules of data contract versioning so that they can diverge from one another in a robust fashion.