ASP .NET Core - Criando uma Web API com Dapper - II
Hoje continuar a criação de uma Web API ASP .NET Core usando o Dapper. |
Continuando a primeira parte do artigo vamos implementar o padrão repository e a seguir criar a API.
Implementando o padrão Repository
Na pasta Repositories do projeto crie a interface ITarefaRepository :
using API.Data;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace API.Repositories
{
public interface ITarefaRepository
{
Task<List<Tarefa>> GetTarefasAsync();
Task<Tarefa> GetTarefaByIdAsync(int id);
Task<TarefaContainer> GetTarefasEContadorAsync();
Task<int> SaveAsync(Tarefa novaTarefa);
Task<int> UpdateTarefaStatusAsync(Tarefa atualizaTarefa);
Task<int> DeleteAsync(int id);
}
}
|
Esta interface define o contrato para expor os serviços da API. Usamos a classe Task de forma que a implementação será feita de forma assíncrona.
A seguir na mesma pasta crie a classe concreta TarefaRepository que implementa esta interface :
using API.Data;
using Dapper;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace API.Repositories
{
public class TarefaRepository : ITarefaRepository
{
private DbSession _db;
public TarefaRepository(DbSession dbSession)
{
_db = dbSession;
}
public async Task<List<Tarefa>> GetTarefasAsync()
{
using (var conn = _db.Connection)
{
string query = "SELECT * FROM Tarefas";
List<Tarefa> tarefas = (await conn.QueryAsync<Tarefa>(sql: query)).ToList();
return tarefas;
}
}
public async Task<Tarefa> GetTarefaByIdAsync(int id)
{
using (var conn = _db.Connection)
{
string query = "SELECT * FROM Tarefas WHERE Id = @id";
Tarefa tarefa = await conn.QueryFirstOrDefaultAsync<Tarefa>
(sql: query, param: new { id });
return tarefa;
}
}
public async Task<TarefaContainer> GetTarefasEContadorAsync()
{
using (var conn = _db.Connection)
{
string query =
@"SELECT COUNT(*) FROM Tarefas
SELECT * FROM Tarefas";
var reader = await conn.QueryMultipleAsync(sql: query);
return new TarefaContainer
{
Contador = (await reader.ReadAsync<int>()).FirstOrDefault(),
Tarefas = (await reader.ReadAsync<Tarefa>()).ToList()
};
}
}
public async Task<int> SaveAsync(Tarefa novaTarefa)
{
using (var conn = _db.Connection)
{
string command = @"NSERT INTO Tarefas(Descricao, IsCompleta)
VALUES(@Descricao, @IsCompleta)";
var result = await conn.ExecuteAsync(sql: command, param: novaTarefa);
return result;
}
}
public async Task<int> UpdateTarefaStatusAsync(Tarefa atualizaTarefa)
{
using (var conn = _db.Connection)
{
string command = @"
UPDATE Tarefas SET IsCompleta = @IsCompleta WHERE Id = @Id";
var result = await conn.ExecuteAsync(sql: command, param: atualizaTarefa);
return result;
}
}
public async Task<int> DeleteAsync(int id)
{
using (var conn = _db.Connection)
{
string command = @"DELETE FROM Tarefas WHERE Id = @id";
var resultado = await conn.ExecuteAsync(sql: command, param: new { id });
return resultado;
}
}
}
}
|
Na implementação
da interface estamos injetando uma instância da classe
DbSession e a seguir definimos métodos assíncronos usando
async/await e estamos usando uma instrução
using onde usamos o contexto da conexão que
ao final vai liberar a conexão desativando o objeto
Connection.
Nota: A instrução using permite usar a conexão obtida da instancia de DbSession e ao final vai fechar e liberar o objeto connection.
Na implementação dos métodos usamos os seguintes recursos:
1- GetTarefasAsync
Aqui usamos o método QueryAsync<T> para executar a consulta que a considera como um parâmetro de entrada e retorna os dados mapeando para o tipo T.
2- GetTarefaByIdAsync
Usamos o método de extensão 'QueryFirstOrDefaultAsync' que retorna o primeiro registro de acordo com o filtro que passamos na consulta. Para este método de extensão, estamos passando valores de filtro como objetos anônimos usando a palavra-chave 'new'.
3- GetTarefasEContadorAsync
O Dapper tem a capacidade de retornar os conjuntos de resultados de várias tabelas. Aqui criamos um novo modelo para capturar mais de um resultado na classe TarefaContainer.
Criamos duas consultas, uma retorna a contagem das tarefas e a outra retorna o total das tarefas. A seguir usamos o método 'QueryMultipleAsync' que executa a consulta e retorna vários conjuntos de resultados.
Depois lemos o primeiro conjunto de resultados, usando ReadAsync<int> para obter o total das tarefas e depois lemos o segundo conjunto de resultados, ou seja, coleção de tarefas usando ReadAsync<Tarefa>.
4 - SaveAsync
Este método vai
incluir uma nova tarefa e criamos a consulta SQL usando INSERT INTO e estamos
usando parâmetros dinâmicos : '@Descricao' e '@IsCompleta'.
O método de extensão 'ExecuteAsync' executa o
comando fornecido.
Este método recebe como parâmetro 'param' uma 'novaTarefa'
(instância de tarefa), onde cada nome das propriedades definidas em 'novaTarefa'
deve corresponder ao nome dos parâmetro dinâmicos.
O tipo de retorno de 'ExecuteAsync' é um valor
inteiro para representar o número de linhas afetadas na execução do comando,
portanto, se retornar zero, não haverá efeito do comando no banco de dados.
A lógica do método UpdateTarefaStatusAsync é a mesma usada em SaveAsync , e, a lógica do método DeleteAsync é a mesma que a usada em GetTarefaByIdAsync.
Para concluir precisamos agora registrar tanto a classe DbSession como o Repository como um serviço na classe Startup:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "API", Version = "v1" });
});
services.AddScoped<DbSession>();
services.AddTransient<ITarefaRepository, TarefaRepository>();
}
|
E desta forma podemos agora criar o controlador TarefasController na pasta Controllers:
using API.Data;
using API.Repositories;
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;
namespace API.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class TarefasController : ControllerBase
{
private readonly ITarefaRepository _tarefaRepo;
public TarefasController(ITarefaRepository tarefaRepo)
{
_tarefaRepo = tarefaRepo;
}
[HttpGet]
[Route("tarefas")]
public async Task<IActionResult> GetTarefasAsync()
{
var result = await _tarefaRepo.GetTarefasAsync();
return Ok(result);
}
[HttpGet]
[Route("tarefa")]
public async Task<IActionResult> GetTodoItemByIdAsync(int id)
{
var tarefa = await _tarefaRepo.GetTarefaByIdAsync(id);
return Ok(tarefa);
}
[HttpGet]
[Route("tarefascontador")]
public async Task<IActionResult> GetTodosAndCountAsync()
{
var resultado = await _tarefaRepo.GetTarefasEContadorAsync();
return Ok(resultado);
}
[HttpPost]
[Route("criartarefa")]
public async Task<IActionResult> SaveAsync(Tarefa novaTarefa)
{
var result = await _tarefaRepo.SaveAsync(novaTarefa);
return Ok(result);
}
[HttpPost]
[Route("atualizastatus")]
public async Task<IActionResult> UpdateTodoStatusAsync(Tarefa atualizaTarefa)
{
var result = await _tarefaRepo.UpdateTarefaStatusAsync(atualizaTarefa);
return Ok(result);
}
[HttpDelete]
[Route("deletatarefa")]
public async Task<IActionResult> DeleteAsync(int id)
{
var resultado = await _tarefaRepo.DeleteAsync(id);
return Ok(resultado);
}
}
}
|
No controlador injetamos uma instância do repositório ITarefaRepository e com essa instância estamos realizando o CRUD básico na tabela Tarefas.
Ao executar o projeto teremos a apresentação dos endpoints da nossa API exibidos na interface do Swagger:
Basta testar cada um deles e confirmar a implementação feita conforme foi mostrado neste vídeo no youtube: https://www.youtube.com/watch?v=85w8m-EdRs8&t=390s
Pegue o projeto aqui : DapperAPI.zip (sem as referências)
"Voz do que
clama no deserto: Preparai o caminho do Senhor; endireitai no ermo vereda a
nosso Deus.
Todo o vale será exaltado, e todo o monte e todo o outeiro será abatido; e o que
é torcido se endireitará, e o que é áspero se aplainará."
Isaías 40:3,4
Referências:
C# - Tasks x Threads. Qual a diferença
VB .NET - Datas, horas: conceitos e operações
C# - Programação Assíncrona como : Asycn e Task
O tratamento de datas no VB.NET -
C# - Obtendo a data e a hora por TimeZone
C# - O Struct Guid - Macoratti.net
C# - Checando Null de uma forma mais elegante e concisa
DateTime - Macoratti.net
Null o que é isso ?
Formatação de data e hora para uma cultura ...
C# - Calculando a diferença entre duas datas
NET - Padrão de Projeto - Null Object Pattern
C# - Fundamentos : Definindo DateTime como Null ...
C# - Os tipos Nullable (Tipos Anuláveis)