O NeoMatrix Tech está de casa nova!

Você deverá ser redirecionado em 6 segundos. Se não, visite:
http://www.leonelfraga.com/neomatrixtech
e atualize seus favoritos.

Aviso IMPORTANTÍSSIMO!

Aviso aos navegantes:

O NeoMatrix Tech mudou de casa!!!

A partir de agora, acessem pelo novo endereço:

http://www.leonelfraga.com/neomatrixtech

Ué... mas é só o domínio mudou de lugar?

R: Na verdade, não é bem assim hehe. Este domínio que você acessa agora aponta para um blog hospedado no Blogger, enquanto no novo, aponta para um blog na plataforma Wordpress, hospedagem própria, muito mais rápida e com um layout mais agradável de ler ;)

Não vou fechar este domínio igual ao que eu fiz com o NM Light (que já está 100% na nova plataforma). Talvez beeeeeeem depois eu faça isso.

Todos os posts daqui se encontram lá, e novos posts serão colocados somente no novo endereço.
A única coisa que não consegui importar foram os comentários. Mas em breve vai ter um post contando sobre a epopéia que foi migrar o NeoMatrix Tech!

Somente vou fechar a área de comentários daqui. Caso queiram comentar, favor ver o post correspondente no "Novo NeoMatrix Tech" e comentem por lá. É bem melhor! (pena que os permalinks "amigáveis para SEO" não funcionam lá, dá erro 404 e não consigo fazer a configuração funcionar. E olha que eu já vi vários artigos falando desse assunto :( ).

Quem assina o feed, já está lendo o conteúdo do novo NeoMatrix Tech!

terça-feira, 17 de fevereiro de 2009

Nova Classe de Conexão para Vários Bancos de Dados – Visão Geral

Olá! Agora que a nova Classe de Conexão já está em uso em sistemas de produção, bombando e não dando nenhum tipo de problema, já chegou a hora de mostrá-la para você.

Quando eu mencionei que tinha uma classe do gênero em um dos tópicos do fórum do Meio Bit, fui questionado sobre o NHibernate e outros frameworks de persistência de dados.

Estaria eu reinventando a roda não usando algo pronto?

De certa forma, talvez. Se eu for levar em consideração o meu ego como programador, é simplesmente lindo você ver uma cria sua em funcionamento e ser adotada pela empresa em que você trabalha. Vários dos sistemas que são desenvolvidos lá em ASP.NET usam esta classe com os mais variados bancos de dados: Firebird, SQL Server e Oracle.

Vendo de outra forma, esta é a forma que eu encontrei para aprimorar meus conhecimentos na plataforma .NET e na linguagem C#, pois esta versão da classe usa um conceito que eu não havia utilizado antes, o Reflection.

Usando mais um ponto de vista na brincadeira, ao utilizar um framework de terceiros estarei perdendo um pouco o controle das rotinas do sistema. E se um bug desse framework me ferra uma parte do sistema? Não temos tempo de ficar olhando o código dele (isso se for de código aberto), pois o cliente quer o sistema para ONTEM. Já que a “Classe de Conexão” já está bem consolidada e FUNCIONA, nada melhor que utilizá-la!

Após o break, irei descrever as modificações de código, os campos, propriedades e métodos que foram acrescentadas desde a última versão da Classe de Conexão. Para saber o que já está implementado antes da alteração, leiam os artigos da categoria “Conexão com Banco de Dados”.

Na parte de otimização de códigos, passei a utilizar os Data Provider Factories, que são classes genéricas dos objetos ADO.NET, ao invés de instanciar a classe específica do provider através das diretivas de compilação.

Estas diretivas agora são utilizadas apenas no construtor da classe Conexao para alimentar o novo campo protegido _DataProvider:

   1: public Conexao()
   2: {
   3: #if FIREBIRD
   4:     TTipoBancoDados TipoBD = TTipoBancoDados.tbFireBird;
   5:     _DataProvider = "FirebirdSql.Data.FirebirdClient";
   6: #endif
   7: #if ORACLE
   8:     TTipoBancoDados TipoBD = TTipoBancoDados.tbOracle;
   9:     _DataProvider = "System.Data.OracleClient";
  10: #endif
  11: #if SQLSERVER
  12:     TTipoBancoDados TipoBD = TTipoBancoDados.tbSQLServer;
  13:     _DataProvider = "System.Data.SqlClient";
  14: #endif
  15: #if ODBC
  16:     TTipoBancoDados TipoBD = TTipoBancoDados.tbODBC;
  17:     _DataProvider = "System.Data.Odbc";
  18: #endif
  19: #if OLEDB
  20:     TTipoBancoDados TipoBD = TTipoBancoDados.tbOleDb;
  21:     _DataProvider = "System.Data.OleDb";
  22: #endif
  23: }

Veja como ficou o método AddSQLParam:

Antigo:

   1: protected void AddSQLParam(string pNomeParam, object pValor, ParameterDirection pDirecao)
   2: {
   3:     switch (TipoBD)
   4:     {
   5:         case TTipoBancoDados.tbFireBird:
   6:             {
   7:                 #if FIREBIRD
   8:                 this.SQLParams.Add(new FbParameter(pNomeParam, pValor));
   9:                 #endif                        
  10:                 break;
  11:             }
  12:         case TTipoBancoDados.tbOracle:
  13:             {
  14:                 #if ORACLE
  15:                 this.SQLParams.Add(new OracleParameter(pNomeParam, pValor));
  16:                 #endif                        
  17:                 break;
  18:             }
  19:         case TTipoBancoDados.tbSQLServer:
  20:             {
  21:                 #if SQLSERVER                        
  22:                 this.SQLParams.Add(new SqlParameter(pNomeParam, pValor));
  23:                 #endif                        
  24:                 break;
  25:             }
  26:         case TTipoBancoDados.tbOleDb:
  27:             {
  28:                 #if OLEDB                        
  29:                 this.SQLParams.Add(new OleDbParameter(pNomeParam, pValor));
  30:                 #endif                        
  31:                 break;
  32:             }
  33:         case TTipoBancoDados.tbODBC:
  34:             {
  35:                 #if ODBC                        
  36:                 this.SQLParams.Add(new OdbcParameter(pNomeParam, pValor));
  37:                 #endif                        
  38:                 break;
  39:             }
  40:     }
  41:     (this.SQLParams[SQLParams.Count - 1] as IDataParameter).Direction = pDirecao;
  42: }

Novo:

   1: protected void AddSQLParam(string pNomeParam, object pValor, ParameterDirection pDirecao)
   2: {
   3:     DbProviderFactory factory = DbProviderFactories.GetFactory(_DataProvider);
   4:     this.SQLParams.Add((IDataParameter)factory.CreateParameter());
   5:     (this.SQLParams[SQLParams.Count - 1] as IDataParameter).ParameterName = pNomeParam;
   6:     (this.SQLParams[SQLParams.Count - 1] as IDataParameter).Value = pValor;
   7:     (this.SQLParams[SQLParams.Count - 1] as IDataParameter).Direction = pDirecao;
   8: }

Com isso, reduzimos o código deste método de 42 para apenas 8 linhas.

Um detalhe importante e que não posso deixar passar batido, é que a nova Classe de Conexão é 100% compatível com a versão anterior, podendo substituir a anterior pela nova sem problemas.

O modo de compilação e alimentação da string de conexão continuam igual: colocar nas propriedades do projeto que utiliza a Classe de Conexão a diretiva referente ao(s) banco de dados utilizado e a string de conexão em uma chave chamada strConexao na seção appSettings do arquivo de configuração da aplicação (web.config, app.config).

Campo _ExcludeFieldsAutoIsql :

Este Arraylist é utilizado pelo novo método AutoIsql, e serve para excluir determinados campos de uma tabela da geração automática de instruções SQL de Insert e Update.
Os nomes dos campos que estiverem nesse ArrayList não serão incluidos no SQL.

Campo _TableName :

Serve para indicar o nome da tabela que uma classe derivada (lembra, para utilizarmos a Classe de Conexão precisamos criar uma classe filha?) irá representar no C# (a classe que faz o mapeamento BD –> Objeto, que representa uma tabela do BD).

Campo _DataKeys :

Um array de string, serve para guardar as chaves primárias da tabela informada no campo _TableName.

Campo _FormatProperties :

Utilizado pelo novo método BindToUI, este ArrayList armazena strings de formatação de uma propriedade que será aplicada em um textbox correspondente na interface de usuário (por enquanto, somente interface Web).

Deve ser informado em cada item do ArrayList o nome da propriedade (variável pública, com métodos get e set) da classe e a string de formatação, separados por ponto-e-vírgula. Exemplo:

“DataNascimento;{0:dd/MM/yyyy}”

Campo _ParameterDefinedBy :

Esta string indica o caractere definidor de parâmetro de instruções SQL parametrizadas. Esta constante é utilizada pelo método AutoIsql e BindObject2Parameters. Por exemplo, se for trabalhar com SQL Server, seu valor é “@”, e se for Oracle, “:”.

Campo _NotConvention2DBNull:

Este ArrayList serve para que os campos da tabela informadas nele não sofram a conversão (seguindo uma convenção que eu estipulei para facilitar a manipulação dentro da UI e de atribuição no objeto) de Zero (campos numéricos), String vazia e DateTime.MinValue para Nulo. Por exemplo, se um campo numérico for informado como zero na propriedade, eu costumo colocar o estado nulo no banco de dados, principalmente se o campo em questão for uma chave estrangeira, visto que eu nunca utilizo o valor zero como um identificador único (ou seja, se eu informar zero em uma PK, vai violar esta chave, visto que nunca existirá um ID igual a 0).

Método AlterSQLParamDirection:

Altera a direção de um parâmetro que esteja na coleção SQLParams. Utilizo esta função caso eu queira alterar um parâmetro criado via BindObject2Parameters antes de chamar a execução do SQL que usa este parâmetro.

Método AlterSQLParamValue:

Análogo ao AlterSQLParamDirection, altera o valor de um parâmetro da coleção SQLParams.

Médodo Select:

Opa… Este método já existe!

Sim, mas ele teve a sua funcionalidade extendida: Além de adicionar os campos da tabela na coleção ListaCamposTabela (usado se fomos ler os campos através da propriedade indexada this), agora também atribui AUTOMATICAMENTE os valores diretamente nas variáveis privadas da classe filha. Esta é a primeira benesse da aplicação do Reflection na nossa Classe de Conexão :-)

