What is New in ASP.NET Web API OData 5.3 and 5.3.1 Beta

Overview

The ASP.NET Web API is a framework developed by Microsoft that makes it easy to build HTTP services that reach a broad range of clients, including browsers and mobile devices. With WebAPI content negotiation, we can return data based on the client requests. It means that if the client is requesting the data to be returned as JSON or XML, the WebAPI framework deals with the request type and returns the data appropriately based on the media type. By default the WebAPI provides JSON and XML based responses.

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.

File uploaded successfully!ComplexInheritance
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:

NewProject

ConsoleApps

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: 

  1. public static void Configuration(IAppBuilder builder)  
  2. {  
  3.    HttpConfiguration config = new HttpConfiguration();  
  4.    IEdmModel edmModel = ODataModels.GetModel();  
  5.    config.MapODataServiceRoute(routeName: "OData", routePrefix: "odata", model: edmModel);  
  6.    builder.UseWebApi(config);  
  7. }   
  1. private static HttpResponseMessage Get(string requestUri)  
  2. {  
  3.      HttpResponseMessage response = _httpClient.GetAsync(requestUri).Result;  
  4.      response.EnsureSuccessStatusCode();  
  5.      return response;  
  6. }  
  1.   private static HttpResponseMessage Post(string requestUri)  
  2.   {  
  3.       string content = @"
  4.       {  
  5.           'Id':0,  
  6.           'Name':'Name4',  
  7.           'CurrentShape':  
  8.           {  
  9.               '@odata.type':'#ODataComplexTypeInheritanceSample.Circle',    
  10.               'Radius':10,  
  11.               'Center':{'X':1,'Y':2},  
  12.               'HasBorder':true  
  13.           },  
  14.          'OptionalShapes':  
  15.           [  
  16.              {  
  17.                 '@odata.type':'#ODataComplexTypeInheritanceSample.Polygon',  
  18.                 'HasBorder':true,  
  19.                 'Vertexes':  
  20.                 [  
  21.                    {  
  22.                       'X':0,'Y':0  
  23.                    },  
  24.                    {  
  25.                       'X':2,'Y':0  
  26.                    },  
  27.                    {  
  28.                       'X':2,'Y':2  
  29.                    }  
  30.                 ]  
  31.             }  
  32.          ]  
  33.      }";  
  34.      HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, requestUri);  
  35.      request.Content = new StringContent(content);  
  36.      request.Content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json");  
  37.   
  38.      HttpResponseMessage response = _httpClient.SendAsync(request).Result;  
  39.      response.EnsureSuccessStatusCode();  
  40.      return response;  
  41. }  
  1.     private static HttpResponseMessage ActionCall(string requestUri)  
  2.     {  
  3.         string content = @" 'shape':  
  4.        {  
  5.           '@odata.type':'#ODataComplexTypeInheritanceSample.Polygon',  
  6.           'HasBorder':true,  
  7.           'Vertexes':
  8.           [  
  9.              {  
  10.                 'X':0,'Y':0  
  11.              },  
  12.              {  
  13.                 'X':2,'Y':0  
  14.              },  
  15.              {  
  16.                 'X':2,'Y':2  
  17.              },  
  18.              {  
  19.                 'X':0,'Y':2  
  20.               }  
  21.           ]  
  22.        }  
  23.   
  24.        HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, requestUri);  
  25.        request.Content = new StringContent(content);  
  26.        request.Content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json");  
  27.   
  28.        HttpResponseMessage response = _httpClient.SendAsync(request).Result;  
  29.        response.EnsureSuccessStatusCode();  
  30.        return response;  
  31.    }  
  1. private static void Comment(string message)  
  2. {  
  3.     Console.WriteLine(message);  
  4. }  
  5.   
  6. private static void Comment(HttpResponseMessage response, bool metadataResponse = false)  
  7. {  
  8.    Console.WriteLine(response);  
  9.    if (metadataResponse)  
  10.    {  
  11.         string content = response.Content.ReadAsStringAsync().Result;  
  12.         Console.WriteLine(content);  
  13.    }  
  14.    else  
  15.    {  
  16.         JObject result = response.Content.ReadAsAsync<JObject>().Result;  
  17.         Console.WriteLine(result);  
  18.    }  
  19. }  
