Sobre Abstração de Bancos de Dados em Aplicações .NET

Estive fazendo a migração da base de dados de um projeto ASP.Net do Oracle para o SQL Server. A princípio acreditei que, por termos utilizado a tecnologia do Entity Framework 3.5, o trabalho seria, simplesmente, trocar o provider EF do Oracle para o SQL Server. No entanto, por várias razões que eu citarei ainda neste artigo, foi bem mais complicado do que isso.

Intrigado pela dificuldade de lidar com o Entity Framework 3.5 neste cenário, comecei a buscar alternativas que fossem capazes de realizar a total abstração do banco de dados para o ambiente .NET.

Depois de muitas leituras, fiquei surpreso ao perceber que a única tecnologia capaz de fazer isso de forma eficiente é, de fato, o Entity Framework. Os dados que me levaram a esta conclusão, assim como as demais tecnologias testadas por mim, estão descritos abaixo.

1. DbProviderFactory (System.Data.Common).

Depois de uma conversa com o Ivan Kurt, descobri a existência do DbProviderFactory. O DbProviderFactory é uma classe que surgiu no .NET 2.0 que abstrai as classes ADO.Net de acesso ao banco de dados (EntityProvider, Odbc, OleDb, OracleClient e SqlClient). Isso quer dizer que, ao invés de utilizar essas classes específicas para cada banco de dados na sua camada de acesso a dados, você utiliza o DbProviderFactory. Este, por sua vez, através do polimorfismo e de uma chave definida no arquivo de configuração da aplicação, criará o provider correto para acessar o banco de dados, de forma transparente. Sim, exatamente como no padrão de design “Factory”. Sim, não é coincidência.

Fiquei entusiasmado com a ideia e cheguei a implementar um DbProviderFactory para o SQLite em um dos meus programas. No entanto, após finalizá-lo, percebi que, mesmo utilizando o meu provider factory, uma grande quantidade de trabalho poderia ser necessária para convertê-lo para, digamos, Oracle.

Isto porque, no final das contas, você TEM que especificar o comando SQL para a propriedade DbCommand.CommandText. Isso pode até funcionar bem entre bancos que suportam Stored Procedures, onde o comando de chamada pode permanecer sempre o mesmo. Não é o caso do SQLite, e de outras dezenas de banco de dados. Não é uma solução genérica.

O melhor artigo que eu encontrei sobre o assunto (ótimo, diga-se de passagem), inclusive com o passo a passo sobre como construir o seu próprio DbProviderFactory, está aqui:
http://msdn.microsoft.com/en-us/library/ms971499.aspx

