ASP .NET Core -  Criando uma Web API com Dapper - I


 Hoje vamos criar uma Web API ASP .NET Core usando o Dapper.

O Dapper é um mapeador de objetos simples para a plataforma .NET de alto desempenho, sendo considerado um micro-ORM.

Ele estende a interface IDbConnection, fornecendo métodos de extensão úteis para consultar seu banco de dados, e, foi criado pela equipe do Stackoverflow.

Destaques dos recursos do Dapper:

Podemos dizer que o foco principal do Dapper é o desempenho.

O Dapper a rigor não pode ser considerado um ORM se formos levar em conta alguns recursos que praticamente todos os ORMs full do mercado possuem.

Assim o Dapper :

Dessa forma podemos considerar o Dapper um micro ORM.

Como o Dapper funciona ?

O Dapper funciona com o objeto ADO.NET IDbConnection, o que significa que funcionará com qualquer sistema de banco de dados para o qual exista um provedor ADO.NET.

Para usar o Dapper basicamente fazemos o seguinte:

Nesta perspectiva, as funcionalidades do Dapper podem ser resumidas a  : Consultar ,  Parametrizar a consulta  e  Mapear o resultado obtido.

Quando você deve considerar usar o Dapper ?

A seguir alguns fatores que você deve considerar para usar o Dapper:

- Considere o principal motivo do Dapper existir : desempenho
- Cenários onde os dados read-only mudam e são solicitados com frequência
- Cenários onde não existe estado (aplicações web)
- Considere que você vai ter que escrever consultas SQL
- Cenários onde a estrutura do banco de dados não é normalizada
- Considere que você pode usar o EF Core e também o Dapper no mesmo projeto

Como instalar o Dapper ?

O Dapper é distribuído via pacotes Nuget e pode ser obtido neste endereço :
https://www.nuget.org/packages/Dapper/

O código fonte do Dapper pode ser consultado aqui : https://github.com/DapperLib/Dapper

Para instalar o Dapper em seu aplicativo .NET você pode usar a janela do Package Manager Console e digitar o comando:  install-package Dapper -Version 2.0.90

Usando a ferramenta de linha de comando NET CLI o comando usado é o seguinte:
dotnet add package Dapper --version 2.0.90

Principais métodos de extensão do Dapper

O Dapper oferece sete métodos de extensão para do objeto IDbConnection, além de seus respectivos métodos assíncronos e dinâmicos e todos os métodos podem ser chamados a partir deste objeto.

1-Execute - Pode executar um comando uma ou várias vezes e retornar o número de linhas afetadas. Usado para executar: Stored procedures e instruções INSERT, UPDATE e DELETE

2- Query - Executa uma consulta e mapeia o resultado para um objeto. O resultado pode ser mapeado para : tipo fortemente tipado, objeto anônimo, multi-mapeamento(um-para-um) e (um-para-muitos), multi-tipo.

3- QueryFirst - Executa uma consulta e mapeia a primeira linha do resultado. Se a consulta não tiver resultado, uma exceção InvalidOperationException é disparada.

4- QueryFirstOrDefault - Executa uma consulta e mapeia a primeira linha do resultado. Se a consulta não tiver resultado pode retornar um valor padrão ou retornar null.

5- QuerySingle - Executa uma consulta e retorna uma única linha caso contrário, se não houver resultado, dispara uma exceção InvalidOperationException

6- QuerySingleOrDefault - Executa uma consulta e mapeia o primeiro resultado ou um valor padrão se a sequência estiver vazia; este método lança uma exceção se houver mais de um elemento na sequência.

7- QueryMultiple - Pode manipular múltiplos results sets, permitindo escrever consultas que retornem mais de um select.

O Dapper permite parametrizar as consultas de 3 formas principais:

1- Anonymous - Usar objetos/tipos anônimos.  É a forma mais usada e simples de parametrização:

IEnumerable<Product> produto;

using (var connection = new SqlConnection(configuration.GetConnectionString("DefaultConnection")))
{
   produto = connection.Query<Product>(@"SELECT * FROM Products WHERE ProductID = @ID", new { id = 2 });
}

2- Dynamic - Usar o mapeamento dinâmico.  Cria e usa um parâmetro em um método Dapper. Ideal para executar Stored Procedures parametrizadas:

var sql = “VendasPorCategoria";

