.NET - Usando Dapper Contrib


   Hoje vamos apresentar o Dapper.Contrib, qual a sua proposta, e como ele pode ajudar quem usa o Dapper.

Indo direto ao ponto o Dapper.Contrib é uma biblioteca 'helper' construída sobre o Dapper que inclui recursos para facilitar a vida de quem usa o Dapper.

Ele foi criado para facilitar ainda mais o desenvolvimento com o Dapper, reduzindo a quantidade de código necessário para executar operações comuns de CRUD (Create, Read, Update, Delete).

O Dapper.Contrib fornece um conjunto de atributos que você pode adicionar às suas classes de modelo para definir o mapeamento entre as propriedades da classe e as colunas do banco de dados. Com esses atributos, você pode definir de forma simples e declarativa como as operações de inserção, atualização e exclusão devem ser realizadas.

Aqui estão alguns dos principais recursos do Dapper.Contrib:

  1. Mapeamento automático: Com o Dapper.Contrib, você não precisa escrever manualmente as consultas SQL para operações básicas de CRUD. Ele pode mapear automaticamente suas classes de modelo para as tabelas do banco de dados com base nos atributos fornecidos.
     
  2. Suporte a operações CRUD: O Dapper.Contrib adiciona métodos auxiliares para realizar facilmente operações de inserção, atualização e exclusão em seu banco de dados. Por exemplo, você pode usar o método Insert para inserir um novo registro e o método Update para atualizar um registro existente.
     
  3. Suporte a chaves primárias: Com o Dapper.Contrib, você pode especificar a chave primária de uma tabela usando o atributo [Key]. Isso permite que você use métodos como Get, Delete e GetAll para recuperar registros com base em suas chaves primárias.
     
  4. Relacionamentos entre tabelas: O Dapper.Contrib também oferece suporte a relacionamentos entre tabelas. Você pode usar atributos como [ExplicitKey], [Computed] e [Write(false)] para configurar relacionamentos entre as entidades e mapear as colunas correspondentes.

Desta forma o Dapper.Contrib é uma extensão do Dapper que adiciona recursos extras, como mapeamento automático, operações CRUD simplificadas e suporte a relacionamentos entre tabelas ajudando a reduzir a quantidade de código que você precisa escrever ao trabalhar com o Dapper, tornando o desenvolvimento de acesso a banco de dados mais rápido e fácil.

Usando o Dapper.Contrib

Para usar este recurso no seu projeto .NET basta incluir o pacote nuget Dapper.Contrib :

 

Ao instalar o pacote Dapper.Contrib o Dapper vem junto como uma dependência do pacote.

A seguir precisamos criar o banco de dados e as tabelas e a seguir criar as entidades definindo propriedades que devem mapear para as respectivas tabelas do banco de dados que desejamos usar.

Assim criamos o banco de dados Cadastro e a tabela Eventos com a estrutura abaixo onde já incluímos um registro na tabela:

Importante destacar que definimos a coluna Id como sendo do tipo Identidade e assim ela será autoincremental e o SGBD vai gerenciar esta informação.

Agora vamos realizar um CRUD básico nesta tabela Eventos usando o Dapper.Contrib.

Vamos criar uma entidade Evento com o seguinte código:

public class Evento
{
   public int Id { get; set; }
   public int LocalId { get; set; }
   public string Nome { get; set; }
   public DateTime Data { get; set; }
   public DateTime DataCriacao { get; set; }
}

Em primeiro lugar, Dapper.Contrib requer um “Id” para poder atualizar um registro. No nosso caso, temos uma propriedade chamada “Id” e o Dapper.Contrib usa essa convenção e sabe que ela é nossa chave primária na tabela.

Mas, e se usarmos algo como “EventoId” como nossa chave primária ?

public class Evento
{
   [Key]
   public int EventoId { get; set; }

   public int LocalId { get; set; }
   public string Nome { get; set; }
   public DateTime Data { get; set; }
   public DateTime DataCriacao { get; set; }
}

Basta adicionar o atributo “Key” da biblioteca Dapper.Contrib para informar que é nossa chave primária.

Em seguida, também precisamos informar que o nome da classe seja o mesmo que o nome da Tabela SQL, mas no plural. Então, no nosso caso, o nome da nossa classe é Evento e ela espera que nossa tabela de banco de dados seja chamada de “Eventos”.

