Öffnen von Typen in OData v4 mit ASP.NET-Web-API

von Microsoft

In OData v4 ist ein geöffneter Typ ein strukturierter Typ, der dynamische Eigenschaften enthält, zusätzlich zu allen Eigenschaften, die in der Typdefinition deklariert sind. Mit offenen Typen können Sie Ihre Datenmodelle flexibler machen. In diesem Tutorial erfahren Sie, wie Sie offene Typen in ASP.NET-Web-API OData verwenden.

In diesem Tutorial wird davon ausgegangen, dass Sie bereits wissen, wie Sie einen OData-Endpunkt in ASP.NET-Web-API erstellen. Andernfalls lesen Sie zuerst Erstellen eines OData v4-Endpunkts .

Im Tutorial verwendete Softwareversionen

  • Web-API OData 5.3
  • OData v4

Zunächst einige OData-Terminologien:

  • Entitätstyp: Ein strukturierter Typ mit einem Schlüssel.
  • Komplexer Typ: Ein strukturierter Typ ohne Schlüssel.
  • Offener Typ: Ein Typ mit dynamischen Eigenschaften. Sowohl Entitätstypen als auch komplexe Typen können geöffnet sein.

Der Wert einer dynamischen Eigenschaft kann ein primitiver Typ, ein komplexer Typ oder ein Enumerationstyp sein. oder eine Auflistung eines dieser Typen. Weitere Informationen zu geöffneten Typen finden Sie in der OData v4-Spezifikation.

Installieren der Web-OData-Bibliotheken

Verwenden Sie den NuGet-Paket-Manager, um die neuesten OData-Bibliotheken der Web-API zu installieren. Aus dem Fenster Paket-Manager-Konsole:

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

Definieren der CLR-Typen

Definieren Sie zunächst die EDM-Modelle als CLR-Typen.

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; }
}

Wenn das Entitätsdatenmodell (Entity Data Model, EDM) erstellt wird,

  • Category ist ein Enumerationstyp.
  • Address ist ein komplexer Typ. (Es verfügt nicht über einen Schlüssel, daher handelt es sich nicht um einen Entitätstyp.)
  • Customer ist ein Entitätstyp. (Es verfügt über einen Schlüssel.)
  • Press ist ein offener komplexer Typ.
  • Book ist ein offener Entitätstyp.

Um einen offenen Typ zu erstellen, muss der CLR-Typ über eine Eigenschaft vom Typ IDictionary<string, object>verfügen, die die dynamischen Eigenschaften enthält.

Erstellen des EDM-Modells

Wenn Sie ODataConventionModelBuilder zum Erstellen des EDM verwenden und PressBook basierend auf dem Vorhandensein einer IDictionary<string, object> Eigenschaft automatisch als geöffnete Typen hinzugefügt werden.

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);

    }
}

Sie können den EDM auch explizit mit ODataModelBuilder erstellen.

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();

Hinzufügen eines OData-Controllers

Fügen Sie als Nächstes einen OData-Controller hinzu. In diesem Tutorial verwenden wir einen vereinfachten Controller, der nur GET- und POST-Anforderungen unterstützt und eine In-Memory-Liste zum Speichern von Entitäten verwendet.

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);
        }
    }
}

Beachten Sie, dass die erste Book instance keine dynamischen Eigenschaften aufweist. Die zweite Book instance weist die folgenden dynamischen Eigenschaften auf:

  • "Veröffentlicht": Primitiver Typ
  • "Autoren": Sammlung von primitiven Typen
  • "OtherCategories": Auflistung von Enumerationstypen.

Außerdem weist die Press Eigenschaft dieses Book instance die folgenden dynamischen Eigenschaften auf:

  • "Blog": Primitiver Typ
  • "Adresse": Komplexer Typ

Abfragen der Metadaten

Um das OData-Metadatendokument abzurufen, senden Sie eine GET-Anforderung an ~/$metadata. Der Antworttext sollte wie folgt aussehen:

<?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>

Im Metadatendokument sehen Sie Folgendes:

  • Für die Book Typen und Press ist der Wert des OpenType Attributs true. Die Customer Typen und Address verfügen nicht über dieses Attribut.
  • Der Book Entitätstyp verfügt über drei deklarierte Eigenschaften: ISBN, Title und Press. Die OData-Metadaten enthalten nicht die Book.Properties Eigenschaft aus der CLR-Klasse.
  • Ebenso weist der Press komplexe Typ nur zwei deklarierte Eigenschaften auf: Name und Kategorie. Die Metadaten enthalten nicht die Press.DynamicProperties Eigenschaft aus der CLR-Klasse.

Abfragen einer Entität

Um das Buch mit der ISBN zu erhalten, die "978-0-7356-7942-9" entspricht, senden Sie eine GET-Anforderung an ~/Books('978-0-7356-7942-9'). Der Antworttext sollte wie folgt aussehen. (Eingerückt, um ihn lesbarer zu machen.)

{
  "@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"
  ]
}

Beachten Sie, dass die dynamischen Eigenschaften inline mit den deklarierten Eigenschaften enthalten sind.

POST einer Entität

Um eine Book-Entität hinzuzufügen, senden Sie eine POST-Anforderung an ~/Books. Der Client kann dynamische Eigenschaften in der Anforderungsnutzlast festlegen.

Hier sehen Sie eine Beispielanforderung. Beachten Sie die Eigenschaften "Price" und "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"
}

Wenn Sie einen Haltepunkt in der Controllermethode festlegen, können Sie sehen, dass die Web-API diese Eigenschaften dem Properties Wörterbuch hinzugefügt hat.

Screenshot des Codes, der eine POST-Anforderung sendet, in dem der Codeteil

Zusätzliche Ressourcen

OData-Beispiel für den offenen Typ