Overview
Now we will describe the new features of Web API OData that was announced by Microsoft in Visual Studio Connect() event on November 12, 2013 from New York, USA.
The following are the new features of ASP.NET Web API OData 5.3 and 5.3.1 beta:
- Support $levels in $expand
- Uses OData Core Libraries 6.5.0 and 6.8.1(latest stable)
- Support open entity type
- Support dynamic collection property in open type
- Support for $count in the ODataPathHandler
- Support complex type Inheritance
- Feature updates
- Bug fixes
Support $levels in $expand
With Visual Studio 2015 Preview we can use the $levels query option in $expand queries. Where $expand directs that related records should be retrieved in the record or collection being retrieved. If we want to get related records, locate the name of the entity relationship that defines this relationship. We may need to view the entity relationship information in the application to correctly identify the relationship or the conceptual schema definition language for the Organization Data Service.
Example:
http://www.c-sharpcorner.com/Employees?$expand=Manager($levels=2)
The older version of above $levels query is equivalent to the following code:
http://www.c-sharpcorner.com/Employees?$expand=Manager($expand=Manager))
Support Open Entity Type
An open entity type is a structured type that contains dynamic properties, in addition to any properties that are declared in the type definition. Open types let you add flexibility to your data models.
Support Dynamic Collection Property in Open Type
This release version of the WebAPI supports a dynamic collection property that might be the extension of the previous property. Previously, a dynamic property needs to be a single value. In Web API OData 5.3, dynamic properties can have collection values. For example, in the following JSON payload, the Emails property is a dynamic property and is a collection of string types.
Uses OData Core Libraries 6.5.0 and 6.8.1
This new release of Web API OData includes use of core libraries that adds some functionalities in this regard when developing the project. Here are some features that core libraries provide and their own functionality as well.
- ODataLib now has support of TypeDefinition
- ODataLib now has support to serialize and deserialize unsigned integers
- ODataLib now has support to $count on collection of Primitive or complex type
- Support Capabilities.ChangeTracking annotation
- OData Client for .Net now has support to change the http method in the BuildingRequest event handler
- OData Client for .Net and ODataLib now supports a Windows Phone 8.1 project
- Rename client portable DLL from “Microsoft.Data.Services.Client.Portable” to “Microsoft.Data.Services.Client”
- OData Client for .NET now supports Edm.TimeOfDay/Edm.Date
- OData Client for .NET now can takes an entity or collection of entity as a parameter of the action.
Support for $count in the ODataPathHandler
This support now has references to allow and to support as well the use of primitive types of a collection and also follows complex types.
- $count follows a collection of primitive type
- $count follows a collection of Complex type and is followed by the query options $filter
- $count follows a collection of Enumeration type property
Bug Fixes
This new release of Web API OData fixes many bugs that arose in previous versions. For example EnumProperty/$value/something should report error that $value must be the last segment, Updating 'Microsoft.OData.Core 6.4.0' to 'Microsoft.OData.Core 6.5.0' failed. Another one is AllowedFunctions Ignored on Queryable Call so these are some bugs fixed that enhanced the functionality of the code.
Support complex type inheritance
This new release of Web API OData supports complex type inheritance that was not possible in previous versions. Let's see the following pictures that shows the complex type inheritance.

Let's do implement it with a example project.
Step 1
Create a Console Application in Visual Studio 2015 and select Visual C# as the project language. For more see the following picture:


