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 können. In diesem Tutorial erfahren Sie, wie Sie mithilfe der Web-API 2.2 Aktionen und Funktionen zu einem OData v4-Endpunkt hinzufügen. Das Tutorial baut auf dem Tutorial Erstellen eines OData v4-Endpunkts mit ASP.NET-Web-API 2 auf.
Im Tutorial verwendete Softwareversionen
- Web-API 2.2
- OData v4
- Visual Studio 2013 (Laden Sie Visual Studio 2017 hier herunter)
- .NET 4.5
Tutorialversionen
Informationen zu OData Version 3 finden Sie unter OData Actions 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 sind:
- Komplexe Transaktionen.
- Bearbeiten mehrerer Entitäten gleichzeitig.
- Zulassen von Updates nur für bestimmte Eigenschaften einer Entität.
- Senden von Daten, die keine Entität sind.
Funktionen sind nützlich, um Informationen zurückzugeben, die nicht direkt einer Entität oder Sammlung 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 aufgerufen werden.
Beispiel: Hinzufügen einer Aktion
Definieren wir eine Aktion zum Bewerten eines Produkts.
Hinweis
Dieses Tutorial baut auf dem Tutorial Erstellen eines OData v4-Endpunkts mit ASP.NET-Web-API 2 auf.
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 ProductsContext
Klasse außerdem ein DbSet hinzu, damit EF eine Ratings-Tabelle 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 typisierten Parameter für die Aktion an.
Dieser Code legt auch den Namespace für den EDM fest. Der Namespace ist wichtig, da der URI 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 Web.Config-Datei 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 zu 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 mit dem Aktionsnamen übereinstimmt. Das [HttpPost]- Attribut gibt an, dass die Methode eine HTTP POST-Methode ist.
Um die Aktion aufzurufen, sendet der Client eine HTTP POST-Anforderung wie die folgende:
POST http://localhost/Products(1)/ProductService.Rate HTTP/1.1
Content-Type: application/json
Content-Length: 12
{"Rating":5}
Die Aktion "Rate" ist an Product-Instanzen gebunden, sodass der URI für die Aktion der vollqualifizierte Aktionsname ist, der an den Entitäts-URI angefügt wird. (Denken Sie daran, dass wir den EDM-Namespace auf "ProductService" festlegen, sodass der vollqualifizierte Aktionsname "ProductService.Rate" lautet.)
Der Text der Anforderung enthält die Aktionsparameter als JSON-Nutzlast. Die Web-API konvertiert die JSON-Nutzlast automatisch in ein ODataActionParameters-Objekt , bei dem es sich nur um ein Wörterbuch mit Parameterwerten handelt. 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 false. Überprüfen Sie dieses Flag in Ihrer Controllermethode, und geben Sie einen Fehler zurück, wenn IsValid auf "false" festgelegt 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 besteht der erste Schritt darin, die Funktion dem EDM hinzuzufügen. 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 Product-Instanzen gebunden. Clients rufen die Funktion auf, indem sie eine GET-Anforderung senden:
GET http://localhost:38479/Products/ProductService.MostExpensive
Hier 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 mit dem Funktionsnamen übereinstimmt. Das [HttpGet]- Attribut 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. In diesem nächsten Beispiel erstellen wir eine ungebundene Funktion. Ungebundene Funktionen werden als statische Vorgänge für den 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 zum 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 aufrufen, anstelle des Entitätstyps oder der Auflistung. Dies teilt dem Modell-Generator mit, dass die Funktion nicht gebunden ist.
Hier sehen Sie 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 ihn in ProductsController
einfügen oder einen separaten Controller definieren. Das [ODataRoute] -Attribut definiert die URI-Vorlage für die Funktion.
Hier sehen Sie ein Beispiel für eine Clientanforderung:
GET http://localhost:38479/GetSalesTaxRate(PostalCode=10) HTTP/1.1
Die HTTP-Antwort sieht wie folgt aus:
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
}