Dapper

Tenho utilizado nos últimos meses o Dapper como ORM, substituindo o NHibernate nos meus projetos profissionais e pessoais. Não pretendo explicar a origem do Dapper ou os motivos para utilizar um ORM e sim exemplificar o uso do Dapper.

Libs utilizadas

Para nosso exemplo, iremos criar uma entidade chamada Montadora

public class Montadora
{
  public int Id { get; set; }

  public string Nome { get; set; }

  public string PaisOrigem { get; set; }
}

Esta entidade será mantida pela tabela Montadora

CREATE TABLE [dbo].[Montadora] (
    [Id]         NUMERIC(10) IDENTITY(1,1),
    [Nome]       NCHAR (30) NULL,
    [PaisOrigem] NCHAR (30) NULL,
    PRIMARY KEY CLUSTERED ([Id] ASC)
)

A manutenção dos dados será realizado pela classe MontadoraRepositorio

public class MontadoraRepositorio : IMontadoraRepositorio
{

  public string ConnectionString = ConfigurationManager.ConnectionStrings["DbSample"].ConnectionString;

  public IEnumerable<Montadora> GetAll()
  {
	using (var db = new System.Data.SqlClient.SqlConnection(ConnectionString))
	{
	  var data = db.Query<Montadora>("SELECT Id ,Nome ,PaisOrigem FROM Montadora");

	  return data;
	}
  }

  public bool Insert(Montadora montadora)
  {
	using (var db = new System.Data.SqlClient.SqlConnection(ConnectionString))
	{
	  var data = db.Execute("insert into Montadora (Id, Nome, PaisOrigem) values(@Id, @Nome, @PaisOrigem)",
							new {Id = montadora.Id, Nome = montadora.Nome, PaisOrigem = montadora.PaisOrigem});

	  return data > 0;
	}
  }

  public bool Update(Montadora montadora)
  {
	using (var db = new System.Data.SqlClient.SqlConnection(ConnectionString))
	{
	  var data = db.Execute("update Montadora set Nome = @Nome, PaisOrigem = @PaisOrigem where Id = @Id",
							new {montadora.Nome, montadora.PaisOrigem, montadora.Id});

	  return data > 0;
	}
  }

  public bool Delete(int id)
  {
	using (var db = new System.Data.SqlClient.SqlConnection(ConnectionString))
	{
	  var data = db.Execute("delete from Montadora where Id = @Id", new {Id = id});

	  return data > 0;
	}
  }

  public Montadora Get(int id)
  {
	using (var db = new System.Data.SqlClient.SqlConnection(ConnectionString))
	{
	  var data = db.Query<Montadora>("SELECT Id ,Nome ,PaisOrigem FROM Montadora where Id = @Id", new {Id = id}).FirstOrDefault();

	  return data;
	}
  }
}

Detalhes a serem observados no repositório é que não precisamos realizar o mapeamento da nossa consulta para o objeto, o Dapper realiza todo o trabalho árduo.

A passagem de parâmetros para os comandos de insert, delete e updade é feita através de um objeto anônimo, deste modo, se as colunas possuírem a mesma estrutura de nomes da classes, basta passar os valores.

 

Até então, só utilizamos o Dapper, agora mostrarei a utilização do Dapper.Contrib que consegue deixar o desenvolvimento ainda mais simples.

public class MontadoraContribRepositorio : IMontadoraRepositorio
{
  public string ConnectionString = ConfigurationManager.ConnectionStrings["DbSample"].ConnectionString;

  public IEnumerable<Montadora> GetAll()
  {
	using (var db = new System.Data.SqlClient.SqlConnection(ConnectionString))
	{
	  return db.GetAll<Montadora>();
	}
  }

  public bool Insert(Montadora montadora)
  {
	using (var db = new System.Data.SqlClient.SqlConnection(ConnectionString))
	{
	  var data = db.Insert(montadora);

	  return data > 0;
	}
  }

  public bool Update(Montadora montadora)
  {
	using (var db = new System.Data.SqlClient.SqlConnection(ConnectionString))
	{
	  return db.Update(montadora);
	}
  }

  public bool Delete(int id)
  {
	using (var db = new System.Data.SqlClient.SqlConnection(ConnectionString))
	{
	  return db.Delete(new Montadora() {Id = id});
	}
  }

  public Montadora Get(int id)
  {
	using (var db = new System.Data.SqlClient.SqlConnection(ConnectionString))
	{
	  return db.Get<Montadora>(id);
	}
  }
}

Como podemos notar, com o Dapper.Contrib, não precisamos escrever nenhum comando SQL. Internamente ele já sabe o que precisa ser feito, mas para isto, precisamos realizar algumas alterações na nossa entidade

[Table("Montadora")]
public class Montadora
{
  [Key]
  public int Id { get; set; }

  public string Nome { get; set; }

  public string PaisOrigem { get; set; }
}

Um outro ponto importante é a necessidade da chave primária ser auto incremento, caso contrário, o Dapper irá gerar um erro de inclusão de NULL na pk da tabela.

O Dapper ainda permite realizar consultas para retorno de objetos mais complexos.

Para exemplificar, criei a entidade Carro que possui uma propriedade Montadora.

public class Carro
{
  [Key]
  public int Id { get; set; }

  public Montadora Montadora { get; set; }


  public string Nome { get; set; }

  public string Modelo { get; set; }

  public int AnoFabricacao { get; set; }
}

A tabela que representa esta entidade foi definida a seguir:

CREATE TABLE [dbo].[Carro] (
    [Id]            INT         NOT NULL,
    [Nome]          NCHAR (30)  NULL,
    [Modelo]        NCHAR (30)  NULL,
    [AnoFabricacao] NUMERIC (4) NULL,
    [MontadoraId]   INT         NULL,
    PRIMARY KEY CLUSTERED ([Id] ASC)
);

E o repositório para consulta dos dados ficou assim:

public class CarroRepositorio
{
  public string ConnectionString = ConfigurationManager.ConnectionStrings["DbSample"].ConnectionString;

  public IEnumerable<Carro> GetAll()
  {

	var query = @"select Carro.Id, Carro.Nome, Carro.Modelo, Carro.AnoFabricacao, Carro.MontadoraId, Montadora.Nome, Montadora.PaisOrigem 
                            from 	Carro,	Montadora where Carro.MontadoraId = Montadora.Id";

	using (var db = new System.Data.SqlClient.SqlConnection(ConnectionString))
	{
	  var data = db.Query<Carro, Montadora, Carro>(query,
												   (carro, montadora) =>
												   {
													 carro.Montadora = montadora;
													 return carro;
												   }, null, null, true, "MontadoraId");

	  return data;
	}
  }

O Dapper possui ainda uma série de funcionalidades muito bem documentada nos seus repositórios como BulkInsert e BulkUpdate por exemplo.

O código criado neste post pode ser baixado no meu github.