[Table("Eventos"]
public class Evento
{
   [Key]
   public int EventoId { get; set; }

   public int LocalId { get; set; }
   public string Nome { get; set; }
   public DateTime Data { get; set; }
   public DateTime DataCriacao { get; set; }
}

Basta usar o atributo [Table("nome")] para definir para qual tabela do banco vamos mapear a entidade.

Além destes atributos temos outros atributos adicionais como:

Atributo Descricao
Key  Torna a coluna correspondente a coluna chave primária da tabela
Table  Mapeia para uma tabela com o nome especificado
ExplicitKey  Define que a propriedade é uma chave que não é gerada pelo banco
Write  Define que a propriedade é de gravação ou não
Computed  Especifica que a propriedade deve ser excluida da atualização

Para ilustrar o uso dos recursos do Dapper.Contrib vamos criar uma aplicação Console  no VS 2022 e instalar os pacotes :

Vamos criar um método estático para retornar um objeto IDbConnection :

static IDbConnection GetDbConnection()
{
  
string connectionString =
    
"Data Source=<seu_servidor>;Initial Catalog=Cadastro;Integrated Security=True";
  
return new SqlConnection(connectionString);
}

Ao usar IDbConnection, você pode fornecer diferentes implementações, como SqlConnection para SQL Server, MySqlConnection para MySQL ou OracleConnection para Oracle, sem alterar muito o restante do código. Isso ajuda a manter a flexibilidade e facilita a portabilidade do código.

Nesse código, o método GetDbConnection() retorna uma instância de IDbConnection usando a implementação específica SqlConnection. Dessa forma, se você precisar alterar o provedor de banco de dados, basta modificar o método GetDbConnection() para retornar a implementação correspondente, mantendo o restante do código inalterado.

Inserindo um evento

Inserir dados no banco de dados é uma das operações CRUD que atuam em uma linha individual inserindo uma linha.

A biblioteca Dapper.Contrib fornece o método de extensão Insert para inserir dados no banco de dados.

static void InserirEvento()
{
 
var evento = new Evento
  {
    LocalId = 3,
    Nome =
"Meu novo Evento",
    Data = DateTime.Now,
    DataCriacao = DateTime.Now
  };
 
int eventoId;
  Console.WriteLine(
"\nCriar um novo Evento ");
 
// Inserção
 
using (var db = GetDbConnection())
  {
   
try
    {
      db.Open();
      eventoId = (
int)db.Insert(evento);
      db.Close();
      Console.WriteLine($
"Evento {evento.Nome} incluido com sucesso");
    }

    catch
(Exception ex)
    {
      Console.WriteLine(ex.Message);
    }
  }
}

Podemos usar o método Insert para incluir múltiplos registro na base de dados:

static void InserirMultiplosEventos()
{
  
using (var db = GetDbConnection())
   {
     List<Evento> eventos =
new List<Evento>()
     {
       
new Evento { Id = 4, LocalId=2, Nome = "Meu Evento Recente", Data = DateTime.Now, DataCriacao = DateTime.Now },
 
      new Evento { Id = 5, LocalId=3, Nome = "Meu Novo Recente", Data = DateTime.Now, DataCriacao = DateTime.Now },
      };
      db.Insert<List<Evento>>(eventos);
   }
}

Buscando um evento pelo ID

Podemos recuperar um registro específico do banco de dados usando o método Get e passar o id como argumento.

static void BuscarEventoPorId()
{
   Console.WriteLine(
"\nDigite o ID do evento:");
  
if (int.TryParse(Console.ReadLine(), out int eventoId))
   {
    
using (var connection = GetDbConnection())
     {
      
var eventoRecuperado = connection.Get<Evento>(eventoId);
      
if (eventoRecuperado != null)
       {
         Console.WriteLine(
"Evento encontrado:");
         Console.WriteLine(
$"ID: {eventoRecuperado.Id}");
         Console.WriteLine(
$"Nome: {eventoRecuperado.Nome}");
         Console.WriteLine(
$"Data: {eventoRecuperado.Data}");
         Console.WriteLine(
$"Data de Criação: {eventoRecuperado.DataCriacao}");
       }
      
else
       {
          Console.WriteLine(
"\nEvento não encontrado.");
       }
    }
  }
 
else
  {
    Console.WriteLine(
"\nID inválido.");
  }
}

Buscando todos os eventos

Podemos obter todos os eventos do banco de dados usando o método GetAll<T>() :

static void ListarEventos()
{
   Console.WriteLine(
"\nEventos cadastradaos\n");
  
using (var db = GetDbConnection())
   {
      List<Evento> eventos = db.GetAll<Evento>().ToList();
     
foreach (var evento in eventos)
      {
         Console.WriteLine(
$"Nome: {evento.Nome} \t Data: {evento.Data} ");
      }
    }
}

Atualizar um evento

A atualização de uma entidade existente é semelhante à inserção de uma nova entidade.

Para fazer isso podemos usar o método de extensão Update para atualizar os dados existentes no banco de dados.

static void AtualizarEvento()
{
   Console.WriteLine(
"\nDigite o ID do evento a ser atualizado:");
  
if (int.TryParse(Console.ReadLine(), out int eventoId))
   {
    
using (var connection = GetDbConnection())
     {
       
var evento = connection.Get<Evento>(eventoId);
       
if (evento != null)
        {
           Console.WriteLine(
"Digite o novo nome do evento:");
           evento.Nome = Console.ReadLine();
          
bool atualizado = connection.Update(evento);
          
if (atualizado)
           {
             Console.WriteLine(
"\nEvento atualizado com sucesso.");
           }
          
else
           {
              Console.WriteLine(
"\nFalha ao atualizar o evento.");
           }
         }
        
else
         {
            Console.WriteLine(
"\nEvento não encontrado.");
         }
      }
   }
  
else
   {
     Console.WriteLine(
"ID inválido.");
   }
}

Podemos usar o método Update para atualizar múltiplos registros do banco de dados:

static void AtualizarMultiplosEventos()
{
  
using (var db = GetDbConnection())
   {
     List<Evento> eventos =
new List<Evento>()
     {
        
new Evento { Id = 4, LocalId=2, Nome = "Meu Evento Recente Atualizado", Data = DateTime.Now, DataCriacao = DateTime.Now },
 
       new Evento { Id = 5, LocalId=3, Nome = "Meu Novo Recente Atualizado", Data = DateTime.Now, DataCriacao = DateTime.Now },
     };
     db.Update<List<Evento>>(eventos);
   }
}

Excluir um evento

Excluir uma entidade é o mais fácil porque requer apenas um ID exclusivo para identificar a entidade que está sendo excluída.

O Dapper.Contrib fornece os métodos de extensão Delete e DeleteAll para excluir dados existentes do banco de dados.

static void ExcluirEvento()
{
   Console.WriteLine(
"\nDigite o ID do evento a ser excluído:");
  
if (int.TryParse(Console.ReadLine(), out int eventoId))
   {
    
using (var connection = GetDbConnection())
     {
        
var evento = connection.Get<Evento>(eventoId);
        
if (evento != null)
         {
            
bool excluido = connection.Delete(evento);
            
if (excluido)
             {
                Console.WriteLine(
"\nEvento excluído com sucesso.");
             }
            
else
             {
                Console.WriteLine(
"\nFalha ao excluir o evento.");
             }
          }
         
else
          {
            Console.WriteLine(
"\nEvento não encontrado.");
          }
       }
    }
   
else
    {
       Console.WriteLine(
"\nID inválido.");
    }
}

Você também pode usar o método Delete para excluir vários registros passando a lista como um argumento para o método Delete.

static void DeletarMultiplosEventos()
{

   using (var db = GetDbConnection())
   {
      List<Evento> eventos =
new List<Evento>()
      {
      
  new Evento { Id = 4 },
       
 new Evento { Id = 5 }
      };
      db.Delete<List<Evento>>(eventos);
   }
}

Temos assim uma visão geral dos recursos da biblioteca Dapper.Contrib na prática.

Pegue o projeto aqui:  Crud_DapperContrib.zip 

"A lei e os profetas duraram até João; desde então é anunciado o reino de Deus, e todo o homem emprega força para entrar nele.
E é mais fácil passar o céu e a terra do que cair um til da lei."
Lucas 16:16-17

Referências:


José Carlos Macoratti