Partilhar via


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".

Captura de tela da janela do novo projeto do Visual Studio mostrando opções de menu para criar um aplicativo Web A SP dot NET com o dot NET Framework.

Selecione OK.

Captura de tela do Aplicativo Web A SP dot NET, mostrando os modelos disponíveis para criar o aplicativo com uma pasta web A P I e uma referência principal.

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.

Captura de tela da janela do gerenciador de soluções, realçando o caminho para adicionar um objeto de classe de modelo ao projeto.

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);
}