From Day 2 of the series, we looked at data types that are present in the OData protocol. These data types allow for the richness of the data experience. Next we will look at taking these data types and crafting the entities that make up the OData feed.
An OData feed is very much like a database with tables, foreign keys and stored procedures. The data from an OData feed does not have to originate in a relational database but this is the first and most important type of data repository that OData can represent. The OData’s Entity Data Model (EDM) is simple but elegant in its representation of the data. It is designed to handle the data but also the relationships between entities that allow for the rich data experience we experience.
The Roadmap of the EDM
Just like a roadmap the EDM acts as a guide that allows us to travel the OData feed’s paths. It furnishes us the collections of entities and all details needed to understand the entities within its map. The EDM also gives us the paths or associations between the unique Entities of the OData feed. The ability to navigate easily without knowing much about the layout of the OData land is where OData gives us the freedom to have the Data Experiences (DX) we need for today’s solutions and consumer expectations.
The Entitles in an OData feed represents a specific type of data or Entity Type. The Entity Types of an OData feed could simulate some real world item, concept or idea such as a Book, College Course or Business Plan. The Entity Type contains properties that must adhere to the data types allowed and provided by the OData protocol or be of a Complex Type (explained later). To exist in the OData protocol, the Entity Type must be unique and so must have a unique identifier that allow for the Entities of that Entity Type to be located and used within the OData Feed. The Entity Key can be composed of one or more Entity properties and will be a unique property for the Entity Type. Most Entity Types will have a single property but that is not a requirement. An example of a multiple property Entity Type key is on my baseball stats OData feed and the Batting Entity Type.
The Batting Entity Type in my baseball stats OData feed is comprised of the following properties:
- playerID – the Player Entity Type’s Key
- yearID – the year of the baseball season
- stint – baseball players could be traded and traded back to a team in the same season so needs to be tracked
- teamID – the team the Player played for in the stint
- lgID – the league of the team the player played for in the stint
As you can see OData can handle complex or composite Keys and allow for almost anything we give it. The collection of Entities is grouped in an Entity Set within the OData feed.
The following is an example of an Entity Type from the baseball statistics OData feed.
<EntityType Name="Player"> <Key> <PropertyRef Name="playerID"/> </Key> <Property Name="playerID" FixedLength="false" Unicode="true" MaxLength="10" Nullable="false" Type="Edm.String"/> <Property Name="hofID" FixedLength="false" Unicode="true" MaxLength="10" Nullable="true" Type="Edm.String"/> <Property Name="birthYear" Nullable="true" Type="Edm.Int16"/> <Property Name="birthMonth" Nullable="true" Type="Edm.Int16"/> <Property Name="birthDay" Nullable="true" Type="Edm.Int16"/> <Property Name="birthCountry" FixedLength="false" Unicode="true" MaxLength="50" Nullable="true" Type="Edm.String"/> <Property Name="birthState" FixedLength="false" Unicode="true" MaxLength="2" Nullable="true" Type="Edm.String"/> <Property Name="birthCity" FixedLength="false" Unicode="true" MaxLength="50" Nullable="true" Type="Edm.String"/> <Property Name="deathYear" Nullable="true" Type="Edm.Int16"/> <Property Name="deathMonth" Nullable="true" Type="Edm.Int16"/> <Property Name="deathDay" Nullable="true" Type="Edm.Int16"/> <Property Name="deathCountry" FixedLength="false" Unicode="true" MaxLength="50" Nullable="true" Type="Edm.String"/> <Property Name="deathState" FixedLength="false" Unicode="true" MaxLength="2" Nullable="true" Type="Edm.String"/> <Property Name="deathCity" FixedLength="false" Unicode="true" MaxLength="50" Nullable="true" Type="Edm.String"/> <Property Name="nameFirst" FixedLength="false" Unicode="true" MaxLength="50" Nullable="true" Type="Edm.String"/> <Property Name="nameLast" FixedLength="false" Unicode="true" MaxLength="50" Nullable="true" Type="Edm.String"/> <Property Name="nameNote" FixedLength="false" Unicode="true" MaxLength="255" Nullable="true" Type="Edm.String"/> <Property Name="nameGiven" FixedLength="false" Unicode="true" MaxLength="255" Nullable="true" Type="Edm.String"/> <Property Name="nameNick" FixedLength="false" Unicode="true" MaxLength="255" Nullable="true" Type="Edm.String"/> <Property Name="weight" Nullable="true" Type="Edm.Int16"/> <Property Name="height" Nullable="true" Type="Edm.Double"/> <Property Name="bats" FixedLength="false" Unicode="true" MaxLength="1" Nullable="true" Type="Edm.String"/> <Property Name="throws" FixedLength="false" Unicode="true" MaxLength="1" Nullable="true" Type="Edm.String"/> <Property Name="debut" Nullable="true" Type="Edm.DateTime"/> <Property Name="finalGame" Nullable="true" Type="Edm.DateTime"/> <Property Name="college" FixedLength="false" Unicode="true" MaxLength="50" Nullable="true" Type="Edm.String"/> <NavigationProperty Name="Allstar" ToRole="Allstar" FromRole="Player" Relationship="BaseballStatsModel.FK_Allstar_Player"/> <NavigationProperty Name="AllstarFull" ToRole="AllstarFull" FromRole="Player" Relationship="BaseballStatsModel.FK_AllstarFull_Player"/> <NavigationProperty Name="Appearance" ToRole="Appearance" FromRole="Player" Relationship="BaseballStatsModel.FK_Appearances_Player"/> <NavigationProperty Name="AwardPlayer" ToRole="AwardPlayer" FromRole="Player" Relationship="BaseballStatsModel.FK_AwardsPlayers_Player"/> <NavigationProperty Name="AwardSharePlayer" ToRole="AwardSharePlayer" FromRole="Player" Relationship="BaseballStatsModel.FK_AwardsSharePlayers_Player"/> <NavigationProperty Name="Batting" ToRole="Batting" FromRole="Player" Relationship="BaseballStatsModel.FK_Batting_Player"/> <NavigationProperty Name="BattingPost" ToRole="BattingPost" FromRole="Player" Relationship="BaseballStatsModel.FK_BattingPost_Player"/> <NavigationProperty Name="Fielding" ToRole="Fielding" FromRole="Player" Relationship="BaseballStatsModel.FK_Fielding_Player"/> <NavigationProperty Name="FieldingOF" ToRole="FieldingOF" FromRole="Player" Relationship="BaseballStatsModel.FK_FieldingOF_Player"/> <NavigationProperty Name="FieldingPost" ToRole="FieldingPost" FromRole="Player" Relationship="BaseballStatsModel.FK_FieldingPost_Player"/> <NavigationProperty Name="Pitching" ToRole="Pitching" FromRole="Player" Relationship="BaseballStatsModel.FK_Pitching_Player"/> <N avigationProperty Name="PitchingPost" ToRole="PitchingPost" FromRole="Player" Relationship="BaseballStatsModel.FK_PitchingPost_Player"/> </EntityType>
The OData feed can also contain Associations that define the connections (or roads from our earlier analogy) between the Entity Type and another Entity Type. An example would be the Association between a Baseball Player and his Batting Statistics. An Entity Type’s collection of Associations is called an Association Set and along with the Entity Set is assembled into an Entity Container. The Link or Navigation Property that makes up the Entity Type must act on behalf of a defined Association in the OData feed. The following is an example of what the Entity Container looks when querying the Service Metadata Document.
<EntityContainer Name="SolutionEntities" xmlns:p7="http://schemas.microsoft.com/ado/2009/02/edm/annotation" m:IsDefaultEntityContainer="true" p7:LazyLoadingEnabled="true"> <EntitySet Name="Entity1" EntityType="SolutionModel.Entity1"/> <EntitySet Name="Entity2" EntityType="SolutionModel.Entity2"/> ... <EntitySet Name="EntityN" EntityType="SolutionModel.EntityN"/> <AssociationSet Name="FK_Entity2_Entity1" Association="SolutionModel.FK_Entity2_Entity1"> <End Role="Entity2" EntitySet="Entity2"/> <End Role="Entity1" EntitySet="Entity1"/> </AssociationSet> <AssociationSet Name="FK_Entity1_EntityN" Association="SolutionModel.FK_Entity1_EntityN"> <End Role="Entity1" EntitySet="Entity1"/> <End Role="EntityN" EntitySet="EntityN"/> </AssociationSet> ... <AssociationSet Name="FK_Entity2_EntityN" Association="SolutionModel.FK_Entity2_EntityN"> <End Role="Entity2" EntitySet="Entity2"/> <End Role="EntityN" EntitySet="EntityN"/> </AssociationSet> </EntityContainer>
A special type of structured types that can be in an OData feed is a Complex Type. The Complex Type is a collection of properties without a unique Key. This type can only be contained within an Entity Type as a property or live outside of all Entity Types as temporary data.
The last item that can be represented in the Entity Data Model is a Service Operation. We will look at the Service Operation later in the series. For now understand that Service Operations are functions that can be called on an OData feed. A Service Operation behaves similar to the Stored Procedures in a relational database.
We have looked at the Entity Data Model and what defines it in the world of OData. This will allow us to lay down a foundation for future blog posts in this series to explain the behaviors and results that we expect from our OData feeds.
Hi, Chris,
I enjoyed the first three days. Where are the next six?
Cheers,
–rj
Pingback: keezmovie