Como Mapear Coleções no Dapper

O Problema do mapeamento de Collections

Nós sabemos que o Dapper nos auxilia e muito quando a questão é performasse. Muitas pessoas usam ele para ganhar em tempo de resposta. Mais já vi muita gente que acaba utilizando o Entity para porder resolver os problemas das Collections dentro de uma entidade, por não dar conta de fazer esse mapeamento no Dapper , e acaba tendo um deficit de performasse quando tem que fazer esse tipo de mapeamento.

Proposta de Exemplo

Para poder explicar como é o processo de leitura de coleções no Dapper , vamos propor o seguinte cenário, uma entidade Pessoa onde ela tem vários Telefones. Então nossas entidades vão ficar da seguinte forma:

public class Pessoa
{
    [Key]
    public Guid Id { get; set; }

    [MaxLength(100)]
    public string Nome { get; set; }

    public virtual ICollection<Telefone> Telefones { get; set; }
}
public class Telefone
{
    [Key]
    public Guid Id { get; set; }

    [MaxLength(10)]
    public string Contato { get; set; }
        
    public Guid PessoaId { get; set; }

    [ForeignKey("PessoaId")]
    public virtual Pessoa PessoaObject { get; set; }
}

Vejamos que a Pessoa possui uma coleção de Telefones, no Entity Framework já conseguimos fazer a leitura via proxy do Entity, mais e no Dapper como ficaria essa leitura?

Solução do Problema no Dapper

Bem, vamos considerar que já tenhamos incluído dados em nosso banco. O primeiro passo para resolver o problema inicia na Query que vamos ter que escrever, vamos ter que fazer um Join entre as duas Entidades e retornar todas as linhas, veja como vai ficar:

select t1.*, t2.* from Pessoas t1
   left join Telefones t2
   on t1.Id = t2.PessoaId

O nosso retorno vai ficar da seguinte forma:

Retorno do banco para manipulação do Dapper

Podemos ver que as linhas referentes a Pessoa foram repetidas para cada Telefone, ai vem o truque, vamos resolver esse problema no Dapper , veja abaixo como fica o nosso código para mapear o objeto.

var _cn = _db.Database.Connection;

var sql = @"select t1.*, t2.* from Pessoas t1
                left join Telefones t2
                on t1.Id = t2.PessoaId";

var retorno = new Dictionary<Guid, Pessoa>();

_cn.Query<Pessoa, Telefone, Pessoa>(sql,
(p, t) => {
    Pessoa pessoa;
    if (!retorno.TryGetValue(p.Id, out pessoa))
    {
        pessoa = p;
        pessoa.Telefones = new List<Telefone>();

        retorno.Add(pessoa.Id, pessoa);
    }

    pessoa.Telefones.Add(t);

    return pessoa;
}, splitOn: "Id,Id");

Para fazer o mapeamento da coleção vamos utilizar um Dicionário, onde a chave dele vai ser o Id da Pessoa e o value a própria pessoa. A jogada é que quando for mapear os dados vamos incluir a pessoa no Dicionário, se já existir vamos retornar a pessoa e só incluir os contatos, conforme abaixo:

(p, t) => {
    Pessoa pessoa;
    if (!retorno.TryGetValue(p.Id, out pessoa))
    {
        pessoa = p;
        pessoa.Telefones = new List<Telefone>();

        retorno.Add(pessoa.Id, pessoa);
    }

    pessoa.Telefones.Add(t);

    return pessoa;
}

Com isso conseguimos fazer o mapeamento dos Telefones da Pessoa e na hora de Retornar mandamos o Value do Dicionário.

Conclusão

Como podemos ver o Dapper é uma opção de ORM bem flexível quando precisamos fazer um mapeamento.

Nas referências vou deixar um exemplo completo utilizando tanto o Entity como o Dapper para fazer o mesmo mapeamento.

Espero ter tirado todas as dúvidas sobre o assunto, até a próxima.

Referência

https://github.com/tborgesvieira/dapperlist

Posted in .NET, Blog, Dapper and tagged , .

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *