Condividi tramite


Tipi aperti in OData v4 con API Web ASP.NET

di Microsoft

In OData v4 un tipo aperto è un tipo strutturato che contiene proprietà dinamiche, oltre a tutte le proprietà dichiarate nella definizione del tipo. I tipi aperti consentono di aggiungere flessibilità ai modelli di dati. Questa esercitazione illustra come usare tipi aperti in API Web ASP.NET OData.

Questa esercitazione presuppone che si sappia già come creare un endpoint OData in API Web ASP.NET. In caso contrario, prima di tutto leggere Creare un endpoint OData v4 .

Versioni software usate nell'esercitazione

  • API Web OData 5.3
  • OData v4

Prima di tutto, una terminologia OData:

  • Tipo di entità: tipo strutturato con una chiave.
  • Tipo complesso: tipo strutturato senza chiave.
  • Tipo aperto: tipo con proprietà dinamiche. Entrambi i tipi di entità e i tipi complessi possono essere aperti.

Il valore di una proprietà dinamica può essere un tipo primitivo, un tipo complesso o un tipo di enumerazione; o una raccolta di uno di questi tipi. Per altre informazioni sui tipi aperti, vedere la specifica OData v4.

Installare le librerie OData Web

Usare Gestione pacchetti NuGet per installare le librerie OData più recenti dell'API Web. Dalla finestra Console di Gestione pacchetti:

Install-Package Microsoft.AspNet.OData
Install-Package Microsoft.AspNet.WebApi.OData

Definire i tipi CLR

Per iniziare, definire i modelli EDM come tipi CLR.

public enum Category
{
    Book,
    Magazine,
    EBook
}

public class Address
{
    public string City { get; set; }
    public string Street { get; set; }
}

public class Customer
{
    public int Id { get; set; }
    public string Name { get; set; }
    public Address Address { get; set; }
}

public class Press
{
    public string Name { get; set; }
    public string Email { get; set; }
    public Category Category { get; set; }
    public IDictionary<string, object> DynamicProperties { get; set; }
}

public class Book
{
    [Key]
    public string ISBN { get; set; }
    public string Title { get; set; }
    public Press Press { get; set; }
    public IDictionary<string, object> Properties { get; set; }
}

Quando viene creato Entity Data Model (EDM),

  • Category è un tipo di enumerazione.
  • Address è un tipo complesso. Non ha una chiave, quindi non è un tipo di entità.
  • Customer è un tipo di entità. (Ha una chiave.
  • Press è un tipo complesso aperto.
  • Book è un tipo di entità aperto.

Per creare un tipo aperto, il tipo CLR deve avere una proprietà di tipo IDictionary<string, object>, che contiene le proprietà dinamiche.

Compilare il modello EDM

Se si usa ODataConventionModelBuilder per creare l'EDM Press e Book vengono aggiunti automaticamente come tipi aperti, in base alla presenza di una IDictionary<string, object> proprietà.

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
        builder.EntitySet<Book>("Books");
        builder.EntitySet<Customer>("Customers");
        var model = builder.GetEdmModel();

        config.MapODataServiceRoute(
            routeName: "ODataRoute",
            routePrefix: null,
            model: model);

    }
}

È anche possibile compilare in modo esplicito EDM usando ODataModelBuilder.

ODataModelBuilder builder = new ODataModelBuilder();

ComplexTypeConfiguration<Press> pressType = builder.ComplexType<Press>();
pressType.Property(c => c.Name);
// ...
pressType.HasDynamicProperties(c => c.DynamicProperties);

EntityTypeConfiguration<Book> bookType = builder.EntityType<Book>();
bookType.HasKey(c => c.ISBN);
bookType.Property(c => c.Title);
// ...
bookType.ComplexProperty(c => c.Press);
bookType.HasDynamicProperties(c => c.Properties);

// ...
builder.EntitySet<Book>("Books");
IEdmModel model = builder.GetEdmModel();

Aggiungere un controller OData

Aggiungere quindi un controller OData. Per questa esercitazione si userà un controller semplificato che supporta solo le richieste GET e POST e userà un elenco in memoria per archiviare le entità.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Http;
using System.Web.OData;

namespace MyApp.Controllers
{
    public class BooksController : ODataController
    {
        private IList<Book> _books = new List<Book>
        {
            new Book
            {
                ISBN = "978-0-7356-8383-9",
                Title = "SignalR Programming in Microsoft ASP.NET",
                Press = new Press
                {
                    Name = "Microsoft Press",
                    Category = Category.Book
                }
            },

            new Book
            {
                ISBN = "978-0-7356-7942-9",
                Title = "Microsoft Azure SQL Database Step by Step",
                Press = new Press
                {
                    Name = "Microsoft Press",
                    Category = Category.EBook,
                    DynamicProperties = new Dictionary<string, object>
                    {
                        { "Blog", "https://blogs.msdn.com/b/microsoft_press/" },
                        { "Address", new Address { 
                              City = "Redmond", Street = "One Microsoft Way" }
                        }
                    }
                },
                Properties = new Dictionary<string, object>
                {
                    { "Published", new DateTimeOffset(2014, 7, 3, 0, 0, 0, 0, new TimeSpan(0))},
                    { "Authors", new [] { "Leonard G. Lobel", "Eric D. Boyd" }},
                    { "OtherCategories", new [] {Category.Book, Category.Magazine}}
                }
            }
        };

        [EnableQuery]
        public IQueryable<Book> Get()
        {
            return _books.AsQueryable();
        }

