skip to content

Entity Data Model

Entity and complex types with keys, entity sets, singletons and navigation properties with containment turn a model into a resource graph. Interviewers ask because generic OData tooling rests on it.

part ofAPI stylesoverview, primer and where to startread it →
on this pageshow

questions

6

In OData's entity data model, what distinguishes an entity type from a complex type, and how do you choose between them?

level: middleimportance: must knowfreq 30%

answer

  1. identity versus value
  2. a Key of non-nullable primitives
  3. one PropertyRef per key part
  4. keyless means no independent lifecycle

basics

~20 s

An OData entity type declares a key, so each instance has identity and can be addressed, created, updated and deleted on its own; a complex type is keyless and exists only as a property value inside the entity that holds it.

solid answer

~40 s

Both are structured types built from structural and navigation properties. An `EntityType` declares a `Key` - one or more `PropertyRef`s to non-nullable primitive, enumeration or type-definition properties - so an instance is unique within its entity set and has its own URL, ETag and lifecycle. A `ComplexType` has no key: an `Address` on an `Order` cannot be referenced, created, updated or deleted independently of the order that holds it. Choose an entity type when the thing is shared, referenced from elsewhere or changes on its own; choose a complex type for a value that belongs to one owner, such as a shipping address or a money amount. A composite key, such as `OrderID` plus `LineNumber`, is simply a `Key` listing several `PropertyRef`s.

go deeper

for a junior

Recall the one-line rule: entity types have a key and identity, complex types have neither and live inside an entity. Be ready to give one example of each from an ordering domain.

for a middle

Explain the key rules - at least one non-nullable property of an allowed primitive, enumeration or type-definition type - and show a composite key as several PropertyRef entries.

for a senior

Argue the modelling choice from lifecycle and sharing: what breaks when a shared concept is copied as a complex value, and what an unnecessary entity costs in keys, sets and relationships.

for a principal

Frame the type split as a contract decision: identity exposed in the model becomes URLs and ETags clients depend on, so promoting or demoting a type later is a breaking change.

