"
ASP.NET (snapshot 2017) Microsoft documentation and samples

Open Types in OData v4 with ASP.NET Web API

by Microsoft

In OData v4, an open type is a stuctured 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. This tutorial shows how to use open types in ASP.NET Web API OData.

This tutorial assumes that you already know how to create an OData endpoint in ASP.NET Web API. If not, start by reading Create an OData v4 Endpoint first.

Software versions used in the tutorial

  • Web API OData 5.3
  • OData v4

First, some OData terminology:

The value of a dynamic property can be a primitive type, complex type, or enumeration type; or a collection of any of those types. For more information about open types, see the OData v4 specification.

Install the Web OData Libraries

Use NuGet Package Manager to install the latest Web API OData libraries. From the Package Manager Console window:

[!code-consoleMain]

   1:  Install-Package Microsoft.AspNet.OData
   2:  Install-Package Microsoft.AspNet.WebApi.OData

Define the CLR Types

Start by defining the EDM models as CLR types.

[!code-csharpMain]

   1:  public enum Category
   2:  {
   3:      Book,
   4:      Magazine,
   5:      EBook
   6:  }
   7:   
   8:  public class Address
   9:  {
  10:      public string City { get; set; }
  11:      public string Street { get; set; }
  12:  }
  13:   
  14:  public class Customer
  15:  {
  16:      public int Id { get; set; }
  17:      public string Name { get; set; }
  18:      public Address Address { get; set; }
  19:  }
  20:   
  21:  public class Press
  22:  {
  23:      public string Name { get; set; }
  24:      public string Email { get; set; }
  25:      public Category Category { get; set; }
  26:      public IDictionary<string, object> DynamicProperties { get; set; }
  27:  }
  28:   
  29:  public class Book
  30:  {
  31:      [Key]
  32:      public string ISBN { get; set; }
  33:      public string Title { get; set; }
  34:      public Press Press { get; set; }
  35:      public IDictionary<string, object> Properties { get; set; }
  36:  }

When the Entity Data Model (EDM) is created,

To create an open type, the CLR type must have a property of type IDictionary<string, object>, which holds the dynamic properties.

Build the EDM Model

If you use ODataConventionModelBuilder to create the EDM, Press and Book are automatically added as open types, based on the presence of a IDictionary<string, object> property.

[!code-csharpMain]

   1:  public static class WebApiConfig
   2:  {
   3:      public static void Register(HttpConfiguration config)
   4:      {
   5:          ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
   6:          builder.EntitySet<Book>("Books");
   7:          builder.EntitySet<Customer>("Customers");
   8:          var model = builder.GetEdmModel();
   9:   
  10:          config.MapODataServiceRoute(
  11:              routeName: "ODataRoute",
  12:              routePrefix: null,
  13:              model: model);
  14:   
  15:      }
  16:  }

You can also build the EDM explicitly, using ODataModelBuilder.

[!code-csharpMain]

   1:  ODataModelBuilder builder = new ODataModelBuilder();
   2:   
   3:  ComplexTypeConfiguration<Press> pressType = builder.ComplexType<Press>();
   4:  pressType.Property(c => c.Name);
   5:  // ...
   6:  pressType.HasDynamicProperties(c => c.DynamicProperties);
   7:   
   8:  EntityTypeConfiguration<Book> bookType = builder.EntityType<Book>();
   9:  bookType.HasKey(c => c.ISBN);
  10:  bookType.Property(c => c.Title);
  11:  // ...
  12:  bookType.ComplexProperty(c => c.Press);
  13:  bookType.HasDynamicProperties(c => c.Properties);
  14:   
  15:  // ...
  16:  builder.EntitySet<Book>("Books");
  17:  IEdmModel model = builder.GetEdmModel();

Add an OData Controller

Next, add an OData controller. For this tutorial, we???ll use a simplified controller that just supports GET and POST requests, and uses an in-memory list to store entities.

[!code-csharpMain]

   1:  using System;
   2:  using System.Collections.Generic;
   3:  using System.Linq;
   4:  using System.Web.Http;
   5:  using System.Web.OData;
   6:   
   7:  namespace MyApp.Controllers
   8:  {
   9:      public class BooksController : ODataController
  10:      {
  11:          private IList<Book> _books = new List<Book>
  12:          {
  13:              new Book
  14:              {
  15:                  ISBN = "978-0-7356-8383-9",
  16:                  Title = "SignalR Programming in Microsoft ASP.NET",
  17:                  Press = new Press
  18:                  {
  19:                      Name = "Microsoft Press",
  20:                      Category = Category.Book
  21:                  }
  22:              },
  23:   
  24:              new Book
  25:              {
  26:                  ISBN = "978-0-7356-7942-9",
  27:                  Title = "Microsoft Azure SQL Database Step by Step",
  28:                  Press = new Press
  29:                  {
  30:                      Name = "Microsoft Press",
  31:                      Category = Category.EBook,
  32:                      DynamicProperties = new Dictionary<string, object>
  33:                      {
  34:                          { "Blog", "http://blogs.msdn.com/b/microsoft_press/" },
  35:                          { "Address", new Address { 
  36:                                City = "Redmond", Street = "One Microsoft Way" }
  37:                          }
  38:                      }
  39:                  },
  40:                  Properties = new Dictionary<string, object>
  41:                  {
  42:                      { "Published", new DateTimeOffset(2014, 7, 3, 0, 0, 0, 0, new TimeSpan(0))},
  43:                      { "Authors", new [] { "Leonard G. Lobel", "Eric D. Boyd" }},
  44:                      { "OtherCategories", new [] {Category.Book, Category.Magazine}}
  45:                  }
  46:              }
  47:          };
  48:   
  49:          [EnableQuery]
  50:          public IQueryable<Book> Get()
  51:          {
  52:              return _books.AsQueryable();
  53:          }
  54:   
  55:          public IHttpActionResult Get([FromODataUri]string key)
  56:          {
  57:              Book book = _books.FirstOrDefault(e => e.ISBN == key);
  58:              if (book == null)
  59:              {
  60:                  return NotFound();
  61:              }
  62:   
  63:              return Ok(book);
  64:          }
  65:   
  66:          public IHttpActionResult Post(Book book)
  67:          {
  68:              if (!ModelState.IsValid)
  69:              {
  70:                  return BadRequest(ModelState);
  71:              } 
  72:              // For this sample, we aren't enforcing unique keys.
  73:              _books.Add(book);
  74:              return Created(book);
  75:          }
  76:      }
  77:  }

