Model Domain Objects with the Entity Framework: Sidebar 1: Retrieve and Update Entities Across Tiers with WCF and the EntityBag
One of the primary objectives of the LINQ to SQL and Entity Framework (EF) teams was to avoid cluttering domain object models with original-value and modified-value properties for value-based concurrency conflict management, as well as other persistence-related elements and attributes. The goal was to deliver a characteristic called persistence ignorance, which most developers find desirable in an OR/M tool. Total persistence ignorance requires domain objects to be free of any artifacts that relate to physical storage of the object--database type (relational or object) and vendor, or file format (.XML or .CSV). Objects that meet this criterion are called plain old CLR objects
(POCOs), which cannot be required to inherit from a specified base class or implement a particular interface.
HIGH PRICE TO PAY
The EF team came close to the goal by enabling IPOCO, which is POCO that must implement specified interfaces. EF v1 requires IEntityWithChangeTracker for notifying EF of property value changes and IEntityWithRelationships for persisted relationships; IEntityWithKey was optional until beta 3, but might regain optional status in a later EF release. IPOCO requires developers to avoid classes autogenerated by the EDM Designer and write their own domain object classes. This is a high price to pay for partial persistence ignorance.
Minimizing persistence artifacts in domain objects increases the complexity of top-level O/RM objects, such as ObjectContext and its ObjectStateManager, and prevents their serialization by Windows Communication Foundation (WCF)'s DataContractSerializer (DCS), which is required to serialize object graphs with EntitySet relationships. DCS can't serialize object graphs with related EntitySet and EntityReferences to XML–called bidirectional serialization–because DCS can't handle cyclic references. These issues with DCS prevented the LINQ to SQL team from providing "an out-of-the-box n-tier story" for v1. The team proposed a serializable "mini connectionless DataContext" to solve the problem, but didn't deliver it.
Daniel Simmons, an ADO.NET dev lead, announced on Jan. 19, 2008, and in subsequent blog posts, a new top-level object called EntityBag. The EntityBag identifies a single "root" object in the graph from an ObjectContext in a middle-tier WCF service and serializes it with the DCS to the same "root" of a private, client-side ObjectContext that doesn't have a database connection. The client passes the objects to a presentation layer for data display and editing.
To persist the changes, the DCS serializes the EntityBag's contents with original and modified values back to the service, which updates the database. The client must run .NET and EF, and have access to a copy of the service's EDM, so it's not interoperable with Java or other programming platforms. However, if service interoperability was a major problem for data-intensive projects, developers wouldn't be sending DataSets as SOAP messages.
EntityBag includes a Mode enumeration that defines four states: Constructed, Deserialized, LocalContext, and Unwrapped. A client request, usually a LINQ for Entities query, for one or more entities instantiates the ObjectBag in Constructed mode, creates a ContextSnapshot containing the values, serializes the ContextSnapshot, and sends it to the client as the SOAP payload.
Setting up tracing with the WCF Configuration Tool lets you inspect the payload with the ServiceModel Trace Viewer (SvcTraceViewer.exe). The client deserializes the ContextSnapshot and enters Deserialized mode. A request from the presentation layer for the deserialized object or invoking the Delete() method instantiates a local ObjectContext that's populated by the deserialized ContextSnapshot in LocalContext mode.
Invoking the SaveChanges() method after adding, deleting, or modifying the graph in the presentation layer sends the graph with its original and modified values to the service, which populates another ObjectContext and persists the changes. At that point the ObjectContext is in a non-functional Unwrapped mode and ready to be disposed. Simmons delivered the EF counterpart to the erstwhile "mini connectionless data context."
EntityBag won't be a part of EF v1 and it's likely that EntityBag will continue its informal project status for at least the next few months. Simmons noted in his "EntityBag--Wrap Up and Future Directions" post that tracking modifications to complex types (aka value types) as properties will present a problem and updates to graphs that enforce referential integrity will require careful sequencing. He also said that future releases will remove a few current (minor) limitations and minimize the message size in the transition from LocalContext to Unwrapped mode by only sending changed values. Possible future improvements include validation operations prior to data store updates and code generation to create more granular service contracts.
The real significance of Entity Bag is that it proves that ObjectContexts for complex graphs can be serialized to implement WCF SOAP services without modifying the underlying Object Services or other EF layer.
To download the Entity Framework EntityBag (Perseus Project) sample and find links to Daniel Simmons' code walk-through go to tinyurl.com/yph65t. For the Logging EntityBag's SOAP Request and Response Messages, an OakLeaf blog post, click here. This blog post also contains details on the Service Trace Viewer (Steps 10+), including instructions and sample outputs.