Só que para isto funcionar corretamente, as variáveis privadas da classe e que tenham seu correspondente na tabela (ou na instrução SQL em _SelectSQL) do banco de dados deverão ter seu nome IDÊNTICO ao campo da tabela precedido de underline. Exemplo:

Campo NOME, tipo varchar –> Variável correspondente: string _NOME;

Esta é o primeiro cuidado que devemos tomar para que possamos economizar código lá na frente. Logo mais temos outras convenções a seguir.

Ah, e também devemos ter o cuidado de não nomear campos de tabela com o nome de variáveis privadas da classe Conexao!

Método AutoIsql:

Gera instruções SQL parametrizadas para as operações de Insert, Update e Delete para a tabela informada na variável _TableName.

Este método pega os campos DIRETAMENTE da tabela do banco de dados, não necessitando de informação prévia sobre eles (somente das chaves primárias, leia parágrafo abaixo).

Nas instruções de Update e Delete, a cláusula Where das mesmas é montada através da informação fornecida na variável _DataKeys.

Ele monta instruções SQL parametrizadas, ou seja, os valores serão informados através de parâmetros. Estes valores serão atribuídos através do método BindObject2Parameters.

Método BindObject2Parameters :

Cria, a partir da tabela informada na propriedade _TableName, parâmetros para a atribuição de valores a partir das variáveis privadas correspondentes aos campos.

