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:
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.