amily: Arial">
Clique aqui para ler todos os artigos desta edição
Construindo um O/R Mapper em .NET
Como desenvolver um Object/Relational Mapper usando as classes de reflection do Microsoft.NET
Alexandre Valente
O uso cada vez maior de metodologias orientadas a objeto para análise e desenvolvimento de soluções fez com que a chamada “impedância” entre a implementação orientada a objetos e o uso de mecanismos de persistência baseados em bancos de dados relacionais tenha se tornado um fator crítico para muitos sistemas. Este tópico tem sido abordado com freqüência (ver artigos anteriores na SQL Magazine na Nota 1), mas ainda não há uma solução universal para o problema.
Devido ao uso intensivo de objetos em regras de negócio, uma grande porção de código das aplicações empresariais é utilizada para mapear objetos às respectivas entidades de dados do banco relacional. Mesmo com a adoção do Microsoft .NET, e suas diversas classes facilitadoras de acesso (ADO.NET), construir e manter rotinas para persistir e recuperar objetos em tabelas relacionais pode consumir quantidade significativa dos recursos de desenvolvimento de um projeto.
Uma alternativa comum utilizada para minimizar este problema é a adoção de um dos chamados Object/Relational Mappers, também conhecidos como O/R Mappers ou simplesmente ORM (não confundir com Object-Role Modeling, metodologia de análise). O objetivo de um ORM é gerenciar todas as atividades de persistência e recuperação de objetos, isolando a camada de dados relacional da camada de objetos e permitindo que o desenvolvedor possa trabalhar somente com os objetos. Em edições anteriores da SQL Magazine foram abordados, com detalhes, alguns destes ORM (ver Nota 1).
Este artigo tem o objetivo de descrever o funcionamento interno de um ORM baseado nos mecanismos de reflection do Microsoft .NET, usando um banco Microsoft SQL e o Visual Studio .NET 2003. Assume-se que o leitor tenha familiaridade com desenvolvimento .NET em C# e com mecanismos de acesso a dados ADO.NET (ver seção Links para mais informações).
Nota 1. Artigos Anteriores
SQL Magazine 2 e 3
Banco de dados orientado a objetos: uma introdução
Hibernate – Mapeamento OO x SGBDR
BDOO – Desenvolvimento de software com Java e Oracle
SQL Magazine 5
Mapeando um modelo de classes para um banco de dados relacional
SQL Magazine 15
Mecanismos de persistência em Java
SQL Magazine 17
Hibernate - O Framework que veio para simplificar. SQL nunca mais?
SQL Magazine 19
Hibernate - Facilitando o desenvolvimento web. Adivinhe como? Tirando todo o SQL!
Funcionamento de um O/R Mapper
A maior parte dos O/R Mappers existentes possui uma arquitetura bastante similar à mostrada na Figura 1. Existe um componente principal, aqui denominado “Gerente de Objetos” que implementa os patterns de Object-Factory e/ou Object-Builder, que é responsável por criar, recuperar e persistir instâncias de objetos em um banco de dados relacional.
Figura 1. Arquitetura geral de um O/R Mapper.
O segundo componente mais importante de um ORM é o gerador de comandos SQL, denominado na Figura 1 de “Gerente de SQL”. Ele é responsável por construir cada consulta ou comando a ser usado no banco de dados, incluindo comandos do tipo SELECT, INSERT, UPDATE e até mesmo comandos para execução de stored procedures.
Para associar um objeto a uma entidade de dados relacional, os sistemas ORM usualmente utilizam um ou mais dos seguintes métodos: utilização de uma classe-base para persistência; interfaces a serem implementadas pelos objetos de persistência; arquivos XML para mapeamento; e mecanismos de reflection (ver quadro Reflection em .NET). Assim, ao associar um objeto à entidade relacional, o ORM pode determinar quais colunas e tabelas devem ser atualizadas e automaticamente construir e executar o comando SQL pra efetuar a ação desejada.
Nota 2. Reflection em .NET
Reflection é o namespace do Microsoft .NET Framework que reúne funcionalidades que permitem que sejam obtidas informações sobre tipos de dados em tempo de execução. Estes mecanismos são extremamente versáteis e, além de trazer informações sobre parâmetros, tipos de dados e localização em assemblies, permitem também que métodos, construtores ou propriedades sejam invocados dinamicamente.
Construindo um O/R Mapper
Na Listagem 1 é mostrado um exemplo de funcionamento de um ORM básico. Como pode ser visto no exemplo, uma entidade Pessoa, que possui como propriedades o nome, CPF e data de nascimento é criada, persistida e alterada em um banco de dados, com o apoio da classe de controle ORM GerenteObjetos. No decorrer deste artigo serão mostrados os passos necessários para se construir este ORM, incluindo todas as classes e funcionalidades mostradas no exemplo.
using System;
class Principal {
[STAThread]
static void Main(string[] args) {
// Instancia o objeto de controle
GerenteObjetos gerente = new GerenteObjetos();
// Persiste nova instância
Pessoa p = new Pessoa();
p.Nome = "Han Solo";
gerente.Persistir(p);
int novoId = p.Id;
Console.WriteLine("Instância " + novoId.ToString() + " persistida!");
// Recupera instância existente
p = new Pessoa(gerente, novoId);
Console.WriteLine("Pessoa \"" + p.Nome + "\" recuperada!");
...