Step 2
After creating the new application, write the following five static methods inside the Program (Default Class). The code of these methods are as follows:
- public static void Configuration(IAppBuilder builder)
- {
- HttpConfiguration config = new HttpConfiguration();
- IEdmModel edmModel = ODataModels.GetModel();
- config.MapODataServiceRoute(routeName: "OData", routePrefix: "odata", model: edmModel);
- builder.UseWebApi(config);
- }
- private static HttpResponseMessage Get(string requestUri)
- {
- HttpResponseMessage response = _httpClient.GetAsync(requestUri).Result;
- response.EnsureSuccessStatusCode();
- return response;
- }
- private static HttpResponseMessage Post(string requestUri)
- {
- string content = @"
- {
- 'Id':0,
- 'Name':'Name4',
- 'CurrentShape':
- {
- '@odata.type':'#ODataComplexTypeInheritanceSample.Circle',
- 'Radius':10,
- 'Center':{'X':1,'Y':2},
- 'HasBorder':true
- },
- 'OptionalShapes':
- [
- {
- '@odata.type':'#ODataComplexTypeInheritanceSample.Polygon',
- 'HasBorder':true,
- 'Vertexes':
- [
- {
- 'X':0,'Y':0
- },
- {
- 'X':2,'Y':0
- },
- {
- 'X':2,'Y':2
- }
- ]
- }
- ]
- }";
- HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, requestUri);
- request.Content = new StringContent(content);
- request.Content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json");
- HttpResponseMessage response = _httpClient.SendAsync(request).Result;
- response.EnsureSuccessStatusCode();
- return response;
- }
- private static HttpResponseMessage ActionCall(string requestUri)
- {
- string content = @" 'shape':
- {
- '@odata.type':'#ODataComplexTypeInheritanceSample.Polygon',
- 'HasBorder':true,
- 'Vertexes':
- [
- {
- 'X':0,'Y':0
- },
- {
- 'X':2,'Y':0
- },
- {
- 'X':2,'Y':2
- },
- {
- 'X':0,'Y':2
- }
- ]
- }
- HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, requestUri);
- request.Content = new StringContent(content);
- request.Content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json");
- HttpResponseMessage response = _httpClient.SendAsync(request).Result;
- response.EnsureSuccessStatusCode();
- return response;
- }
- private static void Comment(string message)
- {
- Console.WriteLine(message);
- }
- private static void Comment(HttpResponseMessage response, bool metadataResponse = false)
- {
- Console.WriteLine(response);
- if (metadataResponse)
- {
- string content = response.Content.ReadAsStringAsync().Result;
- Console.WriteLine(content);
- }
- else
- {
- JObject result = response.Content.ReadAsAsync<JObject>().Result;
- Console.WriteLine(result);
- }
- }
- public static void Main(string[] args)
- {
- _httpClient.DefaultRequestHeaders.Accept.Add(MediaTypeWithQualityHeaderValue.Parse("application/json"));
- using (WebApp.Start(_baseAddress, Configuration))
- {
- Console.WriteLine("Listening on " + _baseAddress);
- string requestUri = "";
- HttpResponseMessage response = null;
- // The complex type Shape is an abstract type, in the EDM model, its IsAbstract is true.
- // The complex type Circle and Polygon derive from Shape and
- // the complex type Rectangle derives from Polygon.
- requestUri = _baseAddress + "/odata/$metadata";
- Comment("GET " + requestUri);
- response = Get(requestUri);
- Comment(response, true);
- // The property DefaultShape in Windows is declared as Shape, and in the instance Windows(1)
- // it is actually a Ploygon.
- // The property OptionalShapes is a collection of Shape, in the instance the 3 types of Shape
- // are included.
- requestUri = _baseAddress + "/odata/Windows(1)";
- Comment("GET " + requestUri);
- response = Get(requestUri);
- Comment(response);
- // Get a property defined in the derived Complex type.
- requestUri = _baseAddress + "/odata/Windows(1)/CurrentShape/ODataComplexTypeInheritanceSample.Circle/Radius";
- Comment("GET " + requestUri);
- response = Get(requestUri);
- Comment(response);
- // Function that returns a base complex type
- requestUri = _baseAddress + "/odata/Windows(1)/ODataComplexTypeInheritanceSample.GetTheLastOptionalShape()";
- Comment("GET " + requestUri);
- response = Get(requestUri);
- Comment(response);
- // Action that takes in a base complex type
- requestUri = _baseAddress + "/odata/Windows(1)/ODataComplexTypeInheritanceSample.AddOptionalShape";
- Comment("POST " + requestUri);
- response = ActionCall(requestUri);
- Comment(response);
- // This method illustrate how to add an entity which contains derived complex type.
- requestUri = _baseAddress + "/odata/Windows";
- Comment("POST " + requestUri);
- response = Post(requestUri);
- Comment(response);
- Console.WriteLine("\nPress any key to continue...");
- Console.ReadKey();
- }
- }
- public class WindowsController : ODataController
- {
- private IList<Window> _windows = new List<Window>();
- public WindowsController()
- {
- Polygon triagle = new Polygon() { HasBorder = true, Vertexes = new List<Point>() };
- triagle.Vertexes.Add(new Point() { X = 1, Y = 2 });
- triagle.Vertexes.Add(new Point() { X = 2, Y = 3 });
- triagle.Vertexes.Add(new Point() { X = 4, Y = 8 });
- Polygon rectangle = new Polygon() { HasBorder = true, Vertexes = new List<Point>() };
- rectangle.Vertexes.Add(new Point() { X = 0, Y = 0 });
- rectangle.Vertexes.Add(new Point() { X = 2, Y = 0 });
- rectangle.Vertexes.Add(new Point() { X = 2, Y = 2 });
- rectangle.Vertexes.Add(new Point() { X = 0, Y = 2 });
- Circle circle = new Circle() { HasBorder = true, Center = new Point(), Radius = 2 };
- Window dashboardWindow = new Window
- {
- Id = 1,
- Name = "CircleWindow",
- CurrentShape = circle,
- OptionalShapes = new List<Shape>(),
- };
- dashboardWindow.OptionalShapes.Add(rectangle);
- _windows.Add(dashboardWindow);
- Window popupWindow = new Window
- {
- Id = 2,
- Name = "Popup",
- CurrentShape = rectangle,
- OptionalShapes = new List<Shape>(),
- Parent = dashboardWindow,
- };
- popupWindow.OptionalShapes.Add(triagle);
- popupWindow.OptionalShapes.Add(circle);
- _windows.Add(popupWindow);
- Window anotherPopupWindow = new Window
- {
- Id = 3,
- Name = "AnotherPopup",
- CurrentShape = rectangle,
- OptionalShapes = new List<Shape>(),
- Parent = popupWindow,
- };
- anotherPopupWindow.OptionalShapes.Add(triagle);
- anotherPopupWindow.OptionalShapes.Add(circle);
- _windows.Add(anotherPopupWindow);
- }
- [EnableQuery]
- public IHttpActionResult Get()
- {
- return Ok(_windows);
- }
- [EnableQuery]
- public SingleResult<Window> GetWindow([FromODataUri] int key)
- {
- return SingleResult.Create<Window>(_windows.Where(w => w.Id == key).AsQueryable());
- }
- public IHttpActionResult Post(Window window)
- {
- _windows.Add(window);
- window.Id = _windows.Count + 1;
- return Created(window);
- }
- [ODataRoute("Windows({key})/CurrentShape/ODataComplexTypeInheritanceSample.Circle/Radius")]
- public IHttpActionResult GetRadius(int key)
- {
- Window window = _windows.FirstOrDefault(e => e.Id == key);
- if (window == null)
- {
- return NotFound();
- }
- return Ok(((Circle)window.CurrentShape).Radius);
- }
- [ODataRoute("Windows({key})/ODataComplexTypeInheritanceSample.GetTheLastOptionalShape()")]
- public IHttpActionResult GetTheLastOptionalShape(int key)
- {
- Window window = _windows.FirstOrDefault(e => e.Id == key);
- if (window == null)
- {
- return NotFound();
- }
- int count = window.OptionalShapes.Count;
- Shape last = window.OptionalShapes.ElementAt(count - 1);
- return Ok(last);
- }
- public IHttpActionResult AddOptionalShape(int key, ODataActionParameters parameters)
- {
- Shape newShape = parameters["shape"] as Shape;
- Window window = _windows.FirstOrDefault(e => e.Id == key);
- if (window == null)
- {
- return NotFound();
- }
- window.OptionalShapes.Add(newShape);
- int count = window.OptionalShapes.Count;
- return Ok(count);
- }
- }
Again add a class (ODataModels) into the current project with the following code:
- public class ODataModels
- {
- public static IEdmModel GetModel()
- {
- ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
- EntitySetConfiguration<Window> windows = builder.EntitySet<Window>("Windows");
- EntityTypeConfiguration<Window> window = windows.EntityType;
- // Action that takes in a base complex type.
- ActionConfiguration actionConfiguration = window.Action("AddOptionalShape");
- actionConfiguration.Parameter<Shape>("shape");
- actionConfiguration.Returns<int>(); // The number of all optional shapes
- // Function that returns a base complex type
- var functionConfiguration = window.Function("GetTheLastOptionalShape");
- functionConfiguration.Returns<Shape>();
- builder.Namespace = typeof(Window).Namespace;
- return builder.GetEdmModel();
- }
- }
Step 4
Now add a new folder to the current project. To do so use the following procedure.
- Circle.cs
- Point.cs
- Polygon.cs
- Shape.cs
- Window.cs
- public class Circle : Shape
- {
- public Point Center { get; set; }
- public int Radius { get; set; }
- public override string ToString()
- {
- // {centerX, centerY, radius}
- return "{" + Center.X + "," + Center.Y + "," + Radius + "}";
- }
- }
- public class Point
- {
- public int X { get; set; }
- public int Y { get; set; }
- }
- public class Polygon : Shape
- {
- public IList<Point> Vertexes { get; set; }
- public Polygon()
- {
- Vertexes = new List<Point>();
- }
- }
- public abstract class Shape
- {
- public bool HasBorder { get; set; }
- }
- public class Window
- {
- public Window()
- {
- OptionalShapes = new List<Shape>();
- }
- public int Id { get; set; }
- public string Name { get; set; }
- public Window Parent { get; set; }
- public Shape CurrentShape { get; set; }
- public IList<Shape> OptionalShapes { get; set; }
- }
This application must have the following DLL files.

- Microsoft.AspNet.OData -Version 5.3.0
- Microsoft.AspNet.WebApi.OData -Version 5.3.0


This article is about the tooling update of ASP.NET. Although all the things and attributes are the same for both versions there are also some bugs fixed in the beta version of 5.3.1 OData.

Praveen KumarPosted Dec 3, 2014, 12:38 AM
Very Nice! You defined about Complex Inheritance is awesome!