Criar um ponto de extremidade OData v4 usando ASP.NET Web API
O OData (Open Data Protocol) é um protocolo de acesso a dados para a Web. O OData fornece uma maneira uniforme de consultar e manipular conjuntos de dados por meio de operações CRUD (criar, ler, atualizar e excluir).
ASP.NET Web API dá suporte a v3 e v4 do protocolo. Você pode até mesmo ter um ponto de extremidade v4 que é executado lado a lado com um ponto de extremidade v3.
Este tutorial mostra como criar um ponto de extremidade OData v4 que dá suporte a operações CRUD.
Versões de software usadas no tutorial
- API Web 5.2
- OData v4
- Visual Studio 2017 (baixe o Visual Studio 2017 aqui)
- Entity Framework 6
- .NET 4.7.2
Versões do tutorial
Para a versão 3 do OData, consulte Criando um ponto de extremidade OData v3.
Criar o projeto do Visual Studio
No Visual Studio, no menu Arquivo , selecione Novo>Projeto.
ExpandaVisual C#>Webinstalado> e selecione o modelo ASP.NET Aplicativo Web (.NET Framework). Nomeie o projeto como "ProductService".
Selecione OK.
Selecione o modelo Vazio. Em Adicionar pastas e referências principais para:, selecione API Web. Selecione OK.
Instalar os pacotes OData
No menu Ferramentas selecione Gerenciador de Pacotes NuGet>Console do Gerenciador de Pacotes. Na janela Console do Gerenciador de Pacotes, digite:
Install-Package Microsoft.AspNet.Odata
Esse comando instala os pacotes NuGet OData mais recentes.
Adicionar uma classe de modelo
Um modelo é um objeto que representa uma entidade de dados em seu aplicativo.
No Gerenciador de Soluções, clique com o botão direito do mouse na pasta Modelos. No menu de contexto, selecione Adicionar>Classe.
Observação
Por convenção, as classes de modelo são colocadas na pasta Modelos, mas você não precisa seguir essa convenção em seus próprios projetos.
Nome da classe Product
. No arquivo Product.cs, substitua o código clichê pelo seguinte:
namespace ProductService.Models
{
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
public string Category { get; set; }
}
}
A Id
propriedade é a chave de entidade. Os clientes podem consultar entidades por chave. Por exemplo, para obter o produto com a ID de 5, o URI é /Products(5)
. A Id
propriedade também será a chave primária no banco de dados de back-end.
Habilitar o Entity Framework
Para este tutorial, usaremos o EF (Entity Framework) Code First para criar o banco de dados de back-end.
Observação
O OData da API Web não requer EF. Use qualquer camada de acesso a dados que possa converter entidades de banco de dados em modelos.
Primeiro, instale o pacote NuGet para EF. No menu Ferramentas selecione Gerenciador de Pacotes NuGet>Console do Gerenciador de Pacotes. Na janela Console do Gerenciador de Pacotes, digite:
Install-Package EntityFramework
Abra o arquivo Web.config e adicione a seção a seguir dentro do elemento de configuração , após o elemento configSections .
<configuration>
<configSections>
<!-- ... -->
</configSections>
<!-- Add this: -->
<connectionStrings>
<add name="ProductsContext" connectionString="Data Source=(localdb)\mssqllocaldb;
Initial Catalog=ProductsContext; Integrated Security=True; MultipleActiveResultSets=True;
AttachDbFilename=|DataDirectory|ProductsContext.mdf"
providerName="System.Data.SqlClient" />
</connectionStrings>
Essa configuração adiciona uma cadeia de conexão para um banco de dados LocalDB. Esse banco de dados será usado quando você executar o aplicativo localmente.
Em seguida, adicione uma classe chamada ProductsContext
à pasta Models:
using System.Data.Entity;
namespace ProductService.Models
{
public class ProductsContext : DbContext
{
public ProductsContext()
: base("name=ProductsContext")
{
}
public DbSet<Product> Products { get; set; }
}
}
No construtor, "name=ProductsContext"
fornece o nome da cadeia de conexão.
Configurar o ponto de extremidade OData
Abra o arquivo App_Start/WebApiConfig.cs. Adicione as seguintes instruções using :
using ProductService.Models;
using Microsoft.AspNet.OData.Builder;
using Microsoft.AspNet.OData.Extensions;
Em seguida, adicione o seguinte código ao método Register :
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// New code:
ODataModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<Product>("Products");
config.MapODataServiceRoute(
routeName: "ODataRoute",
routePrefix: null,
model: builder.GetEdmModel());
}
}
Esse código faz duas coisas:
- Cria um EDM (Modelo de Dados de Entidade).
- Adiciona uma rota.
Um EDM é um modelo abstrato dos dados. O EDM é usado para criar o documento de metadados de serviço. A classe ODataConventionModelBuilder cria um EDM usando convenções de nomenclatura padrão. Essa abordagem requer o menor código. Se você quiser mais controle sobre o EDM, poderá usar a classe ODataModelBuilder para criar o EDM adicionando propriedades, chaves e propriedades de navegação explicitamente.
Uma rota informa à API Web como rotear solicitações HTTP para o ponto de extremidade. Para criar uma rota OData v4, chame o método de extensão MapODataServiceRoute .
Se o aplicativo tiver vários pontos de extremidade OData, crie uma rota separada para cada um. Dê a cada rota um prefixo e um nome de rota exclusivos.
Adicionar o controlador OData
Um controlador é uma classe que manipula solicitações HTTP. Você cria um controlador separado para cada conjunto de entidades em seu serviço OData. Neste tutorial, você criará um controlador para a Product
entidade.
Em Gerenciador de Soluções, clique com o botão direito do mouse na pasta Controladores e selecione Adicionar>Classe. Nome da classe ProductsController
.
Observação
A versão deste tutorial para OData v3 usa o scaffolding Adicionar Controlador . Atualmente, não há scaffolding para OData v4.
Substitua o código clichê em ProductsController.cs pelo seguinte.
using ProductService.Models;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using System.Web.Http;
using System.Web.OData;
namespace ProductService.Controllers
{
public class ProductsController : ODataController
{
ProductsContext db = new ProductsContext();
private bool ProductExists(int key)
{
return db.Products.Any(p => p.Id == key);
}
protected override void Dispose(bool disposing)
{
db.Dispose();
base.Dispose(disposing);
}
}
}
O controlador usa a ProductsContext
classe para acessar o banco de dados usando o EF. Observe que o controlador substitui o método Dispose para descartar o ProductsContext.
Esse é o ponto de partida para o controlador. Em seguida, adicionaremos métodos para todas as operações CRUD.
Consultar o conjunto de entidades
Adicione os métodos a seguir a ProductsController
.
[EnableQuery]
public IQueryable<Product> Get()
{
return db.Products;
}
[EnableQuery]
public SingleResult<Product> Get([FromODataUri] int key)
{
IQueryable<Product> result = db.Products.Where(p => p.Id == key);
return SingleResult.Create(result);
}
A versão sem parâmetros do Get
método retorna toda a coleção Products. O Get
método com um parâmetro de chave pesquisa um produto por sua chave (nesse caso, a Id
propriedade ).
O atributo [EnableQuery] permite que os clientes modifiquem a consulta usando opções de consulta como $filter, $sort e $page. Para obter mais informações, consulte Suporte a opções de consulta OData.
Adicionar uma entidade ao conjunto de entidades
Para permitir que os clientes adicionem um novo produto ao banco de dados, adicione o método a seguir a ProductsController
.
public async Task<IHttpActionResult> Post(Product product)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
db.Products.Add(product);
await db.SaveChangesAsync();
return Created(product);
}
Atualizar uma entidade
O OData dá suporte a duas semânticas diferentes para atualizar uma entidade, PATCH e PUT.
- PATCH executa uma atualização parcial. O cliente especifica apenas as propriedades a serem atualizadas.
- PUT substitui toda a entidade.
A desvantagem de PUT é que o cliente deve enviar valores para todas as propriedades na entidade, incluindo valores que não estão sendo alterados. A especificação OData afirma que PATCH é preferencial.
De qualquer forma, aqui está o código para os métodos PATCH e PUT:
public async Task<IHttpActionResult> Patch([FromODataUri] int key, Delta<Product> product)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var entity = await db.Products.FindAsync(key);
if (entity == null)
{
return NotFound();
}
product.Patch(entity);
try
{
await db.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!ProductExists(key))
{
return NotFound();
}
else
{
throw;
}
}
return Updated(entity);
}
public async Task<IHttpActionResult> Put([FromODataUri] int key, Product update)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
if (key != update.Id)
{
return BadRequest();
}
db.Entry(update).State = EntityState.Modified;
try
{
await db.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!ProductExists(key))
{
return NotFound();
}
else
{
throw;
}
}
return Updated(update);
}
No caso de PATCH, o controlador usa o tipo Delta<T> para acompanhar as alterações.
Excluir uma entidade
Para permitir que os clientes excluam um produto do banco de dados, adicione o seguinte método a ProductsController
.
public async Task<IHttpActionResult> Delete([FromODataUri] int key)
{
var product = await db.Products.FindAsync(key);
if (product == null)
{
return NotFound();
}
db.Products.Remove(product);
await db.SaveChangesAsync();
return StatusCode(HttpStatusCode.NoContent);
}