Resumo sobre o DbProviderFactory:

  • Pontos Fortes:
    • Bem estruturado e elegante, baseado simplesmente no design pattern Factory;
    • Funciona bem e é suportado pelo Mono (http://www.mono-project.com/Compatibility);
    • Suportado desde a versão 2.0 do .NET Framework;
    • Pode ser utilizado até mesmo no Membership Provider (deve existir um DbProviderMembershipProvider).
  • Pontos Fracos:
    • Exige que o comando SQL seja especificado na propriedade CommandText;
    • Ajuda, mas não é a solução final para generalização de banco de dados.


2. Data Source Controls

Os Data Source Controls são abordados pelo mesmo artigo do MSDN mencionado acima para o DbProviderFactory. A sua grande vantagem em relação ao DbProviderFactory é que os comandos para CRUD já estão embutidos nos componentes (SqlDataSource, AccessDataSource, ObjectDataSource, XmlDataSource, e SiteMapDataSource).

Também é possível criar uma Factory manualmente para os Data Source Controls utilizando-se a interface IDataSource e um pouco de reflection para a criação dinâmica dos controles. No entanto, ele é bastante engessado no que diz respeito aos códigos para acesso aos dados (queries T-SQL por exemplo). No artigo o autor utiliza esses controles em uma camada que realmente não parece uma camada de acesso a dados. Me parece que eles foram feitos mais para prover dados de forma simples para páginas e grids.

Um grande ponto negativo desses controles é que a pesquisa no banco de dados é feita de forma assíncrona, isto é, você precisa especificar uma função de callback para tratar os dados retornados, o que deixa a idéia de utilizá-los em uma camada de acesso a dados ainda mais bizarra. No entanto, aqui vai o resumo sobre os Data Source Controls:

  • Pontos Fortes:
    • Facílimo de configurar/utilizar;
    • Pode ser generalizado para deixar o banco de dados completamente transparente para o sistema;
    • Suportado desde a versão 2.0 do .NET Framework;
    • Funciona bem e é suportado pelo Mono.
  • Pontos Fracos:
    • Performance um tanto quanto duvidosa pela falta de parâmetros para configuração do controle;
    • Comandos são executados de forma assíncrona (difícil utilização em camadas de acesso a dados);
    • Não oferece muito controle sobre os comandos executados (não suporta joins ou qualquer query mais complexa);
    • Os comandos precisam ser pré-embutidos nos controles (complexidade alta para criar, digamos, um SQLiteDataSource);
    • Não parece ser uma solução pensada para ser utilizada em uma camada de acesso a dados.


3. .NET Entity Framework

Voltamos ao .NET Entity Framework. Das três, esta é a tecnologia com a qual eu mais trabalhei. Como mencionei anteriormente, acabei de converter um sistema de complexidade média que foi desenvolvido utilizando o Entity Framework Provider Oracle para o SQL Server. Encontrei diversos entraves e problemas, mas grande parte deles foram por questões alheias à tecnologia em si. Pessoalmente, acredito que esta tecnologia seja a única resposta factível para a total abstração do banco de dados utilizado para uma aplicação .NET.

Antes de enumerar os pontos fortes e fracos da tecnologia, gostaria de enumerar os problemas que tive na troca do banco de dados do sistema de Oracle para SQL Server:

  • Várias chaves estrangeiras não estavam definidas (ou estavam definidas de outra forma) no banco de dados em relação ao modelo do Entity Framework (edmx). Isto fez com que diversas propriedades acessíveis através dessas chaves (ex: Funcionario.Perfis) não ficassem disponíveis e gerassem centenas de erro em tempo de compilação.
  • Vários tipos de campo foram alterados na conversão do banco de dados. Alguns campos que estavam definidos como NUMBER no Oracle foram remodelados para INT no SQL Server, o que fez com que o engine do Entity Framework transformassem todas as propriedades do tipo Decimal (baseadas em NUMBER) para o tipo Int32 (baseadas em INT). Isto também pode ter ocorrido por conta de o driver Entity Framework para Oracle ter sido feito por mim a partir de um exemplo na Internet e em tempo recorde. No entanto, o tipo NUMBER do Oracle aceita casas decimais e, portanto, nunca poderia ser traduzida como Int32 para o .NET. Este é um exemplo de como os DBAs precisarão, cada vez mais, manter a padronização de tipos entre bancos de dados diferentes (nas raras ocasiões em que isto for necessário).
  • Também precisei editar o XML do arquivo .edmx para alterar os tipos nativos de cada campo (VARCHAR2 para VARCHAR, NUMBER para INT/NUMERIC, etc). Não sei dizer, mais uma vez, se isto foi necessário por conta de algo que não foi implementado no meu driver EF para Oracle ou se isso é algo que realmente sempre precisará ser feito, ou ainda se isso foi algo que não estava implementado na versão 3.5 (beta) do Entity Framework e que agora na versão 4.0 foi implementada.

Bem, com isso fora do caminho, podemos partir para os pontos fortes e fracos do Entity Framewok:

  • Pontos Fortes:
    • Extremamente robusto e integrado ao Visual Studio;
    • Queries são geradas dinamicamente (feature que, pelo que dizem, melhorou muito na versão 4.0 e melhorou mais ainda na versão 4.5);
    • Abstrai completamente o banco de dados com o mínimo (ou talvez nenhum) esforço;
    • Possui uma extensão LINQ já desenvolvida pra ela (LINQ to Entities);
    • Parece ser o futuro (recebeu bastante atenção da equipe do ADO.Net na versão 4.0 e mais ainda na 4.5).
  • Pontos Fracos:
    • Ainda pouco difundida;
    • Na versão 3.5, não suporta trabalhar com POCO (Plain Old CLR Object).
    • Na migração de banco de dados, exige que o banco de dados possua tipos de dados compatíveis e não tenha mudanças em chaves estrangeiras (OK, é justo);
    • Não possui drivers gratuitos para muitos bancos (No entanto, existem drivers gratuitos para MySQL e SQLite);
    • Só é suportado pelo .NET Framework a partir da versão 3.5 SP1;
    • Não é suportado pelo Mono (http://www.mono-project.com/Compatibility).

Bem, é isso!

Abraços!

Advertisements

1 Comment

  1. Vou te dar mais um pra investigar: NHibernate. De qualquer forma, a solução pra mim seria abstrair não só os comandos DML (data manipulation language), mas também os comandos DDL (data definition language), criando uma camada de abstração para as definições do banco de dados.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s