Freigeben über


Erstellen eines OData v4-Endpunkts mit ASP.NET-Web-API

Das Open Data Protocol (OData) ist ein Datenzugriffsprotokoll für das Web. OData bietet eine einheitliche Möglichkeit zum Abfragen und Bearbeiten von Datasets über CRUD-Vorgänge (Erstellen, Lesen, Aktualisieren und Löschen).

ASP.NET-Web-API unterstützt sowohl v3 als auch v4 des Protokolls. Sie können sogar über einen v4-Endpunkt verfügen, der parallel zu einem v3-Endpunkt ausgeführt wird.

In diesem Tutorial wird gezeigt, wie Sie einen OData v4-Endpunkt erstellen, der CRUD-Vorgänge unterstützt.

Im Tutorial verwendete Softwareversionen

  • Web-API 5.2
  • OData v4
  • Visual Studio 2017 (Herunterladen von Visual Studio 2017 hier)
  • Entity Framework 6
  • .NET 4.7.2

Tutorialversionen

Informationen zu OData Version 3 finden Sie unter Erstellen eines OData v3-Endpunkts.

Erstellen des Visual Studio-Projekts

Wählen Sie in Visual Studio im Menü Datei die Option Neues>Projekt aus.

Erweitern Sie Installiertes>Visual C#>Web, und wählen Sie die Vorlage ASP.NET Webanwendung (.NET Framework) aus. Nennen Sie das Projekt "ProductService".

Screenshot des Neuen Visual Studio-Projektfensters mit Menüoptionen zum Erstellen einer A S P dot NET-Webanwendung mit dem dot NET Framework

Klicken Sie auf OK.

Screenshot der A S P dot NET-Webanwendung mit verfügbaren Vorlagen zum Erstellen der Anwendung mit einem Web A P I-Ordner und einer Kernreferenz.

Wählen Sie die Vorlage Leer aus. Wählen Sie unter Ordner und Kernverweise hinzufügen für die Option Web-API aus. Klicken Sie auf OK.

Installieren der OData-Pakete

Wählen Sie im Menü Extras die Optionen NuGet-Paket-Manager>Paket-Manager-Konsole aus. Geben Sie im Fenster Paket-Manager-Konsole Folgendes ein:

Install-Package Microsoft.AspNet.Odata

Mit diesem Befehl werden die neuesten OData NuGet-Pakete installiert.

Hinzufügen einer Modellklasse

Ein Modell ist ein Objekt, das eine Datenentität in Ihrer Anwendung darstellt.

Klicken Sie im Projektmappen-Explorer mit der rechten Maustaste auf den Ordner „Modelle“. Wählen Sie im KontextmenüKlassehinzufügen> aus.

Screenshot: Fenster des Projektmappen-Explorers, in dem der Pfad zum Hinzufügen eines Modellklassenobjekts zum Projekt hervorgehoben wird.

Hinweis

Gemäß der Konvention werden Modellklassen im Ordner Models platziert, aber Sie müssen diese Konvention nicht in Ihren eigenen Projekten befolgen.

Geben Sie der Klassen den Namen Product. Ersetzen Sie in der Datei Product.cs den Boilerplate-Code durch Folgendes:

namespace ProductService.Models
{
    public class Product
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public decimal Price { get; set; }
        public string Category { get; set; }
    }
}

Die Id Eigenschaft ist der Entitätsschlüssel. Clients können Entitäten nach Schlüssel abfragen. Um beispielsweise das Produkt mit der ID 5 abzurufen, lautet /Products(5)der URI . Die Id Eigenschaft ist auch der Primärschlüssel in der Back-End-Datenbank.

Aktivieren von Entity Framework

In diesem Tutorial verwenden wir Entity Framework (EF) Code First, um die Back-End-Datenbank zu erstellen.

Hinweis

Für die Web-API OData ist EF nicht erforderlich. Verwenden Sie eine beliebige Datenzugriffsebene, die Datenbankentitäten in Modelle übersetzen kann.

Installieren Sie zunächst das NuGet-Paket für EF. Wählen Sie im Menü Extras die Optionen NuGet-Paket-Manager>Paket-Manager-Konsole aus. Geben Sie im Fenster Paket-Manager-Konsole Folgendes ein:

