As mentioned in previous section, to build Edm model explicitly is to create an IEdmModel
object directly using ODatalib API. The Edm model built by this method is called type-less model , or week type model , or just un-typed model .
Let’s see how to build the Customer-Order business model.
Enum Type
We can use EdmEnumType
to define an Enum type Color
as:
EdmEnumType color = new EdmEnumType ( "WebApiDocNS" , "Color" );
color . AddMember ( new EdmEnumMember ( color , "Red" , new EdmIntegerConstant ( 0 )));
color . AddMember ( new EdmEnumMember ( color , "Blue" , new EdmIntegerConstant ( 1 )));
color . AddMember ( new EdmEnumMember ( color , "Green" , new EdmIntegerConstant ( 2 )));
model . AddElement ( color );
It will generate the below metadata document:
<EnumType Name= "Color" >
<Member Name= "Red" Value= "0" />
<Member Name= "Blue" Value= "1" />
<Member Name= "Green" Value= "2" />
</EnumType>
Complex Type
Basic Complex Type
We can use EdmComplexType
to define a complex type Address
as:
EdmComplexType address = new EdmComplexType ( "WebApiDocNS" , "Address" );
address . AddStructuralProperty ( "Country" , EdmPrimitiveTypeKind . String );
address . AddStructuralProperty ( "City" , EdmPrimitiveTypeKind . String );
model . AddElement ( address );
It will generate the below metadata document:
<ComplexType Name= "Address" >
<Property Name= "Country" Type= "Edm.String" />
<Property Name= "City" Type= "Edm.String" />
</ComplexType>
Derived Complex type
We can set the base type in construct to define a derived complex type SubAddress
as:
EdmComplexType subAddress = new EdmComplexType ( "WebApiDocNS" , "SubAddress" , address );
subAddress . AddStructuralProperty ( "Street" , EdmPrimitiveTypeKind . String );
model . AddElement ( subAddress );
It will generate the below metadata document:
<ComplexType Name= "SubAddress" BaseType= "WebApiDocNS.Address" >
<Property Name= "Street" Type= "Edm.String" />
</ComplexType>
Other Complex Types
We can call the following construct to set a complex type whether it is abstract or open.
public EdmComplexType ( string namespaceName , string name , IEdmComplexType baseType , bool isAbstract , bool isOpen );
For example:
EdmComplexType address = new EdmComplexType ( "WebApiDocNS" , "Address" , baseType : null , isAbstract : true , isOpen : true );
model . AddElement ( address );
It will generate the below metadata document:
<ComplexType Name= "Address" Abstract= "true" OpenType= "true" />
Entity Type
Basic Entity Type
We can use EdmEntityType
to define two entity types Customer
& Order
as:
EdmEntityType customer = new EdmEntityType ( "WebApiDocNS" , "Customer" );
customer . AddKeys ( customer . AddStructuralProperty ( "CustomerId" , EdmPrimitiveTypeKind . Int32 ));
customer . AddStructuralProperty ( "Location" , new EdmComplexTypeReference ( address , isNullable : true ));
model . AddElement ( customer );
EdmEntityType order = new EdmEntityType ( "WebApiDocNS" , "Order" );
order . AddKeys ( order . AddStructuralProperty ( "OrderId" , EdmPrimitiveTypeKind . Int32 ));
order . AddStructuralProperty ( "Token" , EdmPrimitiveTypeKind . Guid );
model . AddElement ( order );
It will generate the below metadata document:
<EntityType Name= "Customer" >
<Key>
<PropertyRef Name= "CustomerId" />
</Key>
<Property Name= "CustomerId" Type= "Edm.Int32" />
<Property Name= "Location" Type= "WebApiDocNS.Address" />
</EntityType>
<EntityType Name= "Order" >
<Key>
<PropertyRef Name= "OrderId" />
</Key>
<Property Name= "OrderId" Type= "Edm.Int32" />
<Property Name= "Token" Type= "Edm.Guid" />
</EntityType>
Derived Entity type
We can set the base type in construct to define a derived entity type VipCustomer
as:
EdmEntityType vipCustomer = new EdmEntityType ( "WebApiDocNS" , "VipCustomer" , customer );
vipCustomer . AddStructuralProperty ( "FavoriteColor" , new EdmEnumTypeReference ( color , isNullable : false ));
model . AddElement ( vipCustomer );
It will generate the below metadata document:
<EntityType Name= "VipCustomer" BaseType= "WebApiDocNS.Customer" >
<Property Name= "FavoriteColor" Type= "WebApiDocNS.Color" Nullable= "false" />
</EntityType>
Other Entity Types
We can call the following construct to set an entity type whether it is abstract or open.
public EdmEntityType ( string namespaceName , string name , IEdmEntityType baseType , bool isAbstract , bool isOpen );
For example:
EdmEntityType customer = new EdmEntityType ( "WebApiDocNS" , "Customer" , baseType : null , isAbstract : true , isOpen : true );
model . AddElement ( customer );
It will generate the below metadata document:
<EntityType Name= "Customer" Abstract= "true" OpenType= "true" />
Default Entity Container
Each model MUST define at most one entity container, in which entity sets, singletons and operation imports are defined. For example:
EdmEntityContainer container = new EdmEntityContainer ( "WebApiDocNS" , "Container" );
EdmEntitySet customers = container . AddEntitySet ( "Customers" , customer );
EdmEntitySet orders = container . AddEntitySet ( "Orders" , order );
model . AddElement ( container );
It will generate the below metadata document:
<EntityContainer Name= "Container" >
<EntitySet Name= "Customers" EntityType= "WebApiDocNS.Customer" />
<EntitySet Name= "Orders" EntityType= "WebApiDocNS.Order" />
</EntityContainer>
Singleton
We can also add singleton into entity container. For example:
EdmSingleton mary = container . AddSingleton ( "Mary" , customer );
It will generate the below metadata document:
<Singleton Name= "Mary" Type= "WebApiDocNS.Customer" />
Navigation Property
Now, we can add navigation property to Customer . For example:
EdmNavigationProperty ordersNavProp = customer . AddUnidirectionalNavigation (
new EdmNavigationPropertyInfo
{
Name = "Orders" ,
TargetMultiplicity = EdmMultiplicity . Many ,
Target = order
});
customers . AddNavigationTarget ( ordersNavProp , orders );
It will generate the below metadata document:
First, it will add a new item in the entity type as:
<EntityType Name= "Customer" >
...
<NavigationProperty Name= "Orders" Type= "Collection(WebApiDocNS.Order)" />
</EntityType>
Second, it will add a new item in the entity container for Customers entity set as:
<EntitySet Name= "Customers" EntityType= "WebApiDocNS.Customer" >
<NavigationPropertyBinding Path= "Orders" Target= "Orders" />
</EntitySet>
Function
Let’s define two functions. One is bound, the other is unbound as:
IEdmTypeReference stringType = EdmCoreModel . Instance . GetPrimitive ( EdmPrimitiveTypeKind . String , isNullable : false );
IEdmTypeReference intType = EdmCoreModel . Instance . GetPrimitive ( EdmPrimitiveTypeKind . Int32 , isNullable : false );
// Bound
EdmFunction getFirstName = new EdmFunction ( "WebApiDocNS" , "GetFirstName" , stringType , isBound : true , entitySetPathExpression : null , isComposable : false );
getFirstName . AddParameter ( "entity" , new EdmEntityTypeReference ( customer , false ));
model . AddElement ( getFirstName );
// Unbound
EdmFunction getNumber = new EdmFunction ( "WebApiDocNS" , "GetOrderCount" , intType , isBound : false , entitySetPathExpression : null , isComposable : false );
model . AddElement ( getNumber );
It will generate the below metadata document:
<Function Name= "GetFirstName" IsBound= "true" >
<Parameter Name= "entity" Type= "WebApiDocNS.Customer" Nullable= "false" />
<ReturnType Type= "Edm.String" Nullable= "false" />
</Function>
<Function Name= "GetOrderCount" >
<ReturnType Type= "Edm.Int32" Nullable= "false" />
</Function>
Action
Let’s define two actions. One is bound, the other is unbound as:
// Bound
EdmAction calculate = new EdmAction ( "WebApiDocNS" , "CalculateOrderPrice" , returnType : null , isBound : true , entitySetPathExpression : null );
calculate . AddParameter ( "entity" , new EdmEntityTypeReference ( customer , false ));
model . AddElement ( calculate );
// Unbound
EdmAction change = new EdmAction ( "WebApiDocNS" , "ChangeCustomerById" , returnType : null , isBound : false , entitySetPathExpression : null );
change . AddParameter ( "Id" , intType );
model . AddElement ( change );
It will generate the below metadata document:
<Action Name= "CalculateOrderPrice" IsBound= "true" >
<Parameter Name= "entity" Type= "WebApiDocNS.Customer" Nullable= "false" />
</Action>
<Action Name= "ChangeCustomerById" >
<Parameter Name= "Id" Type= "Edm.Int32" Nullable= "false" />
</Action>
Function Import
Unbound function can be called through function import. The following codes are used to build a function import:
container . AddFunctionImport ( "GetOrderCount" , getNumber );
It will generate the below metadata document:
<FunctionImport Name= "GetOrderCount" Function= "WebApiDocNS.GetOrderCount" />
Action Import
Unbound actioin can be called through action import. The following codes are used to build an action import:
container . AddActionImport ( "ChangeCustomerById" , change );
It will generate the below metadata document:
<ActionImport Name= "ChangeCustomerById" Action= "WebApiDocNS.ChangeCustomerById" />
Summary
Let’s put all codes together:
public static IEdmModel GetEdmModel ()
{
EdmModel model = new EdmModel ();
// Complex Type
EdmComplexType address = new EdmComplexType ( "WebApiDocNS" , "Address" );
address . AddStructuralProperty ( "Country" , EdmPrimitiveTypeKind . String );
address . AddStructuralProperty ( "City" , EdmPrimitiveTypeKind . String );
model . AddElement ( address );
EdmComplexType subAddress = new EdmComplexType ( "WebApiDocNS" , "SubAddress" , address );
subAddress . AddStructuralProperty ( "Street" , EdmPrimitiveTypeKind . String );
model . AddElement ( subAddress );
// Enum type
EdmEnumType color = new EdmEnumType ( "WebApiDocNS" , "Color" );
color . AddMember ( new EdmEnumMember ( color , "Red" , new EdmIntegerConstant ( 0 )));
color . AddMember ( new EdmEnumMember ( color , "Blue" , new EdmIntegerConstant ( 1 )));
color . AddMember ( new EdmEnumMember ( color , "Green" , new EdmIntegerConstant ( 2 )));
model . AddElement ( color );
// Entity type
EdmEntityType customer = new EdmEntityType ( "WebApiDocNS" , "Customer" );
customer . AddKeys ( customer . AddStructuralProperty ( "CustomerId" , EdmPrimitiveTypeKind . Int32 ));
customer . AddStructuralProperty ( "Location" , new EdmComplexTypeReference ( address , isNullable : true ));
model . AddElement ( customer );
EdmEntityType vipCustomer = new EdmEntityType ( "WebApiDocNS" , "VipCustomer" , customer );
vipCustomer . AddStructuralProperty ( "FavoriteColor" , new EdmEnumTypeReference ( color , isNullable : false ));
model . AddElement ( vipCustomer );
EdmEntityType order = new EdmEntityType ( "WebApiDocNS" , "Order" );
order . AddKeys ( order . AddStructuralProperty ( "OrderId" , EdmPrimitiveTypeKind . Int32 ));
order . AddStructuralProperty ( "Token" , EdmPrimitiveTypeKind . Guid );
model . AddElement ( order );
EdmEntityContainer container = new EdmEntityContainer ( "WebApiDocNS" , "Container" );
EdmEntitySet customers = container . AddEntitySet ( "Customers" , customer );
EdmEntitySet orders = container . AddEntitySet ( "Orders" , order );
model . AddElement ( container );
// EdmSingleton mary = container.AddSingleton("Mary", customer);
// navigation properties
EdmNavigationProperty ordersNavProp = customer . AddUnidirectionalNavigation (
new EdmNavigationPropertyInfo
{
Name = "Orders" ,
TargetMultiplicity = EdmMultiplicity . Many ,
Target = order
});
customers . AddNavigationTarget ( ordersNavProp , orders );
// function
IEdmTypeReference stringType = EdmCoreModel . Instance . GetPrimitive ( EdmPrimitiveTypeKind . String , isNullable : false );
IEdmTypeReference intType = EdmCoreModel . Instance . GetPrimitive ( EdmPrimitiveTypeKind . Int32 , isNullable : false );
EdmFunction getFirstName = new EdmFunction ( "WebApiDocNS" , "GetFirstName" , stringType , isBound : true , entitySetPathExpression : null , isComposable : false );
getFirstName . AddParameter ( "entity" , new EdmEntityTypeReference ( customer , false ));
model . AddElement ( getFirstName );
EdmFunction getNumber = new EdmFunction ( "WebApiDocNS" , "GetOrderCount" , intType , isBound : false , entitySetPathExpression : null , isComposable : false );
model . AddElement ( getNumber );
container . AddFunctionImport ( "GetOrderCount" , getNumber );
// action
EdmAction calculate = new EdmAction ( "WebApiDocNS" , "CalculateOrderPrice" , returnType : null , isBound : true , entitySetPathExpression : null );
calculate . AddParameter ( "entity" , new EdmEntityTypeReference ( customer , false ));
model . AddElement ( calculate );
EdmAction change = new EdmAction ( "WebApiDocNS" , "ChangeCustomerById" , returnType : null , isBound : false , entitySetPathExpression : null );
change . AddParameter ( "Id" , intType );
model . AddElement ( change );
container . AddActionImport ( "ChangeCustomerById" , change );
return model ;
}
And the final XML will be:
<?xml version="1.0" encoding="utf-8"?>
<edmx:Edmx Version= "4.0" xmlns:edmx= "http://docs.oasis-open.org/odata/ns/edmx" >
<edmx:DataServices>
<Schema Namespace= "WebApiDocNS" xmlns= "http://docs.oasis-open.org/odata/ns/edm" >
<ComplexType Name= "Address" >
<Property Name= "Country" Type= "Edm.String" />
<Property Name= "City" Type= "Edm.String" />
</ComplexType>
<ComplexType Name= "SubAddress" BaseType= "WebApiDocNS.Address" >
<Property Name= "Street" Type= "Edm.String" />
</ComplexType>
<EnumType Name= "Color" >
<Member Name= "Red" Value= "0" />
<Member Name= "Blue" Value= "1" />
<Member Name= "Green" Value= "2" />
</EnumType>
<EntityType Name= "Customer" >
<Key>
<PropertyRef Name= "CustomerId" />
</Key>
<Property Name= "CustomerId" Type= "Edm.Int32" />
<Property Name= "Location" Type= "WebApiDocNS.Address" />
<NavigationProperty Name= "Orders" Type= "Collection(WebApiDocNS.Order)" />
</EntityType>
<EntityType Name= "VipCustomer" BaseType= "WebApiDocNS.Customer" >
<Property Name= "FavoriteColor" Type= "WebApiDocNS.Color" Nullable= "false" />
</EntityType>
<EntityType Name= "Order" >
<Key>
<PropertyRef Name= "OrderId" />
</Key>
<Property Name= "OrderId" Type= "Edm.Int32" />
<Property Name= "Token" Type= "Edm.Guid" />
</EntityType>
<Function Name= "GetFirstName" IsBound= "true" >
<Parameter Name= "entity" Type= "WebApiDocNS.Customer" Nullable= "false" />
<ReturnType Type= "Edm.String" Nullable= "false" />
</Function>
<Function Name= "GetOrderCount" >
<ReturnType Type= "Edm.Int32" Nullable= "false" />
</Function>
<Action Name= "CalculateOrderPrice" IsBound= "true" >
<Parameter Name= "entity" Type= "WebApiDocNS.Customer" Nullable= "false" />
</Action>
<Action Name= "ChangeCustomerById" >
<Parameter Name= "Id" Type= "Edm.Int32" Nullable= "false" />
</Action>
<EntityContainer Name= "Container" >
<EntitySet Name= "Customers" EntityType= "WebApiDocNS.Customer" >
<NavigationPropertyBinding Path= "Orders" Target= "Orders" />
</EntitySet>
<EntitySet Name= "Orders" EntityType= "WebApiDocNS.Order" />
<FunctionImport Name= "GetOrderCount" Function= "WebApiDocNS.GetOrderCount" />
<ActionImport Name= "ChangeCustomerById" Action= "WebApiDocNS.ChangeCustomerById" />
</EntityContainer>
</Schema>
</edmx:DataServices>
</edmx:Edmx>