Sunday, June 17, 2007

Entity Framework: Complex Types Redux in Beta 2

The June 2006 ADO.NET Tech Preview: Entity Data Model specification asserts: "An EntityType can have one or more properties of the specified SimpleType, ComplexType, or RowType. Properties can be either single-valued or multi-valued." I've assumed (until now) that the Entity Framework (EF), which implements the Entity Data Model (EDM), supported complex types.

Eric Evans calls complex types value objects in Domain-Driven Design: Tackling Complexity in the Heart of Software and defines them as follows:

An object that represents a descriptive aspect of the domain with no conceptual identity is called a VALUE OBJECT. VALUE OBJECTS are instantiated to represent elements of the design that we care about only for what they are, not who or which they are.

The classic value object that appears in most domains is an Address type. In ordinary contexts, the Address instance that Order.ShipAddress or Vendor.Address represents isn't important if the Address property values are correct. The existence of multiple instances of the Address type having the same or slight variation in property values (such as ST and Street or E and East) isn't significant. However, Address is an entity for gas, water, electric, telephone, and cable TV utilities because it represents a service and billing endpoint and might be the location of a utility's assets, such as a set-top box.

I also assumed that I'd need the missing EDM Designer to define complex types because the current (Beta 1) EDM Wizard doesn't have a feature to define a table as persisting a ComplexType or group table fields to generate a property of the ComplexType. I didn't want to spend the time to research the syntax and hand-code the mapping (MSL) and conceptual (CSDL) layer XML files to test a feature that might change in the EF CTP promised for June.

So I was surprised to learn from Danny Simmons' Non-scalar Value Objects aka "Complex Types" post of late last night that the EF doesn't support complex types. I cringed as I read:

At the moment the EF CTPs do not support complex types, and someone has asked for that feature we just can't support. [Emphasis added.]

Why can't we support it?  Well, first off the lack of a property or properties that can be the key is fundamental.  Secondly, if you think about it, what would insert and delete operations that affect one but not both of the entities in a row really mean?  Things get crazy really quick.

I fully expected to see the persistence ignorance (PI) ruckus raised anew with even greater ferocity. The inability to generate a property of the ComplexType from a group of fields in the table that persists the containing entity appeared to me to be an especially egregious failure to support PI.

Earlier in the post, Danny had mentioned that "the system doesn't let you map two entities to the same row in the same table." That restriction didn't appear to me to prevent mapping an entity and one or more complex types to a single row. For example, a SalesOrder entity might have BillAddress and ShipAddress complex types. Something wasn't adding up here.

Finally, I read the [April Fool!] punch line:

Fortunately, we did decide to implement complex types, and they will appear in an upcoming CTP.

Update 6/18/2007: Danny says in his comment that he intends to clarify his post.

Here's the full story from Danny's answer to Szymon Kobalczyk's June 15, 2007 EDM: Mapping two entities from single table message in the ADO.NET Orcas forum:

A complex type is a value type that does not have identity of its own but it does have structure.  You can define a complex type for address info, and that type can appear as a property of one or more entities.  When we generate code for the model you will have a separate class for the complex type and it will appear as a direct member of the entity class, so you will get a structure much like you define above, but you won't actually have a navigation property from the supplier to the address you would instead just have a property of the supplier whose type is an address. 

With complex types you can actually map a complex type's properties to different columns in the same row with the entity that contains the type--in fact the identity of the complex type is slaved to the identity of the entity which contains it so it must be part of the same row (or rows in multiple tables if you are using entity splitting or table-per-type inheritance or something like that where parts of a single entity appear in multiple tables). 

The nice thing about this kind of construct is that you can fill out the address class with methods and validation and such and share that logic across all instances of an address regardless of which entity type they appear in--it's just that each of those instances is merely a part of the containing entity rather than an entity of its own.

It's my suspicion that the 2nd approach is what you want, but you'll just have to wait until beta 2 before that's available.

The ability to validate a common Address type, as well as AddressUS, AddressCA, AddressUK, AddressEU, etc. subtypes, is an extremely important feature of object/relational mapping (O/RM) tool. EF's complex types support inheritance, according to the spec. Value objects usually map to NHibernate components. I'm not certain how LLBLGen Pro handles them. Frans?

Recursive complex types (a complex type containing a complex type) would be nice in v1. They're presently in the spec's "EDM Future Directions" section, along with conditional association, many-to-many relationships with payload, n-ary relationships, relationship inheritance, and dynamic entity extensibility.

Update 6/18/2007: Danny Simmons' comment indicates that v1 won't implement ComplexType inheritance but will support recursive (nested) complex types. I'd gladly forego ComplexType nesting for inheritance and I believe most other EF users would, too.

Recommended Reading: Agile Joe [Ocampo] has almost-chapter-length posts about entities and value objects with different analogies, as well as domain-driven design as a whole. He offers a substantial number of posts about NHibernate, also. (Some of Joe's posts are even longer than mine.)

More Recommended Reading: John Papa says today that his July 2007 Data Points column has been posted to the MSDN Magazine Web site as "ADO.NET Entity Framework Overview". MSDN should have given him the 7,000 words from his draft version. I was surprised to see  "ADO.NET in the next release of Visual Studio® code-named 'Orcas' features the new Entity Framework" in the online lead, because the Entity Framework hasn't been a part of VS 2008 since April 28, 2007.


Daniel Simmons said...

Apparently I was unclear in my blog posting with the comment about the feature which we just can't support. What I meant is that we can't support having two ENTITIES in the same row (and won't ever be able to for the reasons I listed in my posting). We can, though, and will support one entity plus one or more complex types in a single row. Sorry if this was misleading. I'll make an edit to the post to clarify.

With regard to the level of complex type support, though, unfortunatley we will not support inheritance of complex types in the first release. That's something we'll likely add in a later release. On the other hand, we will support nesting complex types (so that an address complex type could contain a two part zip complex type or something like that).

- Danny

--rj said...


The specific sentence that troubled me is "At the moment the EF CTPs do not support complex types, and someone has asked for that feature we just can't support." This does more than imply that "we just can't support complex types."

Your post's topic is value objects/complex types, which reinforces the preceding interpretation.

Also, as I mentioned in the update, inheritance seems to me to be more important than nested (recursive) complex types.


Daniel Simmons said...

Understood. My apologies for the poorly written sentence. I tried to fix that with a small edit to the post.

With regard to inheritance vs. nesting, the decision about which to support was more a matter of which could fit with the relatively small amount of time we have left than a judgment on relative importance. We'll have to add inheritance next release.

- Danny