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.
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);
- }
- }
Now enter the following Main() method into the Program class.
- 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;
-
-
-
-
- requestUri = _baseAddress + "/odata/$metadata";
- Comment("GET " + requestUri);
- response = Get(requestUri);
- Comment(response, true);
-
-
-
-
-
- requestUri = _baseAddress + "/odata/Windows(1)";
- Comment("GET " + requestUri);
- response = Get(requestUri);
- Comment(response);
-
-
- requestUri = _baseAddress + "/odata/Windows(1)/CurrentShape/ODataComplexTypeInheritanceSample.Circle/Radius";
- Comment("GET " + requestUri);
- response = Get(requestUri);
- Comment(response);
-
-
- requestUri = _baseAddress + "/odata/Windows(1)/ODataComplexTypeInheritanceSample.GetTheLastOptionalShape()";
- Comment("GET " + requestUri);
- response = Get(requestUri);
- Comment(response);
-
-
- requestUri = _baseAddress + "/odata/Windows(1)/ODataComplexTypeInheritanceSample.AddOptionalShape";
- Comment("POST " + requestUri);
- response = ActionCall(requestUri);
- Comment(response);
-
-
- requestUri = _baseAddress + "/odata/Windows";
- Comment("POST " + requestUri);
- response = Post(requestUri);
- Comment(response);
-
- Console.WriteLine("\nPress any key to continue...");
- Console.ReadKey();
- }
- }
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.
- 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);
- }
- }
Step 3
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;
-
-
- ActionConfiguration actionConfiguration = window.Action("AddOptionalShape");
- actionConfiguration.Parameter<Shape>("shape");
- actionConfiguration.Returns<int>();
-
-
- 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.
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
- public class Circle : Shape
- {
- public Point Center { get; set; }
- public int Radius { get; set; }
-
- public override string ToString()
- {
-
- return "{" + Center.X + "," + Center.Y + "," + Radius + "}";
- }
- }
Point.css
- public class Point
- {
- public int X { get; set; }
- public int Y { get; set; }
- }
Polygon.cs
- public class Polygon : Shape
- {
- public IList<Point> Vertexes { get; set; }
- public Polygon()
- {
- Vertexes = new List<Point>();
- }
- }
Shape.cs
- public abstract class Shape
- {
- public bool HasBorder { get; set; }
- }
Windows.cs
- 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; }
- }
Step 6
This application must have the following DLL files.
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.
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.