Funciona da seguinte forma: Lê o a tabela do BD, pega o primeiro campo, verifica nas variáveis privadas se há uma com o mesmo nome precedido de underline. Se existir, verifica se ele utiliza a “convenção de zero para nulo” e cria um item na coleção _SQLParams com este parâmetro já atribuído. E faz isso para cada campo da tabela.

Método ConverterDB2Obj:

Converte um tipo de Banco de Dados em um tipo do C#. Utilizado principalmente no caso do campo vir nulo da base de dados e a propriedade não for do tipo nulável.

Método BindToUI:

Uma das obras-prima dessa nova Classe de Conexão: Atribui as propriedades (com métodos get e set) da classe na interface de usuário.

Passamos como parâmetro a classe da página web (por enquanto, só trabalha com interfaces Web) que terá seus componentes atribuídos, as informações de seu tipo (método GetType() ) e um arraylist que contém os nomes dos componentes que não serão atribuídos caso haja correspondente na classe.

Para fazer a correspondência da propriedade da classe com o componente da interface de usuário, faremos a seguinte convenção na propriedade Name do componente:

Prefixo de 3 letras + Nome da Classe + Nome da Propriedade

Por exemplo, temos uma classe de nome Funcionario e queremos atribuir a propriedade Nome em um textbox. O nome do componente vai ficar assim: tbxFuncionarioNome.

