Dapper: relacionamentos Um-para-Um e Um-para-Muitos (exemplos em ASP.NET Core)
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.