Dapper: relacionamentos Um-para-Um e Um-para-Muitos (exemplos em ASP.NET Core)

Renato Groffe
5 min readAug 6, 2017

--

Embora mais limitado quando comparado a soluções mais sofisticadas de acesso a dados relacionais (como o Entity Framework e o NHibernate), o micro-ORM Dapper ainda assim desfruta de grande popularidade entre desenvolvedores .NET. Um dos principais motivos para isto se deve à alta performance que o mesmo propicia, sobretudo na execução de queries mais complexas.

Já abordei o uso do Dapper em um artigo anterior, no qual apresentei diversos exemplos em .NET Full e .NET Core:

Dapper: exemplos de utilização em ASP.NET Core e .NET Full

Neste novo post demonstrarei como implementar relacionamentos um-para-um e um-para-muitos com este micro-ORM, considerando para isto a execução de uma única consulta/query englobando mais de uma tabela.

OBSERVAÇÃO: embora os exemplos descritos a seguir façam uso do ASP.NET Core 1.1 e do Visual Studio 2017, tais implementações também são válidas para projetos baseados no .NET Full (como aplicações MVC 5 ou Web API 2, por exemplo).

Implementando relacionamentos do tipo um-para-um

A implementação de um relacionamento um-para-um é suportada pelo Dapper sem maiores complicações. Do ponto de vista prático, faremos uso apenas deste framework e não será necessário nenhum recurso adicional (diferentemente de situações envolvendo um-para-muitos, assunto da próxima seção).

Tomemos agora por exemplo uma classe chamada Estado, cujo código está detalhado na listagem a seguir:

  • A propriedade DadosRegiao (tipo Regiao) contém dados da área geográfica da qual um estado faz parte;
  • Podemos concluir a partir disto que a tabela estados está vinculada a uma estrutura do mesmo tipo, esta última englobando dados de regiões.

A definição do tipo Regiao está na listagem seguinte:

Supondo agora uma query que relaciona dados das duas tabelas (estados e regiões):

E cujo resultado pode ser visualizado na próxima imagem:

Como podemos então executar esta única consulta no banco de dados, devolvendo como resultado instâncias do tipo Estado com suas propriedades DadosRegiao devidamente preenchidas?

O Dapper conta com um mecanismo de multi mapping que viabiliza a codificação deste tipo de necessidade.

Na listagem a seguir podemos observar o uso desta funcionalidade na implementação do tipo EstadosController:

  • O método Query conta com uma sobrecarga na qual devem ser especificadas as diferentes classes empregadas na produção do resultado. O último tipo informado corresponde à classe dos objetos retornados (Estado, neste caso);
  • O parâmetro map faz uso de uma expressão lambda, a fim de mapear os diferentes tipos de objetos gerados e devolver via instrução return a instância principal (tipo Estado). Os parâmetros informados aqui seguem a mesma ordem do método Query, com a referência indicada pelo parâmetro regiao sendo associada à propriedade DadosRegiao do objeto estado;
  • No parâmetro splitOn estão definidos os campos-chave dos objetos a serem gerados. Esta informação serve de base para que o Dapper efetue a separação dos dados em suas instâncias equivalentes

As próximas imagens trazem o resultado da execução deste Controller:

Caso queira ter acesso ao código-fonte do projeto detalhado nesta seção acesse esse endereço no GitHub.

Implementando relacionamentos do tipo um-para-muitos

A implementação de relacionamentos um-para-muitos também é possível com o Dapper. Para o exemplo desta seção faremos uso de implementação diferente da classe Regiao, na qual é possível observar a existência de uma coleção chamada Estados (esta última baseada no tipo Estado e contendo dados das diversas unidades federais que integram uma área geográfica):

Será empregado aqui o package Slapper.AutoMapper (ainda em versão beta para .NET Core), além do Dapper e do provider ADO.NET para SQL Server:

O Slapper.Automapper permite o mapeamento de dados dinâmicos para objetos, incluindo a capacidade de preencher coleções empregadas no relacionamento entre diferentes tipos.

A query a seguir lista as regiões e os estados associados a estas divisões geográficas:

O resultado da execução desta consulta pode ser observado na próxima imagem:

Na próxima listagem está a implementação da classe RegioesController:

  • O retorno do método Query (uma extensão disponibilizada pelo Dapper) será uma coleção de objetos dinâmicos. Na instrução SQL informada como parâmetro a esta operação é possível notar que as colunas que se referem a propriedades do tipo Estado tiverem seu alias alterado, de forma que a identificação de cada campo contenha o nome da coleção a ser mapeada (Estados), o caracter underline (“_”) e a propriedade correspondente na classe-filha (Estado, no caso).
  • Serão acessadas então as classes AutoMapper e Config do framework Slapper, a fim de invocar a operação AddIdentifier. Este método receberá como parâmetro o tipo a ser mapeado e o nome da propriedade correspondente à chave primária. Esta ação deverá acontecer para cada classe envolvida no relacionamento;
  • Por fim, a chamada ao método MapDynamic da classe AutoMapper converterá o resultado para uma lista de instâncias do tipo Regiao. A propriedade Estados de cada objeto gerado conterá, por sua vez, diversas referências da classe Estado.

Nas próximas imagens é possível visualizar o resultado da execução desta aplicação:

Os fontes do projeto detalhado nesta seção também foram disponibilizados no GitHub. Se tiver interesse em consultar e/ou baixar tal conteúdo acesse este link.

E por falar em tecnologias Microsoft, no dia 07/07/2017 (quinta) às 21h00 - horário de Brasília - teremos mais um hangout no Canal .NET. O tema desta será a análise do plano de execução de consultas com o SQL Server Query Store, com a presença da MTAC Sulamita Dantas.

Para se inscrever e participar acesse esta página. A transmissão será via YouTube, em um link a ser divulgado em breve.

--

--

Renato Groffe
Renato Groffe

Written by Renato Groffe

Microsoft Most Valuable Professional (MVP), Multi-Plataform Technical Audience Contributor (MTAC), Software Engineer, Technical Writer and Speaker

Responses (6)