Install-Package EntityFramework

Öffnen Sie die datei Web.config, und fügen Sie den folgenden Abschnitt innerhalb des Konfigurationselements nach dem configSections-Element hinzu.

<configuration>
  <configSections>
    <!-- ... -->
  </configSections>

  <!-- Add this: -->
  <connectionStrings>
    <add name="ProductsContext" connectionString="Data Source=(localdb)\mssqllocaldb; 
        Initial Catalog=ProductsContext; Integrated Security=True; MultipleActiveResultSets=True; 
        AttachDbFilename=|DataDirectory|ProductsContext.mdf"
      providerName="System.Data.SqlClient" />
  </connectionStrings>

Diese Einstellung fügt eine Verbindungszeichenfolge für eine LocalDB-Datenbank hinzu. Diese Datenbank wird verwendet, wenn Sie die App lokal ausführen.

Fügen Sie als Nächstes dem Ordner Models eine Klasse mit dem Namen ProductsContext hinzu:

using System.Data.Entity;
namespace ProductService.Models
{
    public class ProductsContext : DbContext
    {
        public ProductsContext() 
                : base("name=ProductsContext")
        {
        }
        public DbSet<Product> Products { get; set; }
    }
}

Gibt im Konstruktor "name=ProductsContext" den Namen der Verbindungszeichenfolge an.

Konfigurieren des OData-Endpunkts

Öffnen Sie die Datei App_Start/WebApiConfig.cs. Fügen Sie die folgenden using-Anweisungen hinzu:

using ProductService.Models;
using Microsoft.AspNet.OData.Builder;
using Microsoft.AspNet.OData.Extensions;

Fügen Sie dann der Register-Methode den folgenden Code hinzu:

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        // New code:
        ODataModelBuilder builder = new ODataConventionModelBuilder();
        builder.EntitySet<Product>("Products");
        config.MapODataServiceRoute(
            routeName: "ODataRoute",
            routePrefix: null,
            model: builder.GetEdmModel());
    }
}

Dieser Code erfüllt zwei Dinge:

  • Erstellt ein Entitätsdatenmodell (Entity Data Model, EDM).
  • Fügt eine Route hinzu.

Ein EDM ist ein abstraktes Modell der Daten. Das EDM wird verwendet, um das Dienstmetadatendokument zu erstellen. Die ODataConventionModelBuilder-Klasse erstellt ein EDM unter Verwendung von Standardbenennungskonventionen. Dieser Ansatz erfordert den geringsten Code. Wenn Sie mehr Kontrolle über das EDM wünschen, können Sie die ODataModelBuilder-Klasse verwenden, um das EDM zu erstellen, indem Sie Eigenschaften, Schlüssel und Navigationseigenschaften explizit hinzufügen.

Eine Route teilt der Web-API mit, wie HTTP-Anforderungen an den Endpunkt weitergeleitet werden. Um eine OData v4-Route zu erstellen, rufen Sie die MapODataServiceRoute-Erweiterungsmethode auf.

Wenn Ihre Anwendung über mehrere OData-Endpunkte verfügt, erstellen Sie eine separate Route für jeden. Geben Sie jeder Route einen eindeutigen Routennamen und ein präfix.

Hinzufügen des OData-Controllers

Ein Controller ist eine Klasse, die HTTP-Anforderungen verarbeitet. Sie erstellen einen separaten Controller für jede Entität in Ihrem OData-Dienst. In diesem Tutorial erstellen Sie einen Controller für die Product Entität.

Klicken Sie in Projektmappen-Explorer mit der rechten Maustaste auf den Ordner Controller, und wählen SieKlassehinzufügen> aus. Geben Sie der Klassen den Namen ProductsController.

Hinweis

Die Version dieses Tutorials für OData v3 verwendet das Gerüst "Controller hinzufügen" . Derzeit gibt es kein Gerüst für OData v4.

Ersetzen Sie den Boilerplate-Code in ProductsController.cs durch Folgendes.

using ProductService.Models;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using System.Web.Http;
using System.Web.OData;
namespace ProductService.Controllers
{
    public class ProductsController : ODataController
    {
        ProductsContext db = new ProductsContext();
        private bool ProductExists(int key)
        {
            return db.Products.Any(p => p.Id == key);
        } 
        protected override void Dispose(bool disposing)
        {
            db.Dispose();
            base.Dispose(disposing);
        }
    }
}

