In the previous two sections, we walk you through the required aspects to build an Edm model by directly using ODatalib or leveraging ODataModelBuilder fluent API in WebApi OData.
Obvious, there are many codes you should add to develop a simple Customer-Order business model. However, Web API OData also provides a simple method by using ODataConventionModelBuilder to do the same thing. It’s called convention model builder and can extremely reduce your workload.
Convention model builder uses a set of pre-defined rules (called conventions) to help model builder identify Edm types, keys, associations, relationships, etc automatically, and build them into the final Edm data model.
In this section, we will go through all conventions used in convention model builder. First, let’s see how to build the Edm model using ODataConventionModelBuilder.
CLR Models
We also use the Customer-Order business model presented in abstract section.
Build the Edm Model
The following codes can add all related entity types, complex types, enum type and the corresponding entity sets into the Edm model:
It will generate the below metadata document:
Note: We omit the function/action building because it’s same as non-convention model builder.
Conventions
Wow, how the convention model builder do that! Actually, convention model builder uses a set of pre-defined rules (called conventions) to achieve this.
If you open the source code for ODataConventionModelBuilder, You can find the following codes at the beginning of the ODataConventionModelBuilder class:
Where lists the conventions wrapped in convention model builder. However, in ODataConventionModelBuilder, there are some conventions which can’t be clearly listed. Let’s walk you through these conventions one by one with some relevant attributes & annotations to illustrate the convention model builder.
Type Inheritance Identify Convention
Rule: Only derived types can be walked.
For example:
By using convention builder:
It will generate the below entity type in the resulted EDM document:
If you change the model builder as:
It will generate the below entity type in the resulted EDM document:
There is no Base entity type existed.
Abstract type convention
Rule: The Edm type will be marked as abstract if the class is abstract.
The result is :
Entity key convention
Rule: If one and only one property’s name is ‘Id’ or ‘<entity class name>Id’ (case insensitive), it becomes entity key property.
The result is:
Key attribute
Rule: The [KeyAttribute] specifies key property, it forces a property without ‘id’ to be Key property. It’ll suppress the entity key convention.
The result is:
ComplexType Attribute Convention
Rule-1: Create a class without any ‘id’ or ‘id’ or [`KeyAttribute`] property, like
Rule-2: Add [ComplexType] attribute to a model class: it will remove ‘id’ or ‘id’ or [Key] properties, the model class will have no entity key, thus becomes a complex type.
Then, the above two types wil be built as Complex Type.
DataContract & DataMember
Rule: If using DataContract or DataMember, only property with [DataMember] attribute will be added into Edm model.
The resulted EDM document is:
You can also change name-space and property name in EDM document. For example, if the above DataContract attribute is added with NameSpace:
The result will become:
NotMapped Attribute Convention
Rule: [NotMapped] deselects the property to be serialized or deserialized, so to some extent, it can be seen as the converse of DataContract & DataMember.
For example, the above if Trip class is changed to the below, it generates exactly the same Trip Entity in EDM document, that is, no ‘SharedId’ property.
The result is:
Required Attribute Convention
Rule: The property with [Required] attribute will be non-nullable.
Then the result has Nullable=”false” for Name property:
ConcurrencyCheck Attribute Convention
Rule: It can mark one or more properties for doing optimistic concurrency check on entity updates.
The expected result should be like the below:
Timestamp Attribute Convention
Rule: It’s same as [ConcurrencyCheck].
The expected result should be like the below:
IgnoreDataMember Attribute Convention
Rule: It has the same effect as [NotMapped] attribute. It is able to revert the [DataMember] attribute on the property when the model class doesn’t have [DataContract] attribute.
Rule: Property marked with [NonFilterable] or [NotFilterable] will not support $filter query option.
Then, if you issue an query option as:
~/odata/Customers?$filter=Title eq 'abc'
You will get the following exception:
NotSortable & Unsortable Attribute Convention
Rule: Property marked with [NotSortable] or [Unsortable] will not support $orderby query option.
Then, if you issue an query option as:
`~/odata/Customers?$orderby=Title
You will get the following exception:
NotNavigable Attribute Convention
Rule: Property marked with [NotNavigable] will not support $select query option.
Then, if you issue an query option as:
`~/odata/Customers?$select=address
You will get the following exception:
NotExpandable Attribute Convention
Rule: Property marked with [NotExpandable] will not support $expand query option.
Then, if you issue an query option as:
`~/odata/Customers?$expand=Orders
You will get the following exception:
NotCountable Attribute Convention
Rule: Property marked with [NotCountable] will not support $count query option.
Then, if you issue an query option as:
`~/odata/Customers(1)/Addresses?$count=true
You will get the following exception:
ForeignKey Attribute Convention
Rule: Property marked with [ForeignKey] will be used to build referential constraint.
You will get the following result:
[ForeignKey] can also put on dependent property. for example:
It’ll get the same result.
ActionOnDelete Attribute Convention
Rule: Property marked with [ActionOnDelete] will be used to build referential constraint action on delete.
You will get the following result:
ForeignKeyDiscovery Convention
Rule: A convention used to discover foreign key properties if there is no any foreign key configured on the navigation property.
The basic rule to discover the foreign key is: with the same property type and follow up the naming convention.
The naming convention is:
1. The “Principal class name + principal key name” equals the dependent property name
For example: Customer (Id) <–> Order (CustomerId)
2. or the “Principal key name” equals the dependent property name.
For example: Customer (CustomerId) <–> Order (CustomerId)