Freigeben über


Aktionen und Funktionen in OData v4 mit ASP.NET Web-API 2.2

von Mike Wasson

In OData sind Aktionen und Funktionen eine Möglichkeit, serverseitige Verhaltensweisen hinzuzufügen, die nicht einfach als CRUD-Vorgänge für Entitäten definiert werden. In diesem Lernprogramm wird gezeigt, wie Sie einem OData v4-Endpunkt Mithilfe der Web-API 2.2 Aktionen und Funktionen hinzufügen. Das Lernprogramm basiert auf dem Lernprogramm "Erstellen eines OData v4-Endpunkts mit ASP.NET Web-API 2"

Im Lernprogramm verwendete Softwareversionen

  • Web-API 2.2
  • OData v4
  • Visual Studio 2013 (Laden Sie Visual Studio 2017 hier herunter)
  • .NET 4.5

Tutorial-Versionen

Informationen zu OData Version 3 finden Sie unter OData-Aktionen in ASP.NET Web-API 2.

Der Unterschied zwischen Aktionen und Funktionen besteht darin, dass Aktionen Nebenwirkungen haben können und Funktionen nicht. Sowohl Aktionen als auch Funktionen können Daten zurückgeben. Einige Verwendungsmöglichkeiten für Aktionen umfassen:

  • Komplexe Transaktionen.
  • Gleichzeitiges Manipulieren mehrerer Entitäten.
  • Aktualisierungen nur für bestimmte Eigenschaften einer Entität zulassen.
  • Senden von Daten, die keine Entität sind.

Funktionen sind nützlich, um Informationen zurückzugeben, die nicht direkt einer Entität oder Auflistung entsprechen.

Eine Aktion (oder Funktion) kann auf eine einzelne Entität oder eine Sammlung abzielen. In der OData-Terminologie ist dies die Bindung. Sie können auch über "ungebundene" Aktionen/Funktionen verfügen, die als statische Vorgänge für den Dienst bezeichnet werden.

Beispiel: Hinzufügen einer Aktion

Definieren wir eine Aktion zum Bewerten eines Produkts.

Hinweis

Dieses Lernprogramm basiert auf dem Lernprogramm Erstellen eines OData v4-Endpunkts mithilfe von ASP.NET Web-API 2

Fügen Sie zunächst ein ProductRating Modell hinzu, um die Bewertungen darzustellen.

namespace ProductService.Models
{
    public class ProductRating
    {
        public int ID { get; set; }
        public int Rating { get; set; }
        public int ProductID { get; set; }
        public virtual Product Product { get; set; }  
    }
}

Fügen Sie der Klasse außerdem ein DbSetProductsContext hinzu, sodass EF eine Bewertungstabelle in der Datenbank erstellt.

public class ProductsContext : DbContext
{
    public ProductsContext() 
            : base("name=ProductsContext")
    {
    }

    public DbSet<Product> Products { get; set; }
    public DbSet<Supplier> Suppliers { get; set; }
    // New code:
    public DbSet<ProductRating> Ratings { get; set; }
}

Hinzufügen der Aktion zum EDM

Fügen Sie in WebApiConfig.cs den folgenden Code hinzu:

ODataModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<Product>("Products");

// New code:
builder.Namespace = "ProductService";
builder.EntityType<Product>()
    .Action("Rate")
    .Parameter<int>("Rating");

Die EntityTypeConfiguration.Action-Methode fügt dem Entitätsdatenmodell (EDM) eine Aktion hinzu. Die Parameter-Methode gibt einen typierten Parameter für die Aktion an.

Mit diesem Code wird auch der Namespace für das EDM festgelegt. Der Namespace ist wichtig, da die URI-Adresse für die Aktion den vollqualifizierten Aktionsnamen enthält.

http://localhost/Products(1)/ProductService.Rate

Hinweis

In einer typischen IIS-Konfiguration führt der Punkt in dieser URL dazu, dass IIS Den Fehler 404 zurückgibt. Sie können dies beheben, indem Sie der Datei "Web.Config" den folgenden Abschnitt hinzufügen:

<system.webServer>
    <handlers>
      <clear/>
      <add name="ExtensionlessUrlHandler-Integrated-4.0" path="/*" 
          verb="*" type="System.Web.Handlers.TransferRequestHandler" 
          preCondition="integratedMode,runtimeVersionv4.0" />
    </handlers>
</system.webServer>

Hinzufügen einer Controllermethode für die Aktion

Um die Aktion "Rate" zu aktivieren, fügen Sie die folgende Methode hinzu:ProductsController

[HttpPost]
public async Task<IHttpActionResult> Rate([FromODataUri] int key, ODataActionParameters parameters)
{
    if (!ModelState.IsValid)
    {
        return BadRequest();
    }

    int rating = (int)parameters["Rating"];
    db.Ratings.Add(new ProductRating
    {
        ProductID = key,
        Rating = rating
    });

    try
    {
        await db.SaveChangesAsync();
    }
    catch (DbUpdateException e)
    {
        if (!ProductExists(key))
        {
            return NotFound();
        }
        else
        {
            throw;
        }
    }

    return StatusCode(HttpStatusCode.NoContent);
}