Der Controller verwendet die ProductsContext -Klasse, um mithilfe von EF auf die Datenbank zuzugreifen. Beachten Sie, dass der Controller die Dispose-Methode außer Kraft setzt, um den ProductsContext zu entsorgen.

Dies ist der Ausgangspunkt für den Controller. Als Nächstes fügen wir Methoden für alle CRUD-Vorgänge hinzu.

Abfragen des Entitätssatzes

Fügen Sie die folgenden Methoden hinzu ProductsController.

[EnableQuery]
public IQueryable<Product> Get()
{
    return db.Products;
}
[EnableQuery]
public SingleResult<Product> Get([FromODataUri] int key)
{
    IQueryable<Product> result = db.Products.Where(p => p.Id == key);
    return SingleResult.Create(result);
}

Die parameterlose Version der Get Methode gibt die gesamte Products-Auflistung zurück. Die Get Methode mit einem Schlüsselparameter sucht ein Produkt nach seinem Schlüssel (in diesem Fall die Id -Eigenschaft).

Mit dem Attribut [EnableQuery] können Clients die Abfrage mithilfe von Abfrageoptionen wie $filter, $sort und $page ändern. Weitere Informationen finden Sie unter Unterstützung von OData-Abfrageoptionen.

Hinzufügen einer Entität zum Entitätssatz

Um Clients das Hinzufügen eines neuen Produkts zur Datenbank zu ermöglichen, fügen Sie die folgende Methode hinzu ProductsController.

public async Task<IHttpActionResult> Post(Product product)
{
    if (!ModelState.IsValid)
    {
        return BadRequest(ModelState);
    }
    db.Products.Add(product);
    await db.SaveChangesAsync();
    return Created(product);
}

Aktualisieren einer Entität

OData unterstützt zwei verschiedene Semantiken zum Aktualisieren einer Entität: PATCH und PUT.

  • PATCH führt ein partielles Update aus. Der Client gibt nur die zu aktualisierenden Eigenschaften an.
  • PUT ersetzt die gesamte Entität.

Der Nachteil von PUT ist, dass der Client Werte für alle Eigenschaften in der Entität senden muss, einschließlich der Werte, die sich nicht ändern. Die OData-Spezifikation gibt an, dass PATCH bevorzugt ist.

In jedem Fall ist hier der Code für die PATCH- und PUT-Methoden:

public async Task<IHttpActionResult> Patch([FromODataUri] int key, Delta<Product> product)
{
    if (!ModelState.IsValid)
    {
        return BadRequest(ModelState);
    }
    var entity = await db.Products.FindAsync(key);
    if (entity == null)
    {
        return NotFound();
    }
    product.Patch(entity);
    try
    {
        await db.SaveChangesAsync();
    }
    catch (DbUpdateConcurrencyException)
    {
        if (!ProductExists(key))
        {
            return NotFound();
        }
        else
        {
            throw;
        }
    }
    return Updated(entity);
}
public async Task<IHttpActionResult> Put([FromODataUri] int key, Product update)
{
    if (!ModelState.IsValid)
    {
        return BadRequest(ModelState);
    }
    if (key != update.Id)
    {
        return BadRequest();
    }
    db.Entry(update).State = EntityState.Modified;
    try
    {
        await db.SaveChangesAsync();
    }
    catch (DbUpdateConcurrencyException)
    {
        if (!ProductExists(key))
        {
            return NotFound();
        }
        else
        {
            throw;
        }
    }
    return Updated(update);
}

Im Fall von PATCH verwendet der Controller den Delta<T-Typ> , um die Änderungen nachzuverfolgen.

Löschen einer Entität

Damit Clients ein Produkt aus der Datenbank löschen können, fügen Sie die folgende Methode hinzu ProductsController.

public async Task<IHttpActionResult> Delete([FromODataUri] int key)
{
    var product = await db.Products.FindAsync(key);
    if (product == null)
    {
        return NotFound();
    }
    db.Products.Remove(product);
    await db.SaveChangesAsync();
    return StatusCode(HttpStatusCode.NoContent);
}