Conventions for OData Metadata¶
CSDL XML Metadata¶
The service generator tool requires as input an OData Common Schema Definition Language (CSDL) XML metadata file (for OData version 2.0 or 4.0). OData version 4.0 is recommended, unless it is necessary to accommodate clients that use pre-4.0 OData frameworks.
The metadata in the CSDL XML document defines a data model for the business objects upon which the OData service will operate.
The following conventions facilitate the generation and deployment of OData services that are compatible with the Server OData API (the Java class library that supports the implementation of the generated OData services), as well as to ensure the smooth interoperability of the generated services with a variety of OData client frameworks and SDKs.
-
Use the file extension
.csdl.xmlfor OData CSDL XML files. When using Visual Studio Code (with theSAP Mobile Services OData CSDL modelerextension) or SAP Business Application Studio, files with this extension can be viewed graphically via the context menu (right mouse click, thenReopen Editor With... OData CSDL Modeler). -
For OData 4.0 models, include the
xmlns:xsiandxsi:schemaLocationattributes in theedmx:Edmxroot element, as shown in the following example, to enable enhanced CSDL XML editing if you have installed the XML Language Support by Red Hat extension for Visual Studio Code. -
For the Schema Namespace, consider using Java package naming conventions or .NET Namespace Conventions.
-
For the EntityContainer Name, provide a name that describes the service, such as
BankService,ShopServiceetc. -
Use Upper Camel Case for naming types and properties, e.g.
TravelAgency. -
Use singular names for complex/entity types, e.g.
Address,Customer. -
Use plural names for entity sets, e.g.
Customers, or use theSetsuffix, e.g.CustomerSet. -
Avoid using property/type names that differ only in case (e.g.
customerandCustomer) or that differ only in the absence/presence of underscores (e.g.Customerand_Customer). -
For key property names, use the
IDsuffix appended to the entity type name, e.g.CustomerIDfor entity typeCustomer. -
To support automatic server-side key generation for primary keys, it is recommended that you use non-composite numeric keys of type
Edm.Int64. If you are certain that 32-bit integer keys are sufficient, then useEdm.Int32. If in any doubt, then use 64-bit integer keys. You can also use typeEdm.Decimalfor key properties, but is likely to use more space in the database thanEdm.Int64. -
You can also use the
Edm.GuidandEdm.Stringkey types, but they are not recommended because they will take up more space within the database. If you use typeEdm.String, then specifyMaxLength="36"to ensure that the key column will be large enough to contain a generated GUID. String-typed properties containing database-generated sequence numbers are not supported. -
Properties of type
Edm.BinaryandEdm.Stringshould include theMaxLengthfacet unless they are intentionally of unbounded length. This ensures an optimal selection of database column type (e.g.varbinaryversusblob,varcharversusclob). It is assumed thatMaxLengthfor strings specifies the number of Unicode code points, which may result in4*MaxLengthbeing used as a database column length if the database usesUTF-8column encoding. -
Properties of type
Edm.Decimalshould specifyScaleandPrecisionfacets. This ensures an optimal selection of database column type. Note that an unspecified scale defaults to zero (only integer values can be stored in the database column ifScaleis unspecified). -
Use an explicit
Nullablefacet for properties. Since the default for OData CSDL XML isNullable="true", it is easy to accidentally have nullable properties. Accidental nullability of properties may be a burden to client application developers, especially when using languages such as Kotlin and Swift that have strong support for nullable types. It may result in client developers having to defensively program against the possibility of properties having null values when it was not intended. -
Primary keys embedded within complex-typed properties are not currently supported.
-
Foreign keys embedded within complex-typed properties are not currently supported.
-
Stream properties embedded within complex-typed properties are not currently supported.
-
Navigation properties embedded within complex-typed properties are not currently supported.
-
Bidirectional relationships (with navigation properties in both directions) are preferred.
-
Foreign keys (specified with
ReferentialConstraint) should be used wherever applicable. This permits the automatic implementation of OData features such as:$expandqueries, deep insert, bind operations, and link operations. -
Use
OnDelete Action="Cascade"for navigation properties where the deletion of a parent entity should cascade to automatic deletion of the related child entities. -
Use
OnDelete Action="None"for navigation properties where the deletion of a parent entity should be restricted (disallowed) when there are still existing related child entities. -
Use the
Core.Immutableannotation for any properties that cannot be changed after an entity is created. SeeCustomerandDateOfBirthin the example below. -
Use
Validationannotations to express simple declarative validation rules. SeeOrder/Quantityin the example below. Currently, such validation annotations might be utilized by client frameworks, but the server runtime does not itself enforce them. Use entity listeners for server-side enforcement of validation rules.
Example Document¶
<edmx:Edmx Version="4.0" xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://docs.oasis-open.org/odata/ns/edmx http://docs.oasis-open.org/odata/odata/v4.0/os/schemas/edmx.xsd http://docs.oasis-open.org/odata/ns/edm http://docs.oasis-open.org/odata/odata/v4.0/os/schemas/edm.xsd">
<edmx:Reference Uri="https://oasis-tcs.github.io/odata-vocabularies/vocabularies/Org.OData.Core.V1.xml">
<edmx:Include Namespace="Org.OData.Core.V1" Alias="Core"/>
</edmx:Reference>
<edmx:Reference Uri="https://oasis-tcs.github.io/odata-vocabularies/vocabularies/Org.OData.Validation.V1.xml">
<edmx:Include Namespace="Org.OData.Validation.V1" Alias="Validation"/>
</edmx:Reference>
<edmx:DataServices>
<Schema Namespace="example" xmlns="http://docs.oasis-open.org/odata/ns/edm" Alias="Self">
<ComplexType Name="Address">
<Property Name="Number" Type="Edm.String" MaxLength="100" Nullable="true"/>
<Property Name="Street" Type="Edm.String" MaxLength="100" Nullable="true"/>
<Property Name="City" Type="Edm.String" MaxLength="100" Nullable="false"/>
<Property Name="Region" Type="Edm.String" MaxLength="100" Nullable="true"/>
<Property Name="Country" Type="Edm.String" MaxLength="100" Nullable="true"/>
<Property Name="PostalCode" Type="Self.PostalCode" Nullable="true"/>
</ComplexType>
<ComplexType Name="PostalCode">
<Property Name="AreaCode" Type="Edm.String" MaxLength="2" Nullable="false"/>
<Property Name="DistrictCode" Type="Edm.String" MaxLength="2" Nullable="false"/>
<Property Name="SectorCode" Type="Edm.String" MaxLength="1" Nullable="false"/>
<Property Name="UnitCode" Type="Edm.String" MaxLength="2" Nullable="false"/>
</ComplexType>
<EntityType Name="Customer">
<Annotation Term="Core.Description" String="Customer entity type"/>
<Key>
<PropertyRef Name="CustomerID"/>
</Key>
<Property Name="CustomerID" Type="Edm.Int64" Nullable="false">
<Annotation Term="Core.Description" String="Customer primary key"/>
</Property>
<Property Name="Name" Type="Edm.String" MaxLength="100" Nullable="false"/>
<Property Name="HomeAddress" Type="Self.Address" Nullable="false"/>
<Property Name="WorkAddress" Type="Self.Address" Nullable="true"/>
<Property Name="PlaceOfWork" Type="Edm.String" MaxLength="100" Nullable="false"/>
<Property Name="DateOfBirth" Type="Edm.Date" Nullable="false">
<Annotation Term="Core.Immutable"/>
</Property>
<NavigationProperty Name="Orders" Type="Collection(Self.Order)" Partner="Customer">
<OnDelete Action="Cascade"/>
</NavigationProperty>
</EntityType>
<EntityType Name="Order">
<Annotation Term="Core.Description" String="Order entity type"/>
<Key>
<PropertyRef Name="OrderID"/>
</Key>
<Property Name="OrderID" Type="Edm.Int64" Nullable="false">
<Annotation Term="Core.Description" String="Order primary key"/>
</Property>
<Property Name="CustomerID" Type="Edm.Int64" Nullable="true"/>
<Property Name="Product" Type="Edm.String" MaxLength="100" Nullable="false"/>
<Property Name="Quantity" Type="Edm.Int32" Nullable="false">
<Annotation Term="Validation.Minimum" Decimal="1"/>
</Property>
<NavigationProperty Name="Customer" Type="Self.Customer" Nullable="true" Partner="Orders">
<ReferentialConstraint Property="CustomerID" ReferencedProperty="CustomerID"/>
</NavigationProperty>
</EntityType>
<EntityContainer Name="ExampleService">
<EntitySet Name="Customers" EntityType="Self.Customer">
<Annotation Term="Core.Description" String="Customers entity set"/>
<NavigationPropertyBinding Path="Orders" Target="Orders"/>
</EntitySet>
<EntitySet Name="Orders" EntityType="Self.Order">
<Annotation Term="Core.Description" String="Orders entity set"/>
<NavigationPropertyBinding Path="Customer" Target="Customers"/>
</EntitySet>
</EntityContainer>
</Schema>
</edmx:DataServices>
</edmx:Edmx>
Annotation Terms¶
OData 4.0 defines various standard
annotation terms such
An annotation term as the Core.Description term, which can be used to annotate any CSDL metadata element with a description.
In general, annotation terms provide for metadata extensibility.
Standard annotation term definitions are grouped together in standard annotation vocabularies.
Custom Vocabularies¶
The service generator tool defines various custom annotation vocabularies. Each custom vocabulary defines a number of annotation terms. You can use these custom annotation terms to annotate CSDL model elements to enable various features for the generated OData service.
The following sections describe each of the custom annotation vocabularies.
Note
All examples use OData 4.0 syntax, but the service generator tool allows OData 4.0 annotations to be used within CSDL XML files for all supported OData versions (2.0, 4.0).
Note
All custom annotation terms defined by the following vocabularies that can be applied to an entity type can also be applied (instead) to an entity set. OData permits multiple entity sets to share the same entity type. However such sharing of an entity type by multiple entity sets is uncommon in practice, so it is recommended to annotate entity types rather than annotating entity sets.
Cache Vocabulary¶
To use terms within the Cache vocabulary include a Reference element for the com.sap.cloud.server.odata.cache.v1 namespace.
This vocabulary defines terms for configuration of caching within a cache databases.
Example:
<edmx:Edmx ...>
<edmx:Reference Uri="vocabularies/com.sap.cloud.server.odata.cache.v1.xml">
<edmx:Include Namespace="com.sap.cloud.server.odata.cache.v1" Alias="Cache"/>
</edmx:Reference>
...
</edmx:Edmx>
Security Vocabulary¶
To use terms within the Security vocabulary include a Reference element for the com.sap.cloud.server.odata.security.v1 namespace.
This vocabulary defines terms for annotating elements with required security roles.
Example:
<edmx:Edmx ...>
<edmx:Reference Uri="vocabularies/com.sap.cloud.server.odata.security.v1.xml">
<edmx:Include Namespace="com.sap.cloud.server.odata.security.v1" Alias="Security"/>
</edmx:Reference>
...
</edmx:Edmx>
SQL Vocabulary¶
To use terms within the SQL vocabulary include a Reference element for the com.sap.cloud.server.odata.sql.v1 namespace.
This vocabulary defines terms relating to the underlying SQL database used by the generated OData service for entity storage.
Example:
<edmx:Edmx ...>
<edmx:Reference Uri="vocabularies/com.sap.cloud.server.odata.sql.v1.xml">
<edmx:Include Namespace="com.sap.cloud.server.odata.sql.v1" Alias="SQL"/>
</edmx:Reference>
...
</edmx:Edmx>
Externalized Annotations¶
Sometimes it's convenient to place server-side annotations in a separate CSDL XML file named server-annotations.xml instead of the main CSDL XML file. This approach is useful when Caching OData Back-End Systems.
Example original main metadata XML, e.g. metadata.csdl.xml with annotations:
<edmx:Edmx ...>
<edmx:Reference Uri="vocabularies/com.sap.cloud.server.odata.cache.v1.xml">
<edmx:Include Namespace="com.sap.cloud.server.odata.cache.v1" Alias="Cache"/>
</edmx:Reference>
<edmx:Reference Uri="vocabularies/com.sap.cloud.server.odata.sql.v1.xml">
<edmx:Include Namespace="com.sap.cloud.server.odata.sql.v1" Alias="SQL"/>
</edmx:Reference>
<edmx:DataServices>
<Schema Namespace="example" xmlns="http://docs.oasis-open.org/odata/ns/edm" Alias="Self">
<EntityType Name="Customer">
<Annotation Term="Cache.RefreshBy" String="loadAll"/>
<Key>
<PropertyRef Name="CustomerID"/>
</Key>
<Property Name="CustomerID" Type="Edm.Int64" Nullable="false"/>
<Property Name="Name" Type="Edm.String" MaxLength="100" Nullable="false"/>
</EntityType>
<EntityContainer Name="ExampleService">
<Annotation Term="SQL.CacheDatabase"/>
<EntitySet Name="Customers" EntityType="Self.Customer">
<Annotation Term="SQL.Indexes">
<Collection>
<Record Type="SQL.Index">
<PropertyValue Property="Name" String="NameIndex"/>
<PropertyValue Property="Properties">
<Collection>
<PropertyPath>Name</PropertyPath>
</Collection>
</PropertyValue>
</Record>
</Collection>
</Annotation>
</EntitySet>
</EntityContainer>
</Schema>
</edmx:DataServices>
</edmx:Edmx>
Example modified main metadata XML, e.g. metadata.csdl.xml without annotations:
<edmx:Edmx ...>
<edmx:DataServices>
<Schema Namespace="example" xmlns="http://docs.oasis-open.org/odata/ns/edm" Alias="Self">
<EntityType Name="Customer">
<Key>
<PropertyRef Name="CustomerID"/>
</Key>
<Property Name="CustomerID" Type="Edm.Int64" Nullable="false"/>
<Property Name="Name" Type="Edm.String" MaxLength="100" Nullable="false"/>
</EntityType>
<EntityContainer Name="ExampleService">
<EntitySet Name="Customers" EntityType="Self.Customer"/>
</EntityContainer>
</Schema>
</edmx:DataServices>
</edmx:Edmx>
Example externalized annotations, moved from main metadata XML into server-annotations.xml:
<edmx:Edmx ...>
<edmx:Reference Uri="vocabularies/com.sap.cloud.server.odata.cache.v1.xml">
<edmx:Include Namespace="com.sap.cloud.server.odata.cache.v1" Alias="Cache"/>
</edmx:Reference>
<edmx:Reference Uri="vocabularies/com.sap.cloud.server.odata.sql.v1.xml">
<edmx:Include Namespace="com.sap.cloud.server.odata.sql.v1" Alias="SQL"/>
</edmx:Reference>
<edmx:DataServices>
<Schema Namespace="example" xmlns="http://docs.oasis-open.org/odata/ns/edm" Alias="Self">
<Annotations Target="Self.Customer">
<Annotation Term="Cache.RefreshBy" String="loadAll"/>
</Annotations>
<Annotations Target="Self.ExampleService">
<Annotation Term="SQL.CacheDatabase"/>
</Annotations>
<Annotations Target="Self.ExampleService/Customers">
<Annotation Term="SQL.Indexes">
<Collection>
<Record Type="SQL.Index">
<PropertyValue Property="Name" String="NameIndex"/>
<PropertyValue Property="Properties">
<Collection>
<PropertyPath>Name</PropertyPath>
</Collection>
</PropertyValue>
</Record>
</Collection>
</Annotation>
</Annotations>
</Schema>
</edmx:DataServices>
</edmx:Edmx>
Externalized Definitions¶
Sometimes it's convenient to place server-side definitions in a separate CSDL XML file named filter-definitions, instead of the main CSDL XML file. This approach is useful when Caching OData Back-End Systems, and when adding extra Filter Entities. These entities should be visible to clients but don't exist in the OData back-end system.
Example externalized annotations, not in main metadata XML, but instead in filter-definitions.xml:
<edmx:Edmx ...>
<edmx:DataServices>
<edmx:Reference Uri="vocabularies/com.sap.cloud.server.odata.sql.v1.xml">
<edmx:Include Namespace="com.sap.cloud.server.odata.sql.v1" Alias="SQL"/>
</edmx:Reference>
<Schema Namespace="example" xmlns="http://docs.oasis-open.org/odata/ns/edm" Alias="Self">
<EntityType Name="RegionFilter">
<Annotation Term="SQL.ClientFilter"/>
<Key>
<PropertyRef Name="FilterID"/>
</Key>
<Property Name="FilterID" Type="Edm.Int64" Nullable="false"/>
<Property Name="RegionID" Type="Edm.Int64" Nullable="false"/>
</EntityType>
<EntityContainer Name="ExampleService">
<EntitySet Name="RegionFilterSet" EntityType="Self.RegionFilter"/>
</EntityContainer>
</Schema>
</edmx:DataServices>
</edmx:Edmx>