using (var connection = new SqlConnection(configuration.GetConnectionString("DefaultConnection")))
{
    var parameter = new DynamicParameters();

    parameter.Add("@CategoryName", categoryName, DbType.String, ParameterDirection.Input);
    parameter.Add("@OrdYear", orderYear.ToString(), DbType.String, ParameterDirection.Input);
    parameter.Add("@RowCount", dbType: DbType.Int32, direction: ParameterDirection.ReturnValue);

    connection.Execute(sql, parameter, commandType: CommandType.StoredProcedure);
    var totalRows = parameter.Get<int>("@RowCount");
}   

3-List -  Permite que você especifique vários parâmetros em uma cláusula IN usando uma lista.

IEnumerable<Product> produto;

using (var connection = new SqlConnection(configuration.GetConnectionString("DefaultConnection")))
{
    produto = connection.Query<Product>(@"SELECT * FROM Products WHERE ProductID IN @ID", new { id = new[] { 2, 3, 8, 20 } });
}
   

Criando uma API com Dapper : Definindo o banco de dados e a tabela

Para mostrar um exemplo prático usando o Dapper vamos criar uma Web API ASP .NET Core no ambiente do  .NET 5.0 onde vamos definir o padrão repositório usando a biblioteca Dapper.

A Web API vai expor endpoints que permitem consultar e realizar as operações para incluir, alterar e excluir informações de Tarefas. Para isso vamos usar um banco de dados SQL Server chamado Teste.mdf onde temos a tabela Tarefas com a seguinte estrutura  :

A tabela Tarefas já possui os seguintes dados para teste:

Criando o projeto Web API no VS 2019

Abra o VS 2019 e clique em New Project e selecione o template ASP .NET Core Web API e clique em Next;

Informe o nome DapperAPI e clique em Next;

A seguir selecione o Target Framework, Authentication Type e demais configurações conforme mostrada na figura:

Clique em Create.

Vamos incluir no projeto o pacote do Dapper via menu Tools usando a opção Manage Nuget Packages for Solution;

Selecione a guia Browse e informe o nome do pacote : Dapper

Agora vamos criar as pastas Data e Repositories no projeto.

Na pasta Data vamos criar a classe Tarefa que representa o nosso modelo de domínio:

public class Tarefa
{
    public int Id { get; set; }
    public string Descricao { get; set; }
    public bool IsCompleta { get; set; }
}

Vamos criar nesta pasta a classe TarefaContainer que iremos usar para obter informações usando o método QueryMultiple que vai retornar o total de tarefas e a lista de tarefas:

public class TarefaContainer
{
    public int Contador { get; set; }
    public List<Tarefa> Tarefas { get; set; }
}

A seguir vamos criar a classe DbSession que vai gerenciar as conexões com o banco de dados:

using Microsoft.Extensions.Configuration;
using System;
using System.Data;
using System.Data.SqlClient;
namespace API.Data
{
        public class DbSession : IDisposable
        {
            public IDbConnection Connection { get; }

            public DbSession(IConfiguration configuration)
            {
                Connection = new SqlConnection(configuration
                         .GetConnectionString("DefaultConnection"));
                Connection.Open();
            }
            public void Dispose() => Connection?.Dispose();
        }
}

A classe DbSession usa a interface IDbConnection responsável por gerenciar a conexão. Ela implementa a interface IDisposable e vai criar e abrir uma conexão e a seguir vai liberar o objeto Connection.

No construtor da classe sempre iniciamos uma conexão e para isso obtemos a string de conexão do arquivo appsettings,json injetando uma instancia de IConfiguration.

O método Dispose da interface IDisposable será executado no término da instância desta classe, e, com isso podemos usar esta classe com a instrução using iniciando o contexto e ao final fechamos a conexão.

Nota:  Se você precisar reabrir a conexão novamente pode usar o método Close ao invés de Dispose().

Vamos concluir este artigo incluindo no arquivo appsettings.json a string de conexão com o banco de dados:

{
  "ConnectionStrings": {
    "DefaultConnection": "Data Source=SUA_STRING_DE_CONEXAO"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*"
}

Na próxima parte do artigo vamos implementar o padrão Repository e criar o controlador TarefasController.

"Porque, se alguém cuida ser alguma coisa, não sendo nada, engana-se a si mesmo. Mas prove cada um a sua própria obra, e terá glória só em si mesmo, e não noutro."
Gálatas 6:3,4

Referências:


José Carlos Macoratti