Tá, poderão existir casos em que o ID de um componente vai ficar com um nome cavalar. Este é mais um preço que teremos que pagar pela comodidade de manutenção futura (imagina o trabalho que daria acrescentar um campo novo, procurar no SQL, fazer as atribuições na UI…).

Caso a propriedade seja indexada, informaremos, na convenção apresentada anteriormente, o índice da propriedade precedido de underline, depois do nome do controle, exemplo: Campo Telefone é um array com 5 posições e iremos atribuir a posição 0: tbxFuncionarioTelefone_0.

As seguintes propriedades serão atribuídas, dependendo do tipo de controle:

TextBox -> Text
DropDownList -> SelectedValue
RadioButton -> Checked (somente campos booleanos, inteiros: 0 -> false; > 0 -> true ou string: "S"/"SIM" -> true, "N"/"NÃO" -> false
Label -> Text
CheckBox -> Checked (somente campos booleanos ou numéricos: 0 -> false; > 0 -> true
CheckBoxList -> SelectedValue
RadioButtonList SelectedValue
Hidden Field (HTML Controls) –> Value

Embora tenha um método para pegar o prefixo dependendo do controle, ele não fará diferença, pois pegará a informação do tipo diretamente do controle, porém, o nome do componente deve ter um prefixo de 3 (três) e somente 3 letras.

Método BindFromUI:

Faz a operação inversa do método BindToUI: Atribui os valores dos controles da interface de usuário em seus correspondentes na classe. Vale as mesmas convenções.

Método ConverterObj2DB (Uso interno):

Converte um tipo de objeto C# em um compatível com banco de dados. Utilizado principalmente quando o objeto (variável) que iremos converter for nula.

Método UIControlPrefix (Uso interno):

Retorna um prefixo correspondente ao tipo de controle informado no parâmetro:

tbx -> TextBox
ddl -> DropDownList
rbt -> RadioButton
lbl -> Label
chk -> CheckBox
cbl -> CheckBoxList
rbl -> RadioButtonList
hdf -> Hidden Field (HTML Controls)

Método ContainFormatInfo (Uso interno):

Verifica se uma propriedade contém informações de formatação (checadas na coleção _FormatProperties).

Método HasIndexIdentifier (Uso interno):

Verifica se um nome de controle é associado a uma propriedade indexada (convenção dos metodos BindFrom (ou To) UI.

Métodos ExcelPlan2DataTable e DataTable2ExcelPlan:

Explicado com detalhes em artigos anteriores, converte DataTable em Planilha Excel e vice-versa.

Método CreateList:

Cria um objeto List<> a partir de uma instrução SQL.

Passamos como parâmetros o tipo de cada item da lista (método GetType() de sua classe), a classe do objeto List que queremos criar (método GetType() de um objeto List<MinhaClasse>, a instrução SQL de origem, e se esta instrução SQL possui parâmetros ou não.

Para funcionar corretamente, devemos seguir as convenções do método Select() da classe: nome de variável privada da classe = nome do campo no Select precedido de underline.

Ufa! Estes são os novos itens colocados nessa nova Classe de Conexão. No próximo artigo, teremos um exemplo em ASP.NET de um cadastro simples utilizando os novos conceitos, e uma sequência de artigos explicando alguns dos métodos mais interessantes desta classe, para aprendermos um pouquinho de Reflection e como ele é utilizado neste contexto.

Caso vocês já queiram se aventurar, faça o download abaixo e divirta-se ;-)

  Nova Classe de Conexão (19 KB).

Um abraço!

7 comentários:

Fernando,  11 de abr de 2009 23:23:00  

Você deve desconhecer orientação a objeto e interface. Te pouparia de ter que ficar usando #if FIREBIRD , #if ORACLE ...

Leonel Fraga de Oliveira 12 de abr de 2009 10:20:00  

@Fernando:

A primeira versão do programa realmente estava assim, mas nessa nova, somente uso as diretivas de conexão no construtor da classe Conexao para inicializar o Provider Factory (que eu desconhecia, realmente).

É vivendo e aprendendo ;-)

[]'s

Anônimo,  13 de mai de 2009 14:32:00  

Boa tarde Leonel, achei bem intessante apesar deu ñ ter um bom conhecimento em C#, vc poderia dar um exemplo de como usar sua classe e se pode ser usada no MONO?, desculpe por ser iniciante!!!

Att,
Lindomar

Anônimo,  13 de mai de 2009 14:33:00  

Boa tarde Leonel, achei bem intessante apesar deu ñ ter um bom conhecimento em C#, vc poderia dar um exemplo de como usar sua classe e se pode ser usada no MONO?, desculpe por ser iniciante!!!

Att,
Lindomar
lindomaronline@hotmail.com

Leonel Fraga de Oliveira 13 de mai de 2009 21:39:00  

@Lindomar

Obrigado por achar interessante o artigo! Quanto a exemplos de uso da Classe de Conexão, dê uma leitura na categoria "Simple PIM" do blog, lá tem um exemplo completo de utilização.

Quanto a rodar em Mono... esta aí uma coisa que eu não testei. Por usar Provider Factories, não sei se o Mono implementa estas classes do .NET Framework.

[]'s!

Anônimo,  22 de mai de 2009 11:37:00  

Bom dia Leonel, td bem?
Seguinte eu baixei a Classe Conexão para varios bando de dados e gostaria de saber como eu faço pra popular um DataGridView e um ListView (Winform). Se vc puder me ajuda t agradeço muito, e desculpe por ser iniciante!!!

Att,

Lindomar
lindomaronline@hotmail.com

Leonel Fraga de Oliveira 22 de mai de 2009 13:02:00  

@Lindomar:

Então, para popular uma lista, ou qualquer coisa, você deve:

- Criar uma classe herdada da Classe Conexão: public class ClasseHerdadaDeConexao : Conexao

- Nesta classe, fazer um método que retorne um DataTable (para ficar mais simples).
Exemplo: public DataTable meuMetodo()

- Neste método, chamar o método getTable() da classe Conexao, informando em seus parâmetros a query a ser executada e se ela é parametrizada (se for fazer com concatenação de strings, pode passar false): return this.getTable(meuSQL,false);

- Na aplicação, instanciar esta classe e fazer com que o DataSource do DataGridView, ou de qualquer outro controle seja este método, exemplo: MeuGrid.DataSource = MeuObjetoDaClasseHerdadaDeConexao.meuMetodo();
E dar o "DataBind" se for o caso (Windows Forms não tem o método DataBind, se eu não me engano :-).

Ah, e não se esqueça de compilar o projeto que usa a classe Conexao com a diretiva correspondente ao mecanismo de acesso a dados que vai utilizar :-)

E quanto a ser iniciante... eu também já fui,e gelei a primeira vez que tive que dar manutenção em uma aplicação ASP.NET, sendo que eu NUNCA tinha visto C# e cia :P

Abraços!


Postar um comentário

Para tornar este artigo ainda mais interessante, escreva suas críticas (desde que construtivas e sem ofenças), elogios, sugestões, complementos, dúvidas, etc, etc, etc!!!

  © Blogger templates ProBlogger Template by Ourblogtemplates.com 2008 - Editado e configurado por Leonel F.

Voltar ao TOPO