Notice that the first Book instance has no dynamic properties. The second Book instance has the following dynamic properties:

Also, the Press property of that Book instance has the following dynamic properties:

Query the Metadata

To get the OData metadata document, send a GET request to ~/$metadata. The response body should look similar to this:

[!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="MyApp.Models" xmlns="http://docs.oasis-open.org/odata/ns/edm">
   5:        <EntityType Name="Book" OpenType="true">
   6:          <Key>
   7:            <PropertyRef Name="ISBN" />
   8:          </Key>
   9:          <Property Name="ISBN" Type="Edm.String" Nullable="false" />
  10:          <Property Name="Title" Type="Edm.String" />
  11:          <Property Name="Press" Type="MyApp.Models.Press" />
  12:        </EntityType>
  13:        <EntityType Name="Customer">
  14:          <Key>
  15:            <PropertyRef Name="Id" />
  16:          </Key>
  17:          <Property Name="Id" Type="Edm.Int32" Nullable="false" />
  18:          <Property Name="Name" Type="Edm.String" />
  19:          <Property Name="Address" Type="MyApp.Models.Address" />
  20:        </EntityType>
  21:        <ComplexType Name="Press" OpenType="true">
  22:          <Property Name="Name" Type="Edm.String" />
  23:          <Property Name="Category" Type="MyApp.Models.Category" Nullable="false" />
  24:        </ComplexType>
  25:        <ComplexType Name="Address">
  26:          <Property Name="City" Type="Edm.String" />
  27:          <Property Name="Street" Type="Edm.String" />
  28:        </ComplexType>
  29:        <EnumType Name="Category">
  30:          <Member Name="Book" Value="0" />
  31:          <Member Name="Magazine" Value="1" />
  32:          <Member Name="EBook" Value="2" />
  33:        </EnumType>
  34:      </Schema>
  35:      <Schema Namespace="Default" xmlns="http://docs.oasis-open.org/odata/ns/edm">
  36:        <EntityContainer Name="Container">
  37:          <EntitySet Name="Books" EntityType="MyApp.Models.Book" />
  38:          <EntitySet Name="Customers" EntityType="MyApp.Models.Customer" />
  39:        </EntityContainer>
  40:      </Schema>
  41:    </edmx:DataServices>
  42:  </edmx:Edmx>

From the metadata document, you can see that:

Query an Entity

To get the book with ISBN equal to ???978-0-7356-7942-9???, send send a GET request to ~/Books('978-0-7356-7942-9'). The response body should look similar to the following. (Indented to make it more readable.)

[!code-consoleMain]

   1:  {
   2:    "@odata.context":"http://localhost:37141/$metadata#Books/$entity",
   3:      "ISBN":"978-0-7356-7942-9",
   4:      "Title":"Microsoft Azure SQL Database Step by Step",
   5:      "Press":{
   6:        "Name":"Microsoft Press",
   7:        "Category":"EBook",
   8:        "Blog":"http://blogs.msdn.com/b/microsoft_press/",
   9:        "Address":{
  10:          "@odata.type":"#MyApp.Models.Address",
  11:          "City":"Redmond",
  12:          "Street":"One Microsoft Way"
  13:        }
  14:    },
  15:    "Published":"2014-07-03T00:00:00Z",
  16:    "Authors@odata.type":"#Collection(String)",
  17:    "Authors":[
  18:      "Leonard G. Lobel","Eric D. Boyd"
  19:    ],
  20:    "OtherCategories@odata.type":"#Collection(MyApp.Models.Category)",
  21:    "OtherCategories":[
  22:      "Book","Magazine"
  23:    ]
  24:  }

Notice that the dynamic properties are included inline with the declared properties.

POST an Entity

To add a Book entity, send a POST request to ~/Books. The client can set dynamic properties in the request payload.

Here is an example request. Note the ???Price??? and ???Published??? properties.

[!code-consoleMain]

   1:  POST http://localhost:37141/Books HTTP/1.1
   2:  User-Agent: Fiddler
   3:  Host: localhost:37141
   4:  Content-Type: application/json
   5:  Content-Length: 191
   6:   
   7:  {
   8:    "ISBN":"978-0-7356-8383-9","Title":"Programming Microsoft ASP.NET MVC","Press":{
   9:    "Name":"Microsoft Press","Category":"Book"
  10:     }, "Price": 49.99, "Published":"2014-02-15T00:00:00Z"
  11:  }

If you set a breakpoint in the controller method, you can see that Web API added these properties to the Properties dictionary.

Additional Resources

OData Open Type Sample



Comments ( )
Link to this page: //www.vb-net.com/AspNet-DocAndSamples-2017/aspnet/web-api/overview/odata-support-in-aspnet-web-api/odata-v4/use-open-types-in-odata-v4.htm
< THANKS ME>