Beachten Sie, dass der Methodenname dem Aktionsnamen entspricht. Das Attribut [HttpPost] gibt an, dass es sich bei der Methode um eine HTTP POST-Methode handelt.

Um die Aktion aufzurufen, sendet der Client eine HTTP POST-Anforderung wie folgt:

POST http://localhost/Products(1)/ProductService.Rate HTTP/1.1
Content-Type: application/json
Content-Length: 12

{"Rating":5}

Die Aktion "Rate" ist an Produktinstanzen gebunden, sodass der URI für die Aktion der vollqualifizierte Aktionsname ist, der an den Entitäts-URI angefügt wird. (Erinnern Sie sich daran, dass wir den EDM-Namespace auf "ProductService" festlegen, sodass der vollqualifizierte Aktionsname "ProductService.Rate" lautet.)

Der Textkörper der Anforderung enthält die Aktionsparameter als JSON-Nutzlast. Die Web-API konvertiert die JSON-Nutzlast automatisch in ein ODataActionParameters -Objekt, das nur ein Wörterbuch mit Parameterwerten ist. Verwenden Sie dieses Wörterbuch, um auf die Parameter in Ihrer Controllermethode zuzugreifen.

Wenn der Client die Aktionsparameter im falschen Format sendet, ist der Wert von ModelState.IsValid falsch. Überprüfen Sie dieses Flag in Ihrer Controllermethode und geben Sie einen Fehler zurück, wenn IsValid falsch ist.

if (!ModelState.IsValid)
{
    return BadRequest();
}

Beispiel: Hinzufügen einer Funktion

Nun fügen wir eine OData-Funktion hinzu, die das teuerste Produkt zurückgibt. Wie zuvor wird im ersten Schritt die Funktion zum EDM hinzugefügt. Fügen Sie in WebApiConfig.cs den folgenden Code hinzu.

ODataModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<Product>("Products");
builder.EntitySet<Supplier>("Suppliers");

// New code:
builder.Namespace = "ProductService";
builder.EntityType<Product>().Collection
    .Function("MostExpensive")
    .Returns<double>();

In diesem Fall ist die Funktion an die Products-Auflistung und nicht an einzelne Produktinstanzen gebunden. Clients rufen die Funktion durch Senden einer GET-Anforderung auf:

GET http://localhost:38479/Products/ProductService.MostExpensive

Dies ist die Controllermethode für diese Funktion:

public class ProductsController : ODataController
{
    [HttpGet]
    public IHttpActionResult MostExpensive()
    {
        var product = db.Products.Max(x => x.Price);
        return Ok(product);
    }

    // Other controller methods not shown.
}

Beachten Sie, dass der Methodenname dem Funktionsnamen entspricht. Das Attribut [HttpGet] gibt an, dass die Methode eine HTTP GET-Methode ist.

Hier ist die HTTP-Antwort:

HTTP/1.1 200 OK
Content-Type: application/json; odata.metadata=minimal; odata.streaming=true
OData-Version: 4.0
Date: Sat, 28 Jun 2014 00:44:07 GMT
Content-Length: 85

{
  "@odata.context":"http://localhost:38479/$metadata#Edm.Decimal","value":50.00
}

Beispiel: Hinzufügen einer ungebundenen Funktion

Das vorherige Beispiel war eine Funktion, die an eine Auflistung gebunden ist. Im nächsten Beispiel erstellen wir eine ungebundene Funktion. Ungebundene Funktionen werden als statische Operationen auf dem Dienst aufgerufen. Die Funktion in diesem Beispiel gibt die Mehrwertsteuer für eine bestimmte Postleitzahl zurück.

Fügen Sie in der WebApiConfig-Datei die Funktion dem EDM hinzu:

ODataModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<Product>("Products");

// New code:
builder.Function("GetSalesTaxRate")
    .Returns<double>()
    .Parameter<int>("PostalCode");

Beachten Sie, dass wir Function direkt für den ODataModelBuilder anstelle des Entitätstyps oder der Auflistung aufrufen. Dadurch wird dem Modell-Generator mitgeteilt, dass die Funktion ungebunden ist.

Dies ist die Controllermethode, die die Funktion implementiert:

[HttpGet]
[ODataRoute("GetSalesTaxRate(PostalCode={postalCode})")]
public IHttpActionResult GetSalesTaxRate([FromODataUri] int postalCode)
{
    double rate = 5.6;  // Use a fake number for the sample.
    return Ok(rate);
}

Es spielt keine Rolle, in welchem Web-API-Controller Sie diese Methode platzieren. Sie können es in ProductsController einfügen, oder einen separaten Controller definieren. Das [ODataRoute] -Attribut definiert die URI-Vorlage für die Funktion.

Hier ist ein Beispiel für eine Clientanforderung:

GET http://localhost:38479/GetSalesTaxRate(PostalCode=10) HTTP/1.1

Die HTTP-Antwort:

HTTP/1.1 200 OK
Content-Type: application/json; odata.metadata=minimal; odata.streaming=true
OData-Version: 4.0
Date: Sat, 28 Jun 2014 01:05:32 GMT
Content-Length: 82

{
  "@odata.context":"http://localhost:38479/$metadata#Edm.Double","value":5.6
}