Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
di Mike Wasson
Panoramica ed esempi di codice per le opzioni di $expand, $select e $value nell'API Web OData 2 per ASP.NET 4.x. Queste opzioni consentono a un client di controllare la rappresentazione restituita dal server.
- $expand fa sì che le entità correlate vengano incluse inline nella risposta.
- $select seleziona un subset di proprietà da includere nella risposta.
- $value ottiene il valore non elaborato di una proprietà.
Schema di esempio
Per questo articolo si userà un servizio OData che definisce tre entità: Product, Supplier e Category. Ogni prodotto ha una categoria e un fornitore.
Ecco le classi C# che definiscono i modelli di entità:
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; }
}
Si noti che la Product classe definisce le proprietà di navigazione per Supplier e Category. La Category classe definisce una proprietà di navigazione per i prodotti in ogni categoria.
Per creare un endpoint OData per questo schema, usare lo scaffolding di Visual Studio 2013, come descritto in Creazione di un endpoint OData in ASP.NET API Web. Aggiungere controller separati per Product, Category e Supplier.
Abilitazione di $expand e $select
In Visual Studio 2013 lo scaffolding OData dell'API Web crea un controller che supporta automaticamente $expand e $select. Per riferimento, ecco i requisiti per supportare $expand e $select in un controller.
Per le raccolte, il metodo del Get controller deve restituire un oggetto IQueryable.
[Queryable]
public IQueryable<Category> GetCategories()
{
return db.Categories;
}
Per le singole entità, restituire un valore SingleResult<T>, dove T è un IQueryable che contiene zero o una delle entità.
[Queryable]
public SingleResult<Category> GetCategory([FromODataUri] int key)
{
return SingleResult.Create(db.Categories.Where(c => c.ID == key));
}
Inoltre, decorare i Get metodi con l'attributo [Queryable] , come illustrato nei frammenti di codice precedenti. In alternativa, chiamare EnableQuerySupport sull'oggetto HttpConfiguration all'avvio. Per altre informazioni, vedere Abilitazione delle opzioni di query OData.
Uso di $expand
Quando si esegue una query su un'entità o una raccolta OData, la risposta predefinita non include entità correlate. Ecco ad esempio la risposta predefinita per il set di entità Categorie:
{
"odata.metadata":"http://localhost/odata/$metadata#Categories",
"value":[
{"ID":1,"Name":"Apparel"},
{"ID":2,"Name":"Toys"}
]
}
Come si può notare, la risposta non include alcun prodotto, anche se l'entità Categoria ha un link di navigazione Prodotti. Tuttavia, il client può usare $expand per ottenere l'elenco dei prodotti per ogni categoria. L'opzione $expand viene inserita nella stringa di query della richiesta:
GET http://localhost/odata/Categories?$expand=Products
Il server includerà ora i prodotti per ogni categoria, in linea con le categorie. Ecco il payload della risposta:
{
"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"
}
]
}
Si noti che ogni voce nella matrice "value" contiene un elenco Prodotti.
L'opzione $expand accetta un elenco delimitato da virgole delle proprietà di navigazione da espandere. La richiesta seguente espande sia la categoria che il fornitore per un prodotto.
GET http://localhost/odata/Products(1)?$expand=Category,Supplier
Ecco il corpo della risposta:
{
"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"
}
È possibile espandere più di un livello di proprietà di navigazione. L'esempio seguente include tutti i prodotti per una categoria e anche il fornitore per ogni prodotto.
GET http://localhost/odata/Categories(1)?$expand=Products/Supplier
Ecco il corpo della risposta:
{
"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"
}
Per impostazione predefinita, l'API Web limita la profondità massima di espansione a 2. Ciò impedisce al client di inviare richieste complesse come $expand=Orders/OrderDetails/Product/Supplier/Region, che potrebbero risultare inefficienti per eseguire query e creare risposte di grandi dimensioni. Per eseguire l'override dell'impostazione predefinita, impostare la proprietà MaxExpansionDepth sull'attributo [Queryable].
[Queryable(MaxExpansionDepth=4)]
public IQueryable<Category> GetCategories()
{
return db.Categories;
}
Per altre informazioni sull'opzione $expand, vedere Espandere l'opzione di query di sistema ($expand) nella documentazione ufficiale di OData.
Uso di $select
L'opzione $select specifica un subset di proprietà da includere nel corpo della risposta. Ad esempio, per ottenere solo il nome e il prezzo di ogni prodotto, usare la query seguente:
GET http://localhost/odata/Products?$select=Price,Name
Ecco il corpo della risposta:
{
"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"}
]
}
È possibile combinare $select e $expand nella stessa query. Assicurarsi di includere la proprietà espansa nell'opzione $select. Ad esempio, la richiesta seguente ottiene il nome del prodotto e il fornitore.
GET http://localhost/odata/Products?$select=Name,Supplier&$expand=Supplier
Ecco il corpo della risposta:
{
"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"
}
]
}
È anche possibile selezionare le proprietà all'interno di una proprietà espansa. La richiesta seguente espande Prodotti e seleziona il nome della categoria più il nome del prodotto.
GET http://localhost/odata/Categories?$expand=Products&$select=Name,Products/Name
Ecco il corpo della risposta:
{
"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"
}
]
}
Per altre informazioni sull'opzione $select, vedere Selezionare l'opzione query di sistema ($select) nella documentazione ufficiale di OData.
Recuperare le singole proprietà di un'entità ($value)
Esistono due modi per un client OData per ottenere una singola proprietà da un'entità. Il client può ottenere il valore in formato OData o ottenere il valore non elaborato della proprietà.
La richiesta seguente ottiene una proprietà in formato OData.
GET http://localhost/odata/Products(1)/Name
Ecco una risposta di esempio in formato JSON:
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"
}
Per ottenere il valore non elaborato della proprietà, aggiungere $value all'URI:
GET http://localhost/odata/Products(1)/Name/$value
Ecco la risposta. Si noti che il tipo di contenuto è "text/plain", non JSON.
HTTP/1.1 200 OK
Content-Type: text/plain; charset=utf-8
DataServiceVersion: 3.0
Content-Length: 3
Hat
Per supportare queste query nel controller OData, aggiungere un metodo denominato GetProperty, dove Property è il nome della proprietà. Ad esempio, il metodo per ottenere la proprietà Name sarà denominato GetName. Il metodo deve restituire il valore di tale proprietà:
public async Task<IHttpActionResult> GetName(int key)
{
Product product = await db.Products.FindAsync(key);
if (product == null)
{
return NotFound();
}
return Ok(product.Name);
}