        public IHttpActionResult Get([FromODataUri]string key)
        {
            Book book = _books.FirstOrDefault(e => e.ISBN == key);
            if (book == null)
            {
                return NotFound();
            }

            return Ok(book);
        }

        public IHttpActionResult Post(Book book)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            } 
            // For this sample, we aren't enforcing unique keys.
            _books.Add(book);
            return Created(book);
        }
    }
}

Si noti che la prima Book istanza non ha proprietà dinamiche. La seconda Book istanza ha le proprietà dinamiche seguenti:

  • "Published": Tipo primitivo
  • "Authors": Raccolta di tipi primitivi
  • "OtherCategories": raccolta di tipi di enumerazione.

Inoltre, la Press proprietà di tale Book istanza ha le proprietà dinamiche seguenti:

  • "Blog": Tipo primitivo
  • "Indirizzo": tipo complesso

Eseguire query sui metadati

Per ottenere il documento di metadati OData, inviare una richiesta GET a ~/$metadata. Il corpo della risposta dovrebbe essere simile al seguente:

<?xml version="1.0" encoding="utf-8"?>
<edmx:Edmx Version="4.0" xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx">
  <edmx:DataServices>
    <Schema Namespace="MyApp.Models" xmlns="http://docs.oasis-open.org/odata/ns/edm">
      <EntityType Name="Book" OpenType="true">
        <Key>
          <PropertyRef Name="ISBN" />
        </Key>
        <Property Name="ISBN" Type="Edm.String" Nullable="false" />
        <Property Name="Title" Type="Edm.String" />
        <Property Name="Press" Type="MyApp.Models.Press" />
      </EntityType>
      <EntityType Name="Customer">
        <Key>
          <PropertyRef Name="Id" />
        </Key>
        <Property Name="Id" Type="Edm.Int32" Nullable="false" />
        <Property Name="Name" Type="Edm.String" />
        <Property Name="Address" Type="MyApp.Models.Address" />
      </EntityType>
      <ComplexType Name="Press" OpenType="true">
        <Property Name="Name" Type="Edm.String" />
        <Property Name="Category" Type="MyApp.Models.Category" Nullable="false" />
      </ComplexType>
      <ComplexType Name="Address">
        <Property Name="City" Type="Edm.String" />
        <Property Name="Street" Type="Edm.String" />
      </ComplexType>
      <EnumType Name="Category">
        <Member Name="Book" Value="0" />
        <Member Name="Magazine" Value="1" />
        <Member Name="EBook" Value="2" />
      </EnumType>
    </Schema>
    <Schema Namespace="Default" xmlns="http://docs.oasis-open.org/odata/ns/edm">
      <EntityContainer Name="Container">
        <EntitySet Name="Books" EntityType="MyApp.Models.Book" />
        <EntitySet Name="Customers" EntityType="MyApp.Models.Customer" />
      </EntityContainer>
    </Schema>
  </edmx:DataServices>
</edmx:Edmx>

Dal documento di metadati è possibile osservare che:

  • Per i Book tipi e Press , il valore dell'attributo OpenType è true. I Customer tipi e Address non hanno questo attributo.
  • Il Book tipo di entità ha tre proprietà dichiarate: ISBN, Title e Press. I metadati OData non includono la Book.Properties proprietà della classe CLR.
  • Analogamente, il Press tipo complesso ha solo due proprietà dichiarate: Name e Category. I metadati non includono la Press.DynamicProperties proprietà della classe CLR.

Eseguire una query su un'entità

Per ottenere il libro con ISBN uguale a "978-0-7356-7942-9", inviare una richiesta GET a ~/Books('978-0-7356-7942-9'). Il corpo della risposta dovrebbe essere simile al seguente. Rientro per renderlo più leggibile.

{
  "@odata.context":"http://localhost:37141/$metadata#Books/$entity",
    "ISBN":"978-0-7356-7942-9",
    "Title":"Microsoft Azure SQL Database Step by Step",
    "Press":{
      "Name":"Microsoft Press",
      "Category":"EBook",
      "Blog":"https://blogs.msdn.com/b/microsoft_press/",
      "Address":{
        "@odata.type":"#MyApp.Models.Address",
        "City":"Redmond",
        "Street":"One Microsoft Way"
      }
  },
  "Published":"2014-07-03T00:00:00Z",
  "Authors@odata.type":"#Collection(String)",
  "Authors":[
    "Leonard G. Lobel","Eric D. Boyd"
  ],
  "OtherCategories@odata.type":"#Collection(MyApp.Models.Category)",
  "OtherCategories":[
    "Book","Magazine"
  ]
}

Si noti che le proprietà dinamiche sono incluse inline con le proprietà dichiarate.

POST di un'entità

Per aggiungere un'entità Book, inviare una richiesta POST a ~/Books. Il client può impostare proprietà dinamiche nel payload della richiesta.

Ecco una richiesta di esempio. Si notino le proprietà "Price" e "Published".

POST http://localhost:37141/Books HTTP/1.1
User-Agent: Fiddler
Host: localhost:37141
Content-Type: application/json
Content-Length: 191

{
  "ISBN":"978-0-7356-8383-9","Title":"Programming Microsoft ASP.NET MVC","Press":{
  "Name":"Microsoft Press","Category":"Book"
   }, "Price": 49.99, "Published":"2014-02-15T00:00:00Z"
}

Se si imposta un punto di interruzione nel metodo controller, è possibile vedere che l'API Web ha aggiunto queste proprietà al Properties dizionario.

Screenshot del codice che invia una richiesta POST, evidenziando la parte

Risorse aggiuntive

Esempio di tipo aperto OData