Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
por Mike Wasson
Visão geral e exemplos de código para as opções de $expand, $select e $value na API Web 2 do OData para ASP.NET 4.x. Estas opções permitem que um cliente controle a representação que recebe do servidor.
- $expand faz com que entidades relacionadas sejam incluídas em linha na resposta.
- $select seleciona um subconjunto de propriedades a incluir na resposta.
- $value recebe o valor bruto de uma propriedade.
Esquema de Exemplo
Para este artigo, vou usar um serviço OData que define três entidades: Produto, Fornecedor e Categoria. Cada produto tem uma categoria e um fornecedor.
Aqui estão as classes C# que definem os modelos de entidades:
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; }
}
Note que a Product classe define propriedades de navegação para e SupplierCategory. A Category classe define uma propriedade de navegação para os produtos de cada categoria.
Para criar um endpoint OData para este esquema, utilize a estrutura de andaimes do Visual Studio 2013, conforme descrito em Criar um Endpoint OData na ASP.NET Web API. Adicione controladores separados para Produto, Categoria e Fornecedor.
Permitir $expand e $select
No Visual Studio 2013, a estrutura de scaffolding da API Web OData cria um controlador que suporta automaticamente $expand e $select. Para referência, aqui estão os requisitos para suportar $expand e $select num controlador.
Para coleções, o método do Get controlador deve devolver um IQueryable.
[Queryable]
public IQueryable<Category> GetCategories()
{
return db.Categories;
}
Para entidades únicas, retorna-se um SingleResult<T>, sendo T um IQueryable que contém zero ou uma entidade.
[Queryable]
public SingleResult<Category> GetCategory([FromODataUri] int key)
{
return SingleResult.Create(db.Categories.Where(c => c.ID == key));
}
Além disso, decore os seus Get métodos com o atributo [Queryable ], como mostrado nos excertos de código anteriores. Alternativamente, chame EnableQuerySupport no objeto HttpConfiguration durante o arranque. (Para mais informações, veja Ativar Opções de Consulta OData.)
Usar $expand
Quando consulta uma entidade ou coleção OData, a resposta padrão não inclui entidades relacionadas. Por exemplo, aqui está a resposta padrão para o conjunto de entidades Categorias:
{
"odata.metadata":"http://localhost/odata/$metadata#Categories",
"value":[
{"ID":1,"Name":"Apparel"},
{"ID":2,"Name":"Toys"}
]
}
Como pode ver, a resposta não inclui nenhum produto, embora a entidade de Categoria tenha um link de navegação de Produtos. No entanto, o cliente pode usar $expand para obter a lista de produtos para cada categoria. A opção $expand vai para a string de consulta do pedido.
GET http://localhost/odata/Categories?$expand=Products
Agora o servidor incluirá os produtos de cada categoria, alinhados com as categorias. Aqui está o conteúdo da resposta:
{
"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"
}
]
}
Note que cada entrada no array "valor" contém uma lista de Produtos.
A opção $expand utiliza uma lista separada por vírgulas de propriedades de navegação para expandir. O pedido seguinte expande tanto a categoria como o fornecedor de um produto.
GET http://localhost/odata/Products(1)?$expand=Category,Supplier
Aqui está o corpo da resposta:
{
"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"
}
Pode expandir mais do que um nível de propriedade de navegação. O exemplo seguinte inclui todos os produtos de uma categoria e também o fornecedor de cada produto.
GET http://localhost/odata/Categories(1)?$expand=Products/Supplier
Aqui está o corpo da resposta:
{
"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"
}
Por defeito, a Web API limita a profundidade máxima de expansão a 2. Isso impede que o cliente envie pedidos complexos como $expand=Orders/OrderDetails/Product/Supplier/Region, que podem ser ineficientes para consultar e criar respostas volumosas. Para sobrescrever o padrão, defina a propriedade MaxExpansionDepth no atributo [Queryable].
[Queryable(MaxExpansionDepth=4)]
public IQueryable<Category> GetCategories()
{
return db.Categories;
}
Para mais informações sobre a opção $expand, consulte Expandir a Opção de Consulta do Sistema ($expand) na documentação oficial do OData.
Usar $select
A opção $select especifica um subconjunto de propriedades a incluir no corpo da resposta. Por exemplo, para obter apenas o nome e o preço de cada produto, use a seguinte consulta:
GET http://localhost/odata/Products?$select=Price,Name
Aqui está o corpo da resposta:
{
"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"}
]
}
Podes combinar $select e $expand na mesma consulta. Certifique-se de incluir a propriedade ampliada na opção $select. Por exemplo, o pedido seguinte recebe o nome do produto e o fornecedor.
GET http://localhost/odata/Products?$select=Name,Supplier&$expand=Supplier
Aqui está o corpo da resposta:
{
"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"
}
]
}
Também pode selecionar as propriedades dentro de uma propriedade expandida. O pedido seguinte expande os Produtos e seleciona nome da categoria mais nome do produto.
GET http://localhost/odata/Categories?$expand=Products&$select=Name,Products/Name
Aqui está o corpo da resposta:
{
"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"
}
]
}
Para mais informações sobre a opção $select, consulte Selecionar Opção de Consulta do Sistema ($select) na documentação oficial do OData.
Obter Propriedades Individuais de uma Entidade ($value)
Existem duas formas de um cliente OData obter uma propriedade individual de uma entidade. O cliente pode obter o valor em formato OData ou obter o valor bruto da propriedade.
O pedido seguinte obtém uma propriedade no formato OData.
GET http://localhost/odata/Products(1)/Name
Aqui está um exemplo de resposta em 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"
}
Para obter o valor bruto da propriedade, acrescente $value ao URI:
GET http://localhost/odata/Products(1)/Name/$value
Aqui está a resposta. Repara que o tipo de conteúdo é "texto/simples", não JSON.
HTTP/1.1 200 OK
Content-Type: text/plain; charset=utf-8
DataServiceVersion: 3.0
Content-Length: 3
Hat
Para suportar estas consultas no seu controlador OData, adicione um método chamado GetProperty, onde Property é o nome da propriedade. Por exemplo, o método para obter a propriedade Name seria chamado GetName. O método deve devolver o valor dessa propriedade:
public async Task<IHttpActionResult> GetName(int key)
{
Product product = await db.Products.FindAsync(key);
if (product == null)
{
return NotFound();
}
return Ok(product.Name);
}