## Structured types in the entity data model OData's **entity data model (EDM)** is the abstract model a service exposes: the types, the containers that publish them, the relationships between them and the operations on them. Two of its type kinds are **structured types**, meaning they are composed of named properties: - **Entity types** - named structured types *with a key*. Their instances are **entities**. - **Complex types** - named structured types *without a key*. Their instances are values. Both can declare **structural properties** (typed as a primitive, complex or enumeration type, or a collection of those) and **navigation properties** (references to entity types). Both support single inheritance through `BaseType`, can be `Abstract`, and can be `OpenType`. The one difference that drives everything else is the key. ## Entity types: identity through a key An entity's **key** is the set of properties that uniquely identifies it *within an entity set*. The CSDL specification puts hard rules on it: - The key **MUST** consist of at least one property, listed as `PropertyRef` elements inside `Key`. - Key properties **MUST NOT** be nullable. - Each key property **MUST** be typed with an enumeration type, a type definition based on an allowed primitive, or one of `Edm.Boolean`, `Edm.Byte`, `Edm.Date`, `Edm.DateTimeOffset`, `Edm.Decimal`, `Edm.Duration`, `Edm.Guid`, `Edm.Int16`, `Edm.Int32`, `Edm.Int64`, `Edm.SByte`, `Edm.String` or `Edm.TimeOfDay`. `Edm.Double`, `Edm.Single`, `Edm.Binary` and `Edm.Stream` are not on the list. - An entity type **MAY** declare a key only if it does not inherit one, so a derived type never adds a second key. - A type used for an entity set **MUST** have a key, declared or inherited. Uniqueness is per entity set, not per service: if two entity sets share one entity type, the same key value in each identifies **two different entities**, each with its own entity-id. A **composite key** is just a key with several parts. An order line identified by its order and its line number looks like this: ```xml <EntityType Name="OrderLine"> <Key> <PropertyRef Name="OrderID" /> <PropertyRef Name="LineNumber" /> </Key> <Property Name="OrderID" Type="Edm.Int32" Nullable="false" /> <Property Name="LineNumber" Type="Edm.Int16" Nullable="false" /> <Property Name="Quantity" Type="Edm.Decimal" Nullable="false" /> </EntityType> ``` A key part may also sit inside a non-nullable, single-valued complex property; the key then gives it an **alias**, a simple identifier used in URL key predicates instead of a slash-separated path. ## Complex types: values without identity The CSDL text is blunt: the lack of a key means instances of complex types **cannot be referenced, created, updated or deleted independently** of an entity. A complex value lives inside its owner and changes when the owner changes. Complex types exist to group properties into reusable structures - an `Address` used for both `ShippingAddress` and `BillingAddress`, or a `Money` value with `Amount` and `Currency`. They also serve as parameter and return types of operations. A complex type may declare navigation properties to entity types, but those navigation properties **MUST NOT** specify a `Partner`. ## Choosing between them | Question about the concept | Entity type | Complex type | |---|---|---| | Is it referenced from more than one place? | Yes - link to it | No - copy the value | | Does it change on its own schedule? | Yes - own lifecycle | No - changes with its owner | | Does it need its own URL or ETag? | Yes | No | | Is it a value whose identity is its content? | Rarely | Typically | In an order-management model, `Customer`, `Order`, `Product` and `OrderLine` are entity types; `Address` and `Money` are complex types. The classic mistake is promoting every nested structure to an entity "for flexibility": each one then needs a key, an entity set or a containment relationship, and relationships to maintain. The opposite mistake - modelling a shared customer as a complex value copied into each order - loses the ability to update it once. ## The other named types | Kind | What it is | Example | |---|---|---| | `EnumType` | Named members with integer values over `Edm.Byte`, `Edm.SByte`, `Edm.Int16`, `Edm.Int32` (default) or `Edm.Int64`; `IsFlags` allows combined values | `OrderStatus` | | `TypeDefinition` | A named specialisation of one primitive type, optionally fixing facets such as `MaxLength` | `OrderNumber` over `Edm.String` | Neither is structured; both can type properties and operation parameters, and both may be key property types - a type definition only when its underlying type is on the permitted list. ## What changed in OData 4.01 1. **Key-less entity types.** In OData 4.01 an entity type used only for a singleton or a single-valued navigation property need not declare a key; in 4.0 it must. 2. **Keys reaching into a related entity.** A 4.01 key may include key properties of a directly related entity reached through a single-valued, non-nullable navigation property - with an alias, and with all of that related entity's key properties included.

  • Where do EnumType and TypeDefinition fit beside entity and complex types?
    Both are named primitive-like types, not structured ones. An `EnumType` has members with integer values over an underlying integer type, `Edm.Int32` by default; with `IsFlags="true"` a value may combine several members as a bitwise OR. A `TypeDefinition` specialises one primitive type and may fix facets such as `MaxLength` or `Precision`, so `OrderNumber` means the same constrained `Edm.String` wherever it is used.
  • Can a key property live inside a complex property?
    Yes, if it is a non-nullable primitive property of a non-nullable, single-valued complex property, recursively. The key must then declare an alias for it - a simple identifier used in URL key predicates instead of the slash-separated path, which would otherwise need percent-encoding. The alias is not used in the query part of URLs, where ordinary property paths work.
  • Can a complex type have navigation properties?
    Yes. A complex type may declare navigation properties to entity types, so an `Address` could reference a `Country` entity. What it cannot do is declare a `Partner` on them: the CSDL forbids partners on complex types' navigation properties, so such a relationship is not declared bidirectional.

saying these in an interview costs you the question

  • A complex type instance can be shared by several entities and referenced by an id.
  • Any property can be part of the key, including a nullable string or an Edm.Double.
  • Entity keys are unique across the whole service, not per entity set.
  • A derived entity type can add its own key property beside the inherited key.
  • Complex types cannot declare navigation properties at all.
open as a page

In OData, how does a function differ from an action, and what changes when either one is bound rather than unbound?

level: middleimportance: must knowfreq 28%

basics

~20 s

An OData function MUST NOT have observable side effects, must return data and is called with GET; an action may change state, may return nothing and is called with POST. Bound operations are called on a resource; unbound ones are static, reached mainly through container imports.

open as a page

In an OData entity container, how does an entity set differ from a singleton, and when would you model a resource as a singleton?

level: juniorimportance: should knowfreq 18%

basics

~20 s

An OData entity set is a named top-level collection of entities addressed by key; a singleton is a named top-level single entity addressed by its name alone, suited to one-per-service resources such as the order desk's settings or the caller's own profile.

open as a page

In OData's entity data model, how is a relationship like Order-to-Customer declared, and what do Partner and navigation property bindings tell a client?

level: middleimportance: should knowfreq 14%

basics

~20 s

An OData relationship is a NavigationProperty typed as another entity type or a collection of it. Partner names the inverse property that leads back; a NavigationPropertyBinding on the entity set says which entity set holds the related entities.

open as a page

In OData, what does declaring a navigation property with ContainsTarget change about the contained entities' keys, identity and URLs?

level: seniorimportance: should knowfreq 10%

basics

~20 s

With ContainsTarget, each parent gets an implicit entity set of its contained entities: their keys need be unique only within that parent, their canonical URL is the parent's plus the property and key, and no top-level entity set may also hold them.

open as a page

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?

level: seniorimportance: nice to knowfreq 6%

basics

~20 s

Use 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.

open as a page