Verwenden von $select, $expand und $value in ASP.NET-Web-API 2 OData
von Mike Wasson
Übersicht und Codebeispiele für die Optionen $expand, $select und $value in OData Web API 2 für ASP.NET 4.x. Diese Optionen ermöglichen es einem Client, die Darstellung zu steuern, die er vom Server zurück erhält.
- $expand bewirkt, dass verwandte Entitäten inline in die Antwort eingeschlossen werden.
- $select wählt eine Teilmenge der Eigenschaften aus, die in die Antwort eingeschlossen werden sollen.
- $value ruft den Rohwert einer Eigenschaft ab.
Beispielschema
Für diesen Artikel verwende ich einen OData-Dienst, der drei Entitäten definiert: Product, Supplier und Category. Jedes Produkt hat eine Kategorie und einen Lieferanten.
Hier sind die C#-Klassen, die die Entitätsmodelle definieren:
public class Supplier
{
[Key]
public string Key {get; set; }
public string Name { get; set; }
}
public class Category
{
public int ID { get; set; }
public string Name { get; set; }
public virtual ICollection<Product> Products { get; set; }
}
public class Product
{
public int ID { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
[ForeignKey("Category")]
public int CategoryId { get; set; }
public Category Category { get; set; }
[ForeignKey("Supplier")]
public string SupplierId { get; set; }
public virtual Supplier Supplier { get; set; }
}
Beachten Sie, dass die Product
-Klasse Navigationseigenschaften für und Supplier
Category
definiert. Die Category
-Klasse definiert eine Navigationseigenschaft für die Produkte in jeder Kategorie.
Verwenden Sie zum Erstellen eines OData-Endpunkts für dieses Schema das Visual Studio 2013 Gerüstbau, wie unter Erstellen eines OData-Endpunkts in ASP.NET-Web-API beschrieben. Fügen Sie separate Controller für Product, Category und Supplier hinzu.
Aktivieren von $expand und $select
In Visual Studio 2013 erstellt das OData-Gerüstbau der Web-API einen Controller, der automatisch $expand und $select unterstützt. Hier finden Sie die Anforderungen zur Unterstützung $expand und $select in einem Controller.
Für Sammlungen muss die -Methode des Get
Controllers ein IQueryable zurückgeben.
[Queryable]
public IQueryable<Category> GetCategories()
{
return db.Categories;
}
Geben Sie für einzelne Entitäten ein SingleResult<T> zurück, wobei T ein IQueryable-Objekt ist, das null oder eine Entität enthält.
[Queryable]
public SingleResult<Category> GetCategory([FromODataUri] int key)
{
return SingleResult.Create(db.Categories.Where(c => c.ID == key));
}
Dekorieren Sie Ihre Get
Methoden außerdem mit dem [Queryable] -Attribut, wie in den vorherigen Codeausschnitten gezeigt. Rufen Sie alternativ enableQuerySupport für das HttpConfiguration-Objekt beim Start auf. (Weitere Informationen finden Sie unter Aktivieren von OData-Abfrageoptionen.)
Verwenden von $expand
Wenn Sie eine OData-Entität oder -Auflistung abfragen, enthält die Standardantwort keine zugehörigen Entitäten. Dies ist beispielsweise die Standardantwort für den Entitätssatz Categories:
{
"odata.metadata":"http://localhost/odata/$metadata#Categories",
"value":[
{"ID":1,"Name":"Apparel"},
{"ID":2,"Name":"Toys"}
]
}
Wie Sie sehen, enthält die Antwort keine Produkte, auch wenn die Entität Kategorie über einen Navigationslink Für Produkte verfügt. Der Client kann jedoch $expand verwenden, um die Liste der Produkte für jede Kategorie abzurufen. Die Option $expand wird in der Abfragezeichenfolge der Anforderung verwendet:
GET http://localhost/odata/Categories?$expand=Products
Nun schließt der Server die Produkte für jede Kategorie inline mit den Kategorien ein. Hier sehen Sie die Antwortnutzlast:
{
"odata.metadata":"http://localhost/odata/$metadata#Categories",
"value":[
{
"Products":[
{"ID":1,"Name":"Hat","Price":"15.00","CategoryId":1,"SupplierId":"CTSO"},
{"ID":2,"Name":"Scarf","Price":"12.00","CategoryId":1,"SupplierId":"CTSO"},
{"ID":3,"Name":"Socks","Price":"5.00","CategoryId":1,"SupplierId":"FBRK"}
],
"ID":1,
"Name":"Apparel"
},
{
"Products":[
{"ID":4,"Name":"Yo-yo","Price":"4.95","CategoryId":2,"SupplierId":"WING"},
{"ID":5,"Name":"Puzzle","Price":"8.00","CategoryId":2,"SupplierId":"WING"}
],
"ID":2,
"Name":"Toys"
}
]
}
Beachten Sie, dass jeder Eintrag im Array "Value" eine Products-Liste enthält.
Die Option $expand verwendet eine durch Trennzeichen getrennte Liste von Navigationseigenschaften, um sie zu erweitern. Die folgende Anforderung erweitert sowohl die Kategorie als auch den Lieferanten für ein Produkt.
GET http://localhost/odata/Products(1)?$expand=Category,Supplier
Hier ist der Antworttext:
{
"odata.metadata":"http://localhost/odata/$metadata#Products/@Element",
"Category": {"ID":1,"Name":"Apparel"},
"Supplier":{"Key":"CTSO","Name":"Contoso, Ltd."},
"ID":1,
"Name":"Hat",
"Price":"15.00",
"CategoryId":1,
"SupplierId":"CTSO"
}
Sie können mehrere Ebenen der Navigationseigenschaft erweitern. Das folgende Beispiel enthält alle Produkte für eine Kategorie und auch den Lieferanten für jedes Produkt.
GET http://localhost/odata/Categories(1)?$expand=Products/Supplier
Hier ist der Antworttext:
{
"odata.metadata":"http://localhost/odata/$metadata#Categories/@Element",
"Products":[
{
"Supplier":{"Key":"CTSO","Name":"Contoso, Ltd."},
"ID":1,"Name":"Hat","Price":"15.00","CategoryId":1,"SupplierId":"CTSO"
},
{
"Supplier":{"Key":"CTSO","Name":"Contoso, Ltd."},
"ID":2,"Name":"Scarf","Price":"12.00","CategoryId":1,"SupplierId":"CTSO"
},{
"Supplier":{
"Key":"FBRK","Name":"Fabrikam, Inc."
},"ID":3,"Name":"Socks","Price":"5.00","CategoryId":1,"SupplierId":"FBRK"
}
],"ID":1,"Name":"Apparel"
}
Standardmäßig beschränkt die Web-API die maximale Erweiterungstiefe auf 2. Dadurch wird verhindert, dass der Client komplexe Anforderungen wie $expand=Orders/OrderDetails/Product/Supplier/Region
sendet, die möglicherweise ineffizient sind, um große Antworten abzufragen und zu erstellen. Um den Standardwert zu überschreiben, legen Sie die MaxExpansionDepth-Eigenschaft für das [Queryable]- Attribut fest.
[Queryable(MaxExpansionDepth=4)]
public IQueryable<Category> GetCategories()
{
return db.Categories;
}
Weitere Informationen zur option $expand finden Sie unter Erweitern der Systemabfrageoption ($expand) in der offiziellen OData-Dokumentation.
Verwenden von $select
Die Option $select gibt eine Teilmenge der Eigenschaften an, die in den Antworttext eingeschlossen werden sollen. Verwenden Sie beispielsweise die folgende Abfrage, um nur den Namen und den Preis der einzelnen Produkte abzurufen:
GET http://localhost/odata/Products?$select=Price,Name
Hier ist der Antworttext:
{
"odata.metadata":"http://localhost/odata/$metadata#Products&$select=Price,Name",
"value":[
{"Price":"15.00","Name":"Hat"},
{"Price":"12.00","Name":"Scarf"},
{"Price":"5.00","Name":"Socks"},
{"Price":"4.95","Name":"Yo-yo"},
{"Price":"8.00","Name":"Puzzle"}
]
}
Sie können $select und $expand in derselben Abfrage kombinieren. Stellen Sie sicher, dass Sie die erweiterte Eigenschaft in die Option $select einschließen. Die folgende Anforderung ruft beispielsweise den Produktnamen und den Lieferanten ab.
GET http://localhost/odata/Products?$select=Name,Supplier&$expand=Supplier
Hier ist der Antworttext:
{
"odata.metadata":"http://localhost/odata/$metadata#Products&$select=Name,Supplier",
"value":[
{
"Supplier":{"Key":"CTSO","Name":"Contoso, Ltd."},
"Name":"Hat"
},
{
"Supplier":{"Key":"CTSO","Name":"Contoso, Ltd."},
"Name":"Scarf"
},
{
"Supplier":{"Key":"FBRK","Name":"Fabrikam, Inc."},
"Name":"Socks"
},
{
"Supplier":{"Key":"WING","Name":"Wingtip Toys"},
"Name":"Yo-yo"
},
{
"Supplier":{"Key":"WING","Name":"Wingtip Toys"},
"Name":"Puzzle"
}
]
}
Sie können auch die Eigenschaften innerhalb einer erweiterten Eigenschaft auswählen. Die folgende Anforderung erweitert Produkte und wählt Kategoriename plus Produktname aus.
GET http://localhost/odata/Categories?$expand=Products&$select=Name,Products/Name
Hier ist der Antworttext:
{
"odata.metadata":"http://localhost/odata/$metadata#Categories&$select=Name,Products/Name",
"value":[
{
"Products":[ {"Name":"Hat"},{"Name":"Scarf"},{"Name":"Socks"} ],
"Name":"Apparel"
},
{
"Products":[ {"Name":"Yo-yo"},{"Name":"Puzzle"} ],
"Name":"Toys"
}
]
}
Weitere Informationen zur option $select finden Sie unter Auswählen der Systemabfrageoption ($select) in der offiziellen OData-Dokumentation.
Abrufen einzelner Eigenschaften einer Entität ($value)
Es gibt zwei Möglichkeiten für einen OData-Client, eine einzelne Eigenschaft von einer Entität abzurufen. Der Client kann entweder den Wert im OData-Format abrufen oder den Rohwert der Eigenschaft abrufen.
Die folgende Anforderung ruft eine Eigenschaft im OData-Format ab.
GET http://localhost/odata/Products(1)/Name
Hier sehen Sie eine Beispielantwort im JSON-Format:
HTTP/1.1 200 OK
Content-Type: application/json; odata=minimalmetadata; streaming=true; charset=utf-8
DataServiceVersion: 3.0
Content-Length: 90
{
"odata.metadata":"http://localhost:14239/odata/$metadata#Edm.String",
"value":"Hat"
}
Um den Rohwert der Eigenschaft abzurufen, fügen Sie $value an den URI an:
GET http://localhost/odata/Products(1)/Name/$value
Hier ist die Antwort. Beachten Sie, dass der Inhaltstyp "text/plain" und nicht JSON ist.
HTTP/1.1 200 OK
Content-Type: text/plain; charset=utf-8
DataServiceVersion: 3.0
Content-Length: 3
Hat
Um diese Abfragen in Ihrem OData-Controller zu unterstützen, fügen Sie eine Methode mit dem Namen hinzu GetProperty
, wobei Property
der Name der Eigenschaft ist. Die Methode zum Abrufen der Name-Eigenschaft lautet GetName
beispielsweise . Die -Methode sollte den Wert dieser Eigenschaft zurückgeben:
public async Task<IHttpActionResult> GetName(int key)
{
Product product = await db.Products.FindAsync(key);
if (product == null)
{
return NotFound();
}
return Ok(product.Name);
}