Complex Type Inheritance in OData v4 with ASP.NET Web API
by Microsoft
According to the OData v4 specification, a complex type can inherit from another complex type. (A complex type is a structured type without a key.) Web API OData 5.3 supports complex type inheritance.
This topic shows how to build an entity data model (EDM) with complex inheritance types. For the complete source code, see OData Complex Type Inheritance Sample.
Software versions used in the tutorial
- Web API OData 5.3
- OData v4
Model Hierarchy
To illustrate complex type inheritance, we’ll use the following class hierarchy.
Shape
is an abstract complex type. Rectangle
, Triangle
, and Circle
are complex types derived from Shape
, and RoundRectangle
derives from Rectangle
. Window
is an entity type and contains a Shape
instance.
Here are the CLR classes that define these types.
[!code-csharpMain]
1: public class Window
2: {
3: public int Id { get; set; }
4: public string Title { get; set; }
5: public Shape Shape { get; set; }
6: }
7:
8: public abstract class Shape
9: {
10: public bool HasBorder { get; set; }
11: public Color Color { get; set; }
12: }
13:
14: public class Rectangle : Shape
15: {
16: public Point P1 { get; set; }
17: public Point P2 { get; set; }
18: public Point P3 { get; set; }
19: }
20:
21: public class RoundRectangle : Rectangle
22: {
23: public double Round { get; set; }
24: }
25:
26: public class Triangle : Shape
27: {
28: public Point LeftTop { get; set; }
29: public int Height { get; set; }
30: public int Weight { get; set; }
31: }
32:
33: public class Circle : Shape
34: {
35: public Point Center { get; set; }
36: public int Radius { get; set; }
37: }
38:
39: public class Point
40: {
41: public int X { get; set; }
42: public int Y { get; set; }
43: }
44:
45: public enum Color
46: {
47: Red,
48: Blue,
49: Green,
50: Yellow
51: }
Build the EDM Model
To create the EDM, you can use ODataConventionModelBuilder, which infers the inheritance relationships from the CLR types.
[!code-csharpMain]
1: private IEdmModel GetEdmModel()
2: {
3: ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
4: builder.EntitySet<Window>("Windows");
5: return builder.GetEdmModel();
6: }
You can also build the EDM explicitly, using ODataModelBuilder. This takes more code, but gives you more control over the EDM.
[!code-csharpMain]
1: private IEdmModel GetExplicitEdmModel()
2: {
3: ODataModelBuilder builder = new ODataModelBuilder();
4:
5: EnumTypeConfiguration<Color> color = builder.EnumType<Color>();
6: color.Member(Color.Red);
7: color.Member(Color.Blue);
8: color.Member(Color.Green);
9: color.Member(Color.Yellow);
10:
11: ComplexTypeConfiguration<Point> point = builder.ComplexType<Point>();
12: point.Property(c => c.X);
13: point.Property(c => c.Y);
14:
15: ComplexTypeConfiguration<Shape> shape = builder.ComplexType<Shape>();
16: shape.EnumProperty(c => c.Color);
17: shape.Property(c => c.HasBorder);
18: shape.Abstract();
19:
20: ComplexTypeConfiguration<Triangle> triangle = builder.ComplexType<Triangle>();
21: triangle.ComplexProperty(c => c.P1);
22: triangle.ComplexProperty(c => c.P2);
23: triangle.ComplexProperty(c => c.P2);
24: triangle.DerivesFrom<Shape>();
25:
26: ComplexTypeConfiguration<Rectangle> rectangle = builder.ComplexType<Rectangle>();
27: rectangle.ComplexProperty(c => c.LeftTop);
28: rectangle.Property(c => c.Height);
29: rectangle.Property(c => c.Weight);
30: rectangle.DerivesFrom<Shape>();
31:
32: ComplexTypeConfiguration<RoundRectangle> roundRectangle = builder.ComplexType<RoundRectangle>();
33: roundRectangle.Property(c => c.Round);
34: roundRectangle.DerivesFrom<Rectangle>();
35:
36: ComplexTypeConfiguration<Circle> circle = builder.ComplexType<Circle>();
37: circle.ComplexProperty(c => c.Center);
38: circle.Property(c => c.Radius);
39: circle.DerivesFrom<Shape>();
40:
41: EntityTypeConfiguration<Window> window = builder.EntityType<Window>();
42: window.HasKey(c => c.Id);
43: window.Property(c => c.Title);
44: window.ComplexProperty(c => c.Shape);
45:
46: builder.EntitySet<Window>("Windows");
47: return builder.GetEdmModel();
48: }
These two examples create the same EDM schema.
Metadata Document
Here is the OData metadata document, showing complex type inheritance.
[!code-xmlMain]
1: <?xml version="1.0" encoding="utf-8"?>
2: <edmx:Edmx Version="4.0" xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx">
3: <edmx:DataServices>
4: <Schema Namespace="NS" xmlns="http://docs.oasis-open.org/odata/ns/edm">
5: <EntityType Name="Window">
6: <Key>
7: <PropertyRef Name="Id" />
8: </Key>
9: <Property Name="Id" Type="Edm.Int32" Nullable="false" />
10: <Property Name="Title" Type="Edm.String" />
11: <Property Name="Shape" Type="BookStore.Shape" />
12: </EntityType>
13: <ComplexType Name="Shape" Abstract="true">
14: <Property Name="HasBorder" Type="Edm.Boolean" Nullable="false" />
15: <Property Name="Color" Type="BookStore.Color" Nullable="false" />
16: </ComplexType>
17: <ComplexType Name="Circle" BaseType="BookStore.Shape">
18: <Property Name="Center" Type="BookStore.Point" />
19: <Property Name="Radius" Type="Edm.Int32" Nullable="false" />
20: </ComplexType>
21: <ComplexType Name="Point">
22: <Property Name="X" Type="Edm.Int32" Nullable="false" />
23: <Property Name="Y" Type="Edm.Int32" Nullable="false" />
24: </ComplexType>
25: <ComplexType Name="Rectangle" BaseType="BookStore.Shape">
26: <Property Name="LeftTop" Type="BookStore.Point" />
27: <Property Name="Height" Type="Edm.Int32" Nullable="false" />
28: <Property Name="Weight" Type="Edm.Int32" Nullable="false" />
29: </ComplexType>
30: <ComplexType Name="RoundRectangle" BaseType="BookStore.Rectangle">
31: <Property Name="Round" Type="Edm.Double" Nullable="false" />
32: </ComplexType>
33: <ComplexType Name="Triangle" BaseType="BookStore.Shape">
34: <Property Name="P1" Type="BookStore.Point" />
35: <Property Name="P2" Type="BookStore.Point" />
36: <Property Name="P3" Type="BookStore.Point" />
37: </ComplexType>
38: <EnumType Name="Color">
39: <Member Name="Red" Value="0" />
40: <Member Name="Blue" Value="1" />
41: <Member Name="Green" Value="2" />
42: <Member Name="Yellow" Value="3" />
43: </EnumType>
44: </Schema>
45: <Schema Namespace="Default" xmlns="http://docs.oasis-open.org/odata/ns/edm">
46: <EntityContainer Name="Container">
47: <EntitySet Name="Windows" EntityType="BookStore.Window" />
48: </EntityContainer>
49: </Schema>
50: </edmx:DataServices>
51: </edmx:Edmx>
From the metadata document, you can see that:
- The
Shape
complex type is abstract. - The
Rectangle
,Triangle
, andCircle
complex type have the base typeShape
. - The
RoundRectangle
type has the base typeRectangle
.
Casting Complex Types
Casting on complex types is now supported. For example, the following query casts a Shape
to a Rectangle
.
[!code-consoleMain]
1: GET ~/odata/Windows(1)/Shape/NS.Rectangle/LeftTop
Here’s the response payload:
[!code-consoleMain]
1: {
2: "@odata.context":"http://localhost/odata/$metadata#Windows(1)/Shape/NS.Rectangle/LeftTop",
3: "X":100,"Y":100
4: }
|