In an OData model, when would you extend an entity type with a derived type rather than declare it OpenType, and what does each cost a client?
answer
- BaseType: single inheritance
- the key is inherited, never added
- type-cast segment narrows
- dynamic properties are always nullable
- open stays open down the hierarchy
basics
~20 sUse a derived entity type (BaseType) when a variant has a known, stable shape: it inherits the key and is reachable by type cast. Use OpenType when instances carry undeclared per-instance properties, which clients learn only from payloads.
solid answer
~50 sA derived type names a `BaseType` and inherits its key, structural and navigation properties; it may not declare a key of its own. An `Orders` entity set typed `Sales.Order` may hold `Sales.RushOrder` instances, and a client narrows to them with a type-cast segment, `/Orders/Sales.RushOrder`; JSON payloads at minimal or full metadata identify such instances with type control information. `Abstract="true"` forbids direct instances of a base type. `OpenType="true"` instead lets clients add uniquely named dynamic properties when creating or updating an instance: they are undeclared, always nullable - a missing one reads as null - may not reuse a declared name, and every type derived from an open type must be open too. Choose derivation for known variants you want typed and filterable by type; reserve open types for genuinely per-instance attributes such as customer-defined tags.
go deeper
Recall that BaseType gives single inheritance with the key inherited, and that OpenType lets instances carry extra, undeclared properties.
Explain the mechanics: entity sets hold subtypes, type-cast segments narrow to them, and dynamic properties are nullable, unnamed in the model and treated as null when missing.
Argue the modelling choice from what clients lose: typed fields and validation with derived types versus run-time flexibility and untyped data with open types.
Decide how much schema flexibility the service should offer at all, since open types shift data governance from the model to every client and tenant that writes them.
## Inheritance with BaseType OData's entity data model supports **single inheritance** for entity types and for complex types. A type names its parent in `BaseType`: - A derived entity type **inherits the key** as well as the structural and navigation properties of its base type. - It **MAY** declare a key only if it does not inherit one, so a derived type never adds a second key. - `BaseType` **MUST NOT** introduce an inheritance cycle. - A type marked `Abstract="true"` cannot have instances; an abstract entity type **MUST NOT** inherit from a non-abstract one. - An entity set **MUST** contain only instances of its declared type or its subtypes, so `Orders` typed `Sales.Order` may hold `Sales.RushOrder` and `Sales.SubscriptionOrder` instances. Clients reach subtypes through **type-cast segments**, which use the derived type's qualified name: 1. On a collection, `/Orders/Sales.RushOrder` restricts the result to rush orders; the result may be empty. 2. On a single entity, `/Orders(1001)/Sales.RushOrder` returns 404 Not Found if order 1001 is not a rush order. 3. Inside a Boolean expression, a cast evaluates to null for instances of another type, and a property of a derived type is addressed by prefixing it with the type's qualified name. In JSON requests, and in responses at minimal or full metadata, an instance whose type is derived from the declared type carries **type control information**, so a client knows which subtype it received. ## Open types and dynamic properties Marking a type `OpenType="true"` lets clients add properties that the model does not declare: - When creating an instance of an open type, extra property values in the request body **MUST** be treated by the service as **dynamic properties** and added to the instance. - A dynamic property **cannot** have the same name as a declared property. - **All dynamic properties are nullable**, and a missing dynamic property is defined to be the same as one with value null - so a filter on it treats instances without it as null rather than failing. - A type derived from an open type **MUST** also be open. - Because dynamic properties are undeclared, their types travel in payloads: a JSON value whose type cannot be inferred from its JSON form, such as a date or a non-Double number, carries type control information. One trap: the CSDL also says services **MAY** return additional properties on instances of *any* structured type, open or not, and clients **MUST** always be prepared for that. `OpenType` is about clients being allowed to **write** undeclared properties, not about whether extra properties can appear in a response. ## Comparing the two | Aspect | Derived type (`BaseType`) | Open type (`OpenType="true"`) | |---|---|---| | Shape known to the model | Yes - every property declared | Only the declared part | | Varies by | Subtype | Individual instance | | Typed client code | Possible from the model | Only for declared properties | | Addressing | Type-cast segment | Property name, treated as null when missing | | Constraints | Declared types, facets, nullability | Always nullable, types from the payload | | Typical use | Rush, subscription and standard orders | Customer-defined tags or attributes | ## Evolving the model The protocol lists **safe model changes** that do not require a new service version, among them adding a nullable property and adding new entity or complex types; it adds that clients **SHOULD** be prepared to receive properties and derived types not previously defined. That makes both mechanisms evolvable, with one interaction to remember: if a property is later declared with the name of an existing dynamic property, it must have the same type (or a base type) as that dynamic property. ## Choosing 1. If the variants are known and each has its own fields, model **derived types**; clients get typed fields, type-cast filtering and validation by the service. 2. If attributes are defined per customer or per tenant at run time, an **open type** lets them flow without a model change, at the price of untyped, always-nullable data. 3. If a value has a fixed set of possibilities but no extra fields, neither is needed - an `EnumType` property is simpler. 4. Avoid opening a type "just in case": every type derived from it must be open too, and clients lose the guarantee that the model describes what they may write.
- What does a client get for /Orders(1001)/Sales.RushOrder when order 1001 is a plain Order?404 Not Found: a type-cast segment on a single entity that is not an instance of the derived type identifies no resource. On a collection the cast filters instead, so `/Orders/Sales.RushOrder` returns only rush orders and may be empty; inside a Boolean expression the cast evaluates to null for instances of another type.
- Why must a type derived from an open type also be open?The CSDL requires it outright. The reasoning is substitutability: an instance of the derived type can appear wherever the base type is expected, including in an entity set whose clients write dynamic properties. If the derived type were closed, the same write would succeed or fail depending on each instance's runtime type, which a client could not predict from the declared type.
saying these in an interview costs you the question
- A derived entity type can declare its own key beside the inherited one.
- OData allows multiple inheritance, so a type can extend Order and Shipment.
- Clients only receive declared properties unless the type is marked OpenType.
- A dynamic property can be made non-nullable when it is first written.
- A type derived from an open type can be closed to forbid dynamic properties.