D3: Modeling Part 1 – Real-World Relationships
While there are a great many scenarios for “standard” associations in the EF, there are cases where an application requires something a little more sophisticated. DPMud is no different. It has two cases.
Relationships with Payload
A key part of the D3 model is the map which consists of rooms which have a many-many relationship. Each room can have exits which lead to other rooms. This would be pretty straightforward except we need to attach properties to the relationship for the name of the exit, whether or not it is locked, etc. In UML terms this would be called an association class. Ideally the EF would be able to directly support this kind of relationship, but there are a number of interesting semantics, and in the end the properties on the relationship amount to an entity with all the same sorts of behaviors. The main problem, though, is that you need both the ability to navigate directly across the relationship skipping the properties and the ability to access the entity “in the middle”.
The only real option here is to replace two entities and one relationship plus payload with three entities and two relationships:
You can easily enumerate the exits from a room, and if you want to transit from that room through an exit to an adjacent room, then you can navigate from the room to the exit and then to the destination room.
(You might notice, by the way, that there’s only one navigation property on Room and only one on Exit even though each of these participates in two associations. I accomplished this by manually editing the XML of the EDMX to get rid of the extra navigation properties. I call them extra because they confuse the common scenarios—it’s interesting to enumerate the exits leading out from a room but not to list the entrances, and similarly it’s much more useful to look at the destination of an exit rather than the source.)
There are lots of kinds of conditions you might want to put on a relationship—maybe the customer entity only has a salesman if their purchases total more than $5000 over the last month or something. In general the EDM doesn’t provide facilities for describing arbitrary conditions on associations, behaviors of that sort should be implemented in business logic on the entity classes. There is a pattern which comes up occasionally, including in D3, which is worth looking at: Sometimes an entity might participate in multiple relationships but where only one of the relationships can be present at any one time.
In the case of D3, this shows up with Items. An Item can be in a Room, it can be carried by an Actor, or (at least in theory, we haven’t added this feature to DPMud yet) inside another Item like a backpack. While the EDM can’t directly capture this kind of constraint in the conceptual model, this can be accomplished fairly easily in business logic in the entities, and it’s quite easy to add constraints in the database itself that require that only one of the three FK properties on Item be non-null at any one time.
You might say that we could create a base type for Items, Rooms and Actors, called “ItemContainer” or something like that and then a relationship between Item and ItemContainer, and that approach could certainly work. There are consequences, though. First of all, we would either have to put all three entity types in the same entity set which is less usable (default context pattern would have one ItemContainers set rather than three sets: Items, Rooms and Actors). Also there could be some perf implications since deeper inheritance hierarchies with relationships affecting subtypes can cost more during view generation and verification and the default database schema would put all three entity types into a single table which probably doesn’t give the ideal locality of reference for common queries, etc. If we try to address these concerns by creating separate entity sets for the three subtypes rather than one top-level entity set, then the EF runtime could handle it, but the EF designer could not, and we would be forced to do all subsequent modeling by editing the XML directly which is certainly less than ideal. In the end, three separate relationships with a constraint just seems to be a smoother path, so that’s the approach we take in D3.
I suppose that’s all for tonight. Next time we’ll continue looking at D3 modeling decisions. Hopefully I’ll be able to work through the issues so that I can set up a source repository and start sharing out code before too long.
P.S. As we walk through this process together, if a better approach occurs to you about one of these design decisions or if I gloss over something that needs more explanation, please don’t hesitate to say so. It’s my hope that we can all learn from one another.