Now enter the following Main() method into the Program class.
  1. public static void Main(string[] args)  
  2. {  
  3.     _httpClient.DefaultRequestHeaders.Accept.Add(MediaTypeWithQualityHeaderValue.Parse("application/json"));  
  4.     using (WebApp.Start(_baseAddress, Configuration))  
  5.     {  
  6.         Console.WriteLine("Listening on " + _baseAddress);  
  7.         string requestUri = "";  
  8.         HttpResponseMessage response = null;  
  9.   
  10.        // The complex type Shape is an abstract type, in the EDM model, its IsAbstract is true.  
  11.        // The complex type Circle and Polygon derive from Shape and  
  12.        // the complex type Rectangle derives from Polygon.  
  13.        requestUri = _baseAddress + "/odata/$metadata";  
  14.        Comment("GET " + requestUri);  
  15.        response = Get(requestUri);  
  16.        Comment(response, true);  
  17.   
  18.        // The property DefaultShape in Windows is declared as Shape, and in the instance Windows(1)  
  19.        // it is actually a Ploygon.  
  20.        // The property OptionalShapes is a collection of Shape, in the instance the 3 types of Shape   
  21.        // are included.  
  22.        requestUri = _baseAddress + "/odata/Windows(1)";  
  23.        Comment("GET " + requestUri);  
  24.        response = Get(requestUri);  
  25.        Comment(response);  
  26.   
  27.        // Get a property defined in the derived Complex type.  
  28.        requestUri = _baseAddress + "/odata/Windows(1)/CurrentShape/ODataComplexTypeInheritanceSample.Circle/Radius";  
  29.        Comment("GET " + requestUri);  
  30.        response = Get(requestUri);  
  31.        Comment(response);  
  32.   
  33.        // Function that returns a base complex type  
  34.        requestUri = _baseAddress + "/odata/Windows(1)/ODataComplexTypeInheritanceSample.GetTheLastOptionalShape()";  
  35.        Comment("GET " + requestUri);  
  36.        response = Get(requestUri);  
  37.        Comment(response);  
  38.   
  39.        // Action that takes in a base complex type  
  40.        requestUri = _baseAddress + "/odata/Windows(1)/ODataComplexTypeInheritanceSample.AddOptionalShape";  
  41.        Comment("POST " + requestUri);  
  42.        response = ActionCall(requestUri);  
  43.        Comment(response);  
  44.   
  45.        // This method illustrate how to add an entity which contains derived complex type.  
  46.        requestUri = _baseAddress + "/odata/Windows";  
  47.        Comment("POST " + requestUri);  
  48.        response = Post(requestUri);  
  49.        Comment(response);  
  50.   
  51.        Console.WriteLine("\nPress any key to continue...");  
  52.        Console.ReadKey();  
  53.    }  
  54. }  
Now add a new class. To add the class use the following procedure.
Right-click the project in Solution Explorer then select Add --> New Item --> Class then name the class WindowsController.

