Let’s go back 15 years ago when I started professionally developing software and using relational databases. The relational database was a great tool to deal with data. No more did we have to use flat files to store our data with tools named Paradox, dBase and other such data management systems. We now had more flexibility to build our solutions and the new architecture pattern Client-Server that was the rage for all developers circa 1996.
Relational databases gave us the SQL language and that in turn allowed the developers of the day to quickly create, read, update and delete (CRUD) data that was stored on the server and build our applications at the client. Life was great (for me at least) because I knew nothing different. If I wanted to learn about a new database, I would get the schema either in a text file that had a set of CREATE SQL calls for all the objects that made up the database. If we were really lucky we received a printed graph of the database that gave the developers a nice graphical layout of the data tables and views with the relationships between the tables and views. It took time to understand the meaning of the database but that is what we did.
In 2007 I experienced a glimpse into the future that shook my world. It was when I started experimenting with a new incubation project from Microsoft called Astoria. I saw it at first as way to get data across HTTP and that excited me. What blew my mind after really learning about it was Metadata. Compared to the time I spent discovering the meaning and shape of the data schema of a relational database that a DBA created and handed to me, Metadata was the bridge into a new frontier.
Let’s step back and take a look at what Metadata is and how it works within OData. Metadata is defined broadly as “Data about Data”. Think of metadata as the Dewey Decimal we learned as kids when we visited the library. The card catalog does not carry the information about the content inside the books but it does describe the books and information that the books contain in a broad level to allow readers to find and locate the books they are interested in on the shelves. Metadata does the same for us as users of the data that the OData feeds allow us to consume.
In OData, the Service Metadata Document allows the consumers of the data provided by the OData feed to understand the Entity Data Model (Day 3 of the series). The Service Metadata Document does not carry any details of the data held within the data repository of the OData feed. It contains the roadmap to the data. In the SOAP world the Service Metadata Document would be the equivalent of the WSDL file. Let’s look at the structure of the Service Metadata Document (in XML) and walk through its map. We will use the Baseball Statistics OData feed found here as our example.
We first need to query the Service Metadata Document from the OData feed. How this is accomplished is my using the $metadata query option at the base of the OData feed as shown below:
http://baseball-stats.info/OData/baseballstats.svc/$metadata
The Service Metadata Document is returned in the payload after the $metadata query and has three parts: the EDMX which is made up of the OData Model and also the Entity Collection.
- <?xmlversion=“1.0“encoding=“UTF8“standalone=“true“?>
- <edmx:Edmxxmlns:edmx=“http://schemas.microsoft.com/ado/2007/06/edmx“Version=“1.0“>
- <edmx:DataServicesm:DataServiceVersion=“1.0“xmlns:m=“http://schemas.microsoft.com/ado/2007/08/dataservices/metadata“>
- <Schemaxmlns=“http://schemas.microsoft.com/ado/2008/09/edm“xmlns:d=“http://schemas.microsoft.com/ado/2007/08/dataservices“xmlns:m=“http://schemas.microsoft.com/ado/2007/08/dataservices/metadata“Namespace=“BaseballStatsModel“>
- <EntityTypeName=“Allstar“>
- <Key>
- <PropertyRefName=“playerID“/>
- <PropertyRefName=“yearID“/>
- <PropertyRefName=“lgID“/>
- </Key>
- <PropertyName=“playerID“FixedLength=“false“Unicode=“true“MaxLength=“10“Nullable=“false“Type=“Edm.String“/>
- <PropertyName=“yearID“Nullable=“false“Type=“Edm.Int16“/> <PropertyName=“lgID“FixedLength=“false“Unicode=“true“MaxLength=“2“Nullable=“false“Type=“Edm.String“/>
- <NavigationPropertyName=“Player“ToRole=“Player“FromRole=“Allstar“Relationship=“BaseballStatsModel.FK_Allstar_Player“/>
- </EntityType>
- <EntityTypeName=“AllstarFull“>
- <Key>
- <PropertyRefName=“playerID“/>
- <PropertyRefName=“yearID“/>
- <PropertyRefName=“gameNum“/>
- </Key>
- <PropertyName=“playerID“FixedLength=“false“Unicode=“true“MaxLength=“10“Nullable=“false“Type=“Edm.String“/>
- <PropertyName=“yearID“Nullable=“false“Type=“Edm.Int16“/> <PropertyName=“gameNum“Nullable=“false“Type=“Edm.Int16“/>
- <PropertyName=“gameID“FixedLength=“false“Unicode=“true“MaxLength=“12“Nullable=“true“Type=“Edm.String“/>
- <PropertyName=“teamID“FixedLength=“false“Unicode=“true“MaxLength=“3“Nullable=“true“Type=“Edm.String“/>
- <PropertyName=“lgID“FixedLength=“false“Unicode=“true“MaxLength=“2“Nullable=“true“Type=“Edm.String“/>
- <PropertyName=“GP“Nullable=“true“Type=“Edm.Int16“/> <PropertyName=“startingPos“Nullable=“true“Type=“Edm.Int16“/>
- <NavigationPropertyName=“Player“ToRole=“Player“FromRole=“AllstarFull“Relationship=“BaseballStatsModel.FK_AllstarFull_Player“/>
- </EntityType>
- …
- <EntityTypeName=“PitchingTotals“>
- <Key>
- <PropertyRefName=“playerID“/>
- </Key>
- <PropertyName=“playerID“FixedLength=“false“Unicode=“true“MaxLength=“10“Nullable=“false“Type=“Edm.String“/>
- <PropertyName=“W“Nullable=“true“Type=“Edm.Int32“/> <PropertyName=“L“Nullable=“true“Type=“Edm.Int32“/>
- <PropertyName=“G“Nullable=“true“Type=“Edm.Int32“/> <PropertyName=“GS“Nullable=“true“Type=“Edm.Int32“/>
- <PropertyName=“CG“Nullable=“true“Type=“Edm.Int32“/> <PropertyName=“SHO“Nullable=“true“Type=“Edm.Int32“/>
- <PropertyName=“SV“Nullable=“true“Type=“Edm.Int32“/> <PropertyName=“IPouts“Nullable=“true“Type=“Edm.Int32“/>
- <PropertyName=“H“Nullable=“true“Type=“Edm.Int32“/> <PropertyName=“ER“Nullable=“true“Type=“Edm.Int32“/>
- <PropertyName=“HR“Nullable=“true“Type=“Edm.Int32“/> <PropertyName=“BB“Nullable=“true“Type=“Edm.Int32“/>
- <PropertyName=“SO“Nullable=“true“Type=“Edm.Int32“/> <PropertyName=“BAOpp“Nullable=“true“Type=“Edm.Double“/>
- <PropertyName=“IBB“Nullable=“true“Type=“Edm.Int32“/> <PropertyName=“WP“Nullable=“true“Type=“Edm.Int32“/>
- <PropertyName=“HBP“Nullable=“true“Type=“Edm.Int32“/> <PropertyName=“BK“Nullable=“true“Type=“Edm.Int32“/>
- <PropertyName=“BFP“Nullable=“true“Type=“Edm.Int32“/> <PropertyName=“GF“Nullable=“true“Type=“Edm.Int32“/>
- <PropertyName=“R“Nullable=“true“Type=“Edm.Int32“/> <PropertyName=“SH“Nullable=“true“Type=“Edm.Int32“/>
- <PropertyName=“SF“Nullable=“true“Type=“Edm.Int32“/> <PropertyName=“GIDP“Nullable=“true“Type=“Edm.Int32“/>
- </EntityType>
- <AssociationName=“FK_Allstar_Player“>
- <EndType=“BaseballStatsModel.Player“Multiplicity=“1“Role=“Player“/> <EndType=“BaseballStatsModel.Allstar“Multiplicity=“*“Role=“Allstar“/> <ReferentialConstraint>
- <PrincipalRole=“Player“>
- <PropertyRefName=“playerID“/>
- </Principal> <DependentRole=“Allstar“>
- <PropertyRefName=“playerID“/>
- </Dependent>
- </ReferentialConstraint>
- </Association>
- <AssociationName=“FK_AllstarFull_Player“>
- <EndType=“BaseballStatsModel.Player“Multiplicity=“1“Role=“Player“/> <EndType=“BaseballStatsModel.AllstarFull“Multiplicity=“*“Role=“AllstarFull“/> <ReferentialConstraint>
- <PrincipalRole=“Player“>
- <PropertyRefName=“playerID“/>
- </Principal> <DependentRole=“AllstarFull“>
- <PropertyRefName=“playerID“/>
- </Dependent>
- </ReferentialConstraint>
- </Association>
- …
- <AssociationName=“FK_TeamsHalf_Teams“>
- <EndType=“BaseballStatsModel.Team“Multiplicity=“1“Role=“Team“/> <EndType=“BaseballStatsModel.TeamHalf“Multiplicity=“*“Role=“TeamHalf“/> <ReferentialConstraint>
- <PrincipalRole=“Team“>
- <PropertyRefName=“yearID“/>
- <PropertyRefName=“lgID“/>
- <PropertyRefName=“teamID“/>
- </Principal> <DependentRole=“TeamHalf“>
- <PropertyRefName=“yearID“/>
- <PropertyRefName=“lgID“/>
- <PropertyRefName=“teamID“/>
- </Dependent>
- </ReferentialConstraint>
- </Association>
- </Schema>
- <Schemaxmlns=“http://schemas.microsoft.com/ado/2008/09/edm“xmlns:d=“http://schemas.microsoft.com/ado/2007/08/dataservices“xmlns:m=“http://schemas.microsoft.com/ado/2007/08/dataservices/metadata“Namespace=“BaseballStatsOData“>
- <EntityContainerName=“BaseballStatsEntities“xmlns:p7=“http://schemas.microsoft.com/ado/2009/02/edm/annotation“m:IsDefaultEntityContainer=“true“p7:LazyLoadingEnabled=“true“>
- <EntitySetName=“Allstar“EntityType=“BaseballStatsModel.Allstar“/>
- <EntitySetName=“AllstarFull“EntityType=“BaseballStatsModel.AllstarFull“/>
- …
- <EntitySetName=“PitchingTotals“EntityType=“BaseballStatsModel.PitchingTotals“/>
- <AssociationSetName=“FK_Allstar_Player“Association=“BaseballStatsModel.FK_Allstar_Player“>
- <EndRole=“Player“EntitySet=“Player“/>
- <EndRole=“Allstar“EntitySet=“Allstar“/>
- </AssociationSet>
- <AssociationSetName=“FK_AllstarFull_Player“Association=“BaseballStatsModel.FK_AllstarFull_Player“>
- <EndRole=“Player“EntitySet=“Player“/>
- <EndRole=“AllstarFull“EntitySet=“AllstarFull“/>
- </AssociationSet>
- …
- <AssociationSetName=“FK_TeamsHalf_Teams“Association=“BaseballStatsModel.FK_TeamsHalf_Teams“>
- <EndRole=“Team“EntitySet=“Team“/>
- <EndRole=“TeamHalf“EntitySet=“TeamHalf“/>
- </AssociationSet>
- </EntityContainer>
- </Schema>
- </edmx:DataServices>
- </edmx:Edmx>
OData Model
The OData Model section of the Service Metadata Document has the following structure:
- <SchemaNamespace=“”>
- <EntityTypeName=“”>
- <Key>
- <PropertyRefName=“”/>
- </Key>
- <PropertyName=“”Type=“Edm.String“/>
- <NavigationPropertyName=“”ToRole=“”FromRole=“”Relationship=“”/>
- </EntityType>
- …
- <AssociationName=“”>
- <EndType=“”Multiplicity=“1“Role=“”/>
- <EndType=“”Multiplicity=“*“Role=“”/>
- <ReferentialConstraint>
- <PrincipalRole=“”>
- <PropertyRefName=“”/>
- </Principal>
- <DependentRole=“”>
- <PropertyRefName=“”/>
- </Dependent>
- </ReferentialConstraint>
- </Association>
- …
- </Schema>
The purpose of the OData Model section is to give the detailed information for each object in the OData feed. The first objects aggregated in the OData Model are the EntityTypes with their full descriptions including the Key, Properties and NavigationProperties. Next is the set of Associations that are in the Entity Data Model. In addition each metadata type is fully described based on the data such as the EntityType’s Property details such as type, if the Property is nullable, the maximum length of the data of the property and/or many more that give us a complete view of the Property. Another example is the details of the Association which will give the entities the Association join together as well as the type of join.
Entity Collection
The Entity Collection is also part of the Service Metadata Document and it follows the OData Model in the payload that is returned from the OData feed. An example of the Entity Collection follows:
- <Schemaxmlns=“http://schemas.microsoft.com/ado/2008/09/edm“xmlns:d=“http://schemas.microsoft.com/ado/2007/08/dataservices“xmlns:m=“http://schemas.microsoft.com/ado/2007/08/dataservices/metadata“Namespace=“MyOData“>
- <EntityContainerName=“MyEntities“xmlns:p7=“http://schemas.microsoft.com/ado/2009/02/edm/annotation“m:IsDefaultEntityContainer=“true“p7:LazyLoadingEnabled=“true“>
- <EntitySetName=“Entity1“EntityType=“ODataModel.Entity1“/>
- <EntitySetName=“Entity2“EntityType=“ODataModel.Entity2“/>
- …
- <EntitySetName=“ODataModel“EntityType=“ODataModel.EntityN“/>
- <AssociationSetName=“FK_Entity1_Entity2“Association=“ODataModel.FK_Entity1_Entity2“>
- <EndRole=“Entity1“EntitySet=“Entity1“/>
- <EndRole=“Entity2“EntitySet=“Entity2“/>
- </AssociationSet>
- …
- <AssociationSetName=“FK_Entity1_EntityN“Association=“ODataModel.FK_Entity1_EntityN“>
- <EndRole=“Entity1“EntitySet=“Entity1“/>
- <EndRole=“EntityN“EntitySet=“EntityN“/>
- </AssociationSet>
- </EntityContainer>
- </Schema>
The Entity Collection does not contain the details of the Entities and Associations contained in the OData feed. It is only the references from the OData Model and gives the roadmap to quickly transverse the landscape from the OData feed. The Entity Collection is made up of the following sections:
- EntitySet
- AssociationSet
You can learn more about the Entity Collection from the third day of the blog series. I will not go through it again since it is the same content and details from that post.
Why should I love the Metadata?
When I speak and present to the community about OData, I think most people do not get the beauty of Metadata. I have a slide deck where I show an iceberg and the data is shown above the water and metadata below. I have the image in this post so you can see what I mean. I do not assume that the Metadata is larger in size or greater in importance to the Data of the OData feed. What I try to get across to the audience is that we may either not see the importance of Metadata or the power we receive from it. It really is the hidden secret of OData (and the Semantic Web).
what most developers also do not know is that is they have worked with OData within Visual Studio 2010 they have benefited from the Metadata of OData. You may ask how and if so lets look at adding a Service Reference to a VS10 project.When we need to add a reference to an OData feed into our VS10 project we usually add it through the “Add Service Reference” feature as shown below:
Going through the Wizard to allow the project access to the OData feed is not anything but the Wizard gathering the metadata from the OData feed and creating the proxy classes based on the EntityTypes from the Service Metadata Document. You can see that the Wizard after discovering the metadata from the OData feed gives us the list of the EntityTypes in the Wizard as shown below. With the proxy classes generated your application has the local Model that can be used to build, retrieve and handle the data from the OData.
Now that we have looked at the Metadata of OData, the moving parts of the Metadata Service Document and finally a real world example on how we work with the Metadata each day, I hope this gives you a little more appreciation for OData and also for the protocol.
Hi,thanks for making this post available, it is easy to understand. I was wondering if we can use metadata to generate C# strong typed classes; something similar to what you do with WSDL with the .net utilities?
Thank you