And write the following code inside the WindowsController class.
  1. public class WindowsController : ODataController   
  2. {  
  3.     private IList<Window> _windows = new List<Window>();  
  4.   
  5.     public WindowsController()  
  6.     {  
  7.         Polygon triagle = new Polygon() { HasBorder = true, Vertexes = new List<Point>() };  
  8.         triagle.Vertexes.Add(new Point() { X = 1, Y = 2 });  
  9.         triagle.Vertexes.Add(new Point() { X = 2, Y = 3 });  
  10.         triagle.Vertexes.Add(new Point() { X = 4, Y = 8 });  
  11.   
  12.         Polygon rectangle = new Polygon() { HasBorder = true, Vertexes = new List<Point>() };  
  13.         rectangle.Vertexes.Add(new Point() { X = 0, Y = 0 });  
  14.         rectangle.Vertexes.Add(new Point() { X = 2, Y = 0 });  
  15.         rectangle.Vertexes.Add(new Point() { X = 2, Y = 2 });  
  16.         rectangle.Vertexes.Add(new Point() { X = 0, Y = 2 });  
  17.   
  18.         Circle circle = new Circle() { HasBorder = true, Center = new Point(), Radius = 2 };  
  19.   
  20.         Window dashboardWindow = new Window  
  21.         {  
  22.             Id = 1,  
  23.             Name = "CircleWindow",  
  24.             CurrentShape = circle,  
  25.             OptionalShapes = new List<Shape>(),  
  26.         };  
  27.         dashboardWindow.OptionalShapes.Add(rectangle);  
  28.         _windows.Add(dashboardWindow);  
  29.   
  30.         Window popupWindow = new Window  
  31.         {  
  32.             Id = 2,  
  33.             Name = "Popup",  
  34.             CurrentShape = rectangle,  
  35.             OptionalShapes = new List<Shape>(),  
  36.             Parent = dashboardWindow,  
  37.         };  
  38.   
  39.         popupWindow.OptionalShapes.Add(triagle);  
  40.         popupWindow.OptionalShapes.Add(circle);  
  41.         _windows.Add(popupWindow);  
  42.   
  43.         Window anotherPopupWindow = new Window  
  44.         {  
  45.             Id = 3,  
  46.             Name = "AnotherPopup",  
  47.             CurrentShape = rectangle,  
  48.             OptionalShapes = new List<Shape>(),  
  49.             Parent = popupWindow,  
  50.         };  
  51.   
  52.         anotherPopupWindow.OptionalShapes.Add(triagle);  
  53.         anotherPopupWindow.OptionalShapes.Add(circle);  
  54.         _windows.Add(anotherPopupWindow);  
  55.     }  
  56.   
  57.     [EnableQuery]  
  58.     public IHttpActionResult Get()  
  59.     {  
  60.         return Ok(_windows);  
  61.     }  
  62.   
  63.     [EnableQuery]  
  64.     public SingleResult<Window> GetWindow([FromODataUri] int key)  
  65.     {  
  66.         return SingleResult.Create<Window>(_windows.Where(w => w.Id == key).AsQueryable());  
  67.     }  
  68.   
  69.     public IHttpActionResult Post(Window window)  
  70.     {  
  71.         _windows.Add(window);  
  72.         window.Id = _windows.Count + 1;  
  73.         return Created(window);  
  74.     }  
  75.   
  76.     [ODataRoute("Windows({key})/CurrentShape/ODataComplexTypeInheritanceSample.Circle/Radius")]  
  77.     public IHttpActionResult GetRadius(int key)  
  78.     {  
  79.         Window window = _windows.FirstOrDefault(e => e.Id == key);  
  80.         if (window == null)  
  81.         {  
  82.             return NotFound();  
  83.         }  
  84.   
  85.         return Ok(((Circle)window.CurrentShape).Radius);  
  86.     }  
  87.   
  88.     [ODataRoute("Windows({key})/ODataComplexTypeInheritanceSample.GetTheLastOptionalShape()")]  
  89.     public IHttpActionResult GetTheLastOptionalShape(int key)  
  90.     {  
  91.         Window window = _windows.FirstOrDefault(e => e.Id == key);  
  92.         if (window == null)  
  93.         {  
  94.             return NotFound();  
  95.         }  
  96.         int count = window.OptionalShapes.Count;  
  97.         Shape last = window.OptionalShapes.ElementAt(count - 1);  
  98.         return Ok(last);  
  99.     }  
  100.   
  101.   
  102.     public IHttpActionResult AddOptionalShape(int key, ODataActionParameters parameters)  
  103.     {  
  104.         Shape newShape = parameters["shape"as Shape;  
  105.         Window window = _windows.FirstOrDefault(e => e.Id == key);  
  106.         if (window == null)  
  107.         {  
  108.             return NotFound();  
  109.         }  
  110.         window.OptionalShapes.Add(newShape);  
  111.         int count = window.OptionalShapes.Count;  
  112.         return Ok(count);  
  113.     }  
  114. }  
Step 3

Again add a class (ODataModels) into the current project with the following code:

  1. public class ODataModels  
  2. {  
  3.     public static IEdmModel GetModel()  
  4.     {  
  5.         ODataConventionModelBuilder builder = new ODataConventionModelBuilder();  
  6.         EntitySetConfiguration<Window> windows = builder.EntitySet<Window>("Windows");  
  7.         EntityTypeConfiguration<Window> window = windows.EntityType;  
  8.   
  9.         // Action that takes in a base complex type.  
  10.         ActionConfiguration actionConfiguration = window.Action("AddOptionalShape");  
  11.         actionConfiguration.Parameter<Shape>("shape");  
  12.         actionConfiguration.Returns<int>(); // The number of all optional shapes  
  13.   
  14.         // Function that returns a base complex type  
  15.         var functionConfiguration = window.Function("GetTheLastOptionalShape");  
  16.         functionConfiguration.Returns<Shape>();  
  17.   
  18.         builder.Namespace = typeof(Window).Namespace;  
  19.   
  20.         return builder.GetEdmModel();  
  21.     }  
  22. }  

Step 4

Now add a new folder to the current project. To do so use the following procedure.

Right-click on the project in Solution Explorer and select Add -> New Folder then name it DomainModel.
 
Step 5
 
Add the following five classes inside the DomainModel folder: 
  • Circle.cs
  • Point.cs
  • Polygon.cs
  • Shape.cs
  • Window.cs
With the following code:
Circle.cs
  1. public class Circle : Shape  
  2. {  
  3.        public Point Center { getset; }  
  4.        public int Radius { getset; }  
  5.   
  6.        public override string ToString()  
  7.        {  
  8.            // {centerX, centerY, radius}  
  9.            return "{" + Center.X + "," + Center.Y + "," + Radius + "}";  
  10.        }  
  11.  }  
Point.css  
  1. public class Point  
  2. {  
  3.        public int X { getset; }  
  4.        public int Y { getset; }  
  5. }  
Polygon.cs
  1. public class Polygon : Shape  
  2. {  
  3.     public IList<Point> Vertexes { getset; }  
  4.     public Polygon()  
  5.     {  
  6.         Vertexes = new List<Point>();  
  7.     }  
  8. }  
Shape.cs 
  1. public abstract class Shape  
  2. {  
  3.     public bool HasBorder { getset; }  
  4. }  
Windows.cs 
  1. public class Window  
  2. {  
  3.      public Window()  
  4.      {  
  5.          OptionalShapes = new List<Shape>();  
  6.      }  
  7.      public int Id { getset; }  
  8.      public string Name { getset; }  
  9.      public Window Parent { getset; }  
  10.      public Shape CurrentShape { getset; }  
  11.      public IList<Shape> OptionalShapes { getset; }  
Step 6

This application must have the following DLL files. 

PAckages
Step 7
 
The following packages must be installed: 
  • Microsoft.AspNet.OData -Version 5.3.0
  • Microsoft.AspNet.WebApi.OData -Version 5.3.0
 Step 8
 
To add all these packages Use the following procedure. Right-click on the project in Solution Explorer  then select Manage NuGet Packages.
Nugate
 
NuGet
 
Output 
 
 Output
 
Note
 
After executing the project we are getting the following error. We will solve this problem in a future article.
 
Summary

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.

Up Next
    Ebook Download
    View all
    Learn
    View all