Bem, dando continuidade a nosso artigo sobre Persistência de dados com Reflection, iremos hoje implementar nossa classe de Persistência, para isso iremos utilizar Reflection.

 

Antes de começarmos a ver um pouco de reflection, vamos criar nossa tabela, na qual iremos trabalhar nosso projeto.

 

Crie uma tabela chamada CLIENTE, voc√™ poder√° utilizar qualquer banco de dados que queira, pois este artigo ir√° abordar utilizar uma forma ‚Äúgen√©rica‚ÄĚ de acesso a dados, por√©m n√£o √© foco de nosso artigo, ficando para um pr√≥ximo artigo.

 

 

Para os que utilizarem o SQL Server, segue abaixo o código para gerar a tabela:

 

CREATE TABLE [CLIENTE]

(

         [ID] [int] IDENTITY (1, 1) NOT NULL ,

         [NOME] [varchar] (80) COLLATE Latin1_General_CI_AS NOT NULL ,

         [EMAIL] [varchar] (50) COLLATE Latin1_General_CI_AS NULL ,

         [TELEFONE] [varchar] (13) NULL

)

GO

 

Feito isso, vamos ver quem é esse tal de Reflection!

 

Para aqueles que já programaram em Java, Reflection não será uma novidade, para nós do .Net é um assunto que merece uma atenção maior, por isso, volto a frisar, este artigo irá mostrar o básico, para mais detalhes procure na Net tudo sobre Reflection, pois vale a pena conferir.

 

Definindo: reflection ou reflex√£o, √© o processo de obter informa√ß√Ķes sobre assemblies e os tipos definidos dentro dele, e ainda a cria√ß√£o, invoca√ß√£o e acesso √†s inst√Ęncias de tipos em tempo de execu√ß√£o. Esta √© uma defini√ß√£o segundo o SDK do Net Framwork. Reflection, nos permite inspecionar programaticamente uma montagem e obter informa√ß√Ķes sobre ela, assim como seus tipos e atributos. N√≥s tamb√©m podemos criar as nossas pr√≥prias montagens e tipos utilizando os servi√ßos dispon√≠veis na class System.Reflection.Emit. Tudo que voc√™ precisa para trabalhar com Reflex√£o est√° na class System.Reflection.

 

Parece meio confuso, mas vocês verão que não é tanto assim!

 

Para entendermos melhor, vamos mostrar alguns exemplos bem simples de Reflection antes de partirmos para o que realmente nos interessa assim vocês entenderão o que é Reflection.

 

Come√ßaremos com um exemplo que ir√° exibir os Construtores de uma montagem qualquer. Montagem ou assembly √© um programa feito em .Net, exibido como uma unidade √ļnica, ou seja, tudo que voc√™ faz em .Net e empacota √© uma montagem. Exemplo, uma class, uma dll, um execut√°vel, tudo isso √© uma montagem.

 

 

- Crie um projeto do tipo Console Application, defina seu nome como Reflection;

- Faça uma chamada ao namespace System.Reflection: using System.Reflection;

- Agora digite todo o código abaixo.

 

Observem que criamos uma class a parte, chamada Teste, para que possamos testar nosso método.


 

Não é necessário criarmos classes para testar, podemos, ao invés de verificarmos o tipo Teste, verificarmos System.Object, System.Int32 ou até mesmo uma class que vocês tenham criado.

 

 

using System;

using System.Collections.Generic;

using System.Text;

using System.Reflection; //Biblioteca respons√°vel pela reflex√£o

 

namespace Reflection

{

    class Program

    {

         static void Main( string[] args )

         {

              //Tipo a ser pesquisado

              Type tipo = typeof( Teste );     //Podemos substituir a class Teste por qualquer

//outra class que exista no Framework

 

              //Chama o m√©todo exibeConstrutores(Type tipo), passando o tipo para ele

              exibeConstrutores( tipo );

 

              //Aguardando o precionar de uma tecla

              Console.Read( );

         }

 

         /// <summary>

         /// M√©todo respons√°vel por exibir os contrutores da class

         /// </summary>

         static void exibeConstrutores( Type tipo )

         {

              //Respons√°vel por armazenar os construtores encontrados

              ConstructorInfo[] cInfo = tipo.GetConstructors( );

 

              //Exibe a quantidade de construtores da class

              Console.WriteLine( "Construtor(es) encontrado(s): " + cInfo.Length );

              Console.WriteLine( "" );

 

              //Varre nosso Objeto, e exibe os construtores

              foreach( ConstructorInfo ci in cInfo )

              {

                   Console.WriteLine( "Nome Construtor: " + ci.Name );

 

                   //Recupera os par√Ęmetros dos construtores encontrados

                   ParameterInfo[] param = ci.GetParameters( );

 

                   //Testa sen√£o est√° vazio

                   if( param.Length > 0 )

                   {

                        Console.WriteLine( "" );

                        Console.WriteLine( "Parametro(s) encontrado(s): " + param.Length );

 

                        //Varre a Collection de parametros

                        foreach( ParameterInfo p in param )

                        {

                            Console.Write( p.Name + " ");

                            Console.WriteLine( "" );

                        }

                   }

              }

         }

    }

 

    class Teste

    {

         public Teste( )

         {

              //Vazio

         }

 

         public Teste( string nome )

         {

              //Um par√Ęmetro

         }

 

         public Teste( string nome, int valor )

         {

              //Dois par√Ęmetros

         }

    }

}

 

Vamos a alguams defini√ß√Ķes, para que nosso artigo n√£o fique t√£o b√°sico assim, os dois primeiros itens(ConstructorInfo e ParameterInfo) n√≥s utilizamos, o restante fica a cargo de voc√™s testarem.

 

ConstructorInfo[] ‚Äď Recebe uma Collection contendo todos os construtores encontrado na class na qual realizamos a pesquisa;

 

ParameterInfo[] ‚Äď Recebe uma Collection contendo os par√Ęmetros encontrados em cada construtor que foi localizado na class que pesquisamos;

 

FieldInfo ‚Äď Recebe informa√ß√£o sobre os campos contido na class;

 

PropertyInfo ‚Äď Recebe informa√ß√Ķes sobre as propriedades na class;

 

MethodInfo ‚Äď Recebe informa√ß√Ķes sobre os m√©todos contidos na class.

 

Estas s√£o algumas das funcionalidades que podemos encontrar na class System.Reflection, para mais informa√ß√Ķes consulte o SDK do .Net Framework, √© uma fonte e tanto para pesquisa. Mas para aqueles que ainda n√£o dominam o ingl√™s, segue o link do mesmo material em portugu√™s: http://msdnwiki.microsoft.com/pt-br/mtpswiki/default.aspx

 

Segue abaixo um outro exemplo de Reflection, desta vez vamos invocar um m√©todo de nossa class Teste, para isso efetue algumas modifica√ß√Ķes na class Teste, para que ela fique como abaixo:

 

      class Teste

      {

            public Teste( )

            {

                  //Vazio            

            }

 

            public void Escreve( string nome )

            {

                  Console.WriteLine( "Unico parametro: " + nome );

            }

 

            public void Escreve( string nome, int valor )

            {

                  Console.WriteLine( "Dois parametros, nome: " + nome + ", valor: " + valor.ToString( ) );

            }

      }

 

Agora, vamos ver o código responsável por invocar nossos métodos da class Teste.

Cole o código abaixo logo após as chamadas ao construtor, dentro do bloco Main de nossa aplicação.

 

//Faz referencia a class a ser carregada dinamicamente

Assembly myClass = Assembly.GetAssembly( typeof( Teste ) );

 

//Cria o objeto a ser invocado

//Observem que o objeto vem acompanhado de seu namespace, que no nosso caso é Reflection

//Ms poder√° ser qualquer um, isso ir√° variar conforme o seu projeto.

object myObj = myClass.CreateInstance( "Reflection.Teste" );

 

//Invoca o método Escreve da class teste

myObj.GetType( ).InvokeMember( "Escreve", BindingFlags.InvokeMethod, null, myObj, new object[] { "Escrevendo com Reflection", 5 } );

 

Caso queiram, basta mudar os par√Ęmetros para que nosso c√≥digo invoque o outro m√©todo, com um parametro apenas. Fa√ßa o teste, crie novos metodos e tente invoc√°los.

 

 

Pois bem, acho que j√° d√° pra ter uma id√©ias das possibilidades que temos com Reflection, vimos como come√ßar a utilizar reflection em nossas aplica√ß√Ķes, tendo como exemplos os c√≥digos acima, agora vamos voltar a nossa class de Persist√™ncia.


 

Vamos começar criando nossa classe que irá servir como base para funcionamento da class persistência.

 

Esta class deve conter os atributos personalizados que criamos no artigo anterior. Vejam o código abaixo:

using System;

using System.Collections.Generic;

using System.Text;

using Persistencia;

 

namespace Pessoa

{

    [Table( "CLIENTE" )]

    public class Cliente : PersistClass

    {

         //Vari√°veis privadas

         private int _id;

         private string _nome;

         private string _email;

         private string _telefone;

 

         /// <summary>

         /// Construtor padr√£o

         /// </summary>

         public Cliente( ){ }

 

 

         [Key( "ID" )]

         public int ID

         {

              get { return _id; }

              set {  _id = value; }

         }

 

         [Column( "NOME", ColumnType.String, 50 )]

         public string Nome

         {

              get { return _nome; }

              set { _nome = value; }

         }

 

         [Column( "EMAIL", ColumnType.String, 50 )]

         public string Email

         {

              get { return _email; }

              set { _email = value; }

         }

 

         [Column( "TELEFONE", ColumnType.String, 13 )]

         public string Telefone

         {

              get { return _telefone; }

              set { _telefone = value; }

         }

    }

}

 

 

Como vocês podem ver, fizemos uma chamada ao namespace Persistencia, de inicio ele não está disponível pois não criamos a class Persist ainda, sendo assim não se preocupe, ele estará disponível assim que terminarmos o artigo, mas para irmos adiantando, basta criar a class como acima.

 

Vamos √†s explica√ß√Ķes detalhadas sobre a class.

 

Essa parte de código é responsável por nos informar o nome da tabela na qual iremos utilizar no banco. Existe a possibilidade de não termos que criar atributos, mas eles irão nos dar maior flexibilidade futuramente, conforme formos melhorando nossa classe.

 

[Table( "CLIENTE" )]

public class Cliente : PersistClass

 

Logo ap√≥s, temos a informa√ß√£o sobre a Chave Prim√°ria e sobre as Colunas no banco, observem que os nomes em nosso atributo personalizado corresponde exatamente com o nome das colunas no banco de dados, isso √© muito importante. Nosso atributo Column, como voc√™s perceberam recebe tr√™s par√Ęmetros, nome da coluna, tipo e tamanho.

 

[Key( "ID" )]

public int ID

 

[Column( "NOME", ColumnType.String, 50 )]

public string Nome

 

[Column( "EMAIL", ColumnType.String, 50 )]

public string Email

 

[Column( "TELEFONE", ColumnType.String, 13 )]

public string Telefone

 

Ok, feito isso, temos nossa classe que ir√° ser instanciada e salva no banco de dados.

Vamos agora partir para a parte legal da coisa!!

 

Antes de come√ßarmos, vamos rever duas instru√ß√Ķes SQL que ser√£o de m√°xima importancia para n√≥s, s√£o elas:

‚ÄúINSERT‚ÄĚ e ‚ÄúUPDATE‚ÄĚ

 

Para que possamos inserir um registro em um banco de dados, fazemos da seguinte forma:

 

‚Äúinsert into tabela (campo1, campo2,...) values (value1, value2,...)‚ÄĚ.

 

Pois bem, desta forma conseguiremos inserir um registro, a partir deste contexto iremos ‚Äúformatar‚ÄĚ nossas instru√ß√Ķes dinamicamente, a ponto de conseguir monta-las via string.Format() e utilizar Reflection para nos retornar os dados necess√°rios. Como? Vejam o c√≥digo abaixo:

 

private static string _insert = "INSERT INTO {0} ({1}) VALUES({2})";

 

Esta instru√ß√£o nada mais √© do que nossa instru√ß√£o pr√© formatada, a paritr dela utilizaremos m√©todos que ir√£o substituir os n√ļmeros entre chaves, pelos campos e valores necess√°rios. Sendo assim, vamos por partes.


 

Primeiro vamos criar um método que nos retorne o nome da tabela. Observem o código abaixo:

 

            /// <summary>

            /// Recupera o nome da tabela (class)

            /// </summary>

            /// <param name="type">Objeto</param>

            /// <returns></returns>

            string getTableName( System.Type type )

            {

                  object[] obj = type.GetCustomAttributes( typeof( TableAttribute ), true );

                  return ( (TableAttribute)obj[0] ).TableName;

            }

 

Este código é bem simples e já estamos utilizando Attribute e Reflection, reparem que nosso método recebe um tipo, que no caso será a nossa classes que contem os dados a serem gravados (class Cliente).

Logo depois como não sabemos o tipo do objeto, criamos um array de objects para receber os atributos que criamos, que no caso são do tipo TableAttribute , esse retorno nada mais é do que o nome da tabela que informamos quando criamos a classe:

 

[Table( "CLIENTE" )]

public class Cliente : PersistClass

 

Pronto, já temos um método que lê uma classe e nos retorna um atributo, caso ela possua. Viu, nada complicado!

 

Vamos agora buscar os ‚Äúcampos‚ÄĚ que iremos trabalhar, nesse caso o m√©todo ir√° retornar algo assim: ‚ÄúNOME, TELEFONE, EMAIL‚ÄĚ, que nada mais √© do que os atributos das propriedade da classe Cliente. Basta observarem os comentarios para entenderem o que cada pedacinho de c√≥digo faz.

 

            /// <summary>

            /// Recupera os campos da tabela (class)

            /// </summary>

            /// <param name="type">Objeto</param>

            /// <returns></returns>

            static string getColumns( System.Type type )

            {

                  //Cria uma string vazia

                  string columns = string.Empty;

 

                  //Faz uma varredura nas propriedades da class e as joga dentro

                  //de um objeto do tipo PropertyInfo

                  foreach ( PropertyInfo prop in type.GetProperties( ) )

                  {

                        //Criamos um array de objects selecionando apenas as pripriedades

                        //que est√£o marcadas com o atributo do tipo ColumnAttribute

                        object[] cols = prop.GetCustomAttributes( typeof( ColumnAttribute ), true );

 

                        //Verificamos sen√£o esta vazio nosso objeto

                        if ( cols.Length == 1 )

                        {

                             //Para que possamos chamar nosso atributos pelo nome

                             //devemos criar um objeto do seu tipo, ColumnAttribute

                             //e dizer ao objeto cols[0] que ele ser√° do tipo ColumnAttribute

                             ColumnAttribute ca = cols[0] as ColumnAttribute;

 

                             //Assim podemos chamar nosso objeto pelo nome

                             //no caso ColumnName

                             columns += ca.ColumnName + ", ";

                        }

                  }

 

                  //Este retorno se d√° com objeto - 2, porque nossa instru√ß√£o

                  //concatenou uma virgula e um espaco no final, ficando assim:

// nome, telefone, email, - daí temos que remover essa vírgula e este espaço

                  return columns.Remove( columns.Length - 2 );

 

Novamente, graças ao fabuloso Reflection, conseguimos recuperar os campos que iremos gravar no banco de dados. Mas atenção, são OS CAMPOS, e não os valores a serem adicionados.

 

Agora que já temos nosso método que retorna o nome da tabela, o método que retorna os campos da classe, nos falta implementar o método que retorna os dados a serem gravados.

Este método é bem parecido com o método para recuperar os campos. Obervem o código abaixo:

 

            /// <summary>

            /// Recupera os valores dos campos da tabela (class)

            /// </summary>

            /// <param name="type">Objeto</param>

            /// <returns></returns>

            string getValues( )

            {

                  //Criamos uma string vazia

                  string values = string.Empty;

 

                  //Varremos a classe diretamente instanciada, isso porque nossa classe cliente

                  //herdou de Persist, assim podemos cham√°-la diretamente com this.GetType()

                  foreach ( PropertyInfo field in this.GetType( ).GetProperties( ) )

                  {

                        //Coloquei esse if propositalmente, isso porque no inicio do artigo

                        //havia comentado que como estamos utilizando atributos temos uma maior

                        //flexibilidade, nesse caso, podemos testar qual o tipo da coluna

                        //para que possamos format√°-la adequadamente na instru√ß√£o colocando aspas

                        //para strings, ## para datas no Acces, etc..etc..

                        if ( field.GetValue( this, null ).GetType( ) == typeof( String ) )

                        {

                             //Fazemos a concatena√ß√£o normalmente

                             values += "'" + field.GetValue( this, null ) + "', ";

                        }

                  }

 

                  //Removemos o espa√ßo e a virgula e retornamos a sente√ßa de valores

                  return values.Remove( values.Length - 2 );

 

Ok, nossa classe de persistência está quase completa, devemos implementar agora nossa método que será responsável por receber um objeto e graválo em nosso banco.

Eu havia dito que explicaria sobre ‚Äúconex√£o gn√©ricas‚ÄĚ, mas vamos deixar para um pr√≥ximo artigo, vamos apenas nos concentrarmos em nossa classe de persist√™ncia.

 

Vejamos, agora que temos todos os metodos que varrem um objeto e nos retorna o tipo de dado que precisamos, basta apenas nós criamos uma conexão com o banco e formatar nossa string insert para finalizarmos.

 

Vamos l√°?


            /// <summary>

            /// Atualiza os dados no banco

            /// </summary>

            /// <returns></returns>

            public Boolean Salvar( )

            {

                  factory = DbProviderFactories.GetFactory( getProviderName( "conString" ) );

                  con = factory.CreateConnection( );

                  con.ConnectionString = getConnectionStringsFromConfigFile( "conString" );

                  cmd = con.CreateCommand( );

                  cmd.CommandText = string.Format( _insert, getTableName( this.GetType( ) ), getColumns( this.GetType( ) ), getValues( ) );

                  cmd.CommandType = System.Data.CommandType.Text;

                  cmd.Connection = con;

 

                  try

                  {

                        con.Open( );

                        int total = cmd.ExecuteNonQuery( );

                        if ( total >= 1 )

                        {

                             return true;

                        }

                        else

                        {

                             return false;

                        }

                  }

                  catch ( DbException dbE )

                  {

                        throw new ArgumentException( "Erro ao criar DbFactory: " + dbE.Message );

                  }

                  finally

                  {

                        con.Close( );

                  }

 

 

Este método não tem nada demais, a não ser a class DbProviderFactories que é responsável por criar nossa conexão com o banco.

 

A parte de maior atenção é essa:

 

cmd.CommandText = string.Format( _insert, getTableName( this.GetType( ) ), getColumns( this.GetType( ) ), getValues( ) );

 

Esta √© a parte onde recebemos nossa instru√ß√£o insert pr√© formatada e depois damos forma a ela. Como par√Ęmetros para a formata√ß√£o temos:

 

_insert ‚Äď instru√ß√£o insert pre formatada;

 

getTableName( this.GetType( ) ) ‚Äď respons√°vel por retornar o nome de nossa tabela;

 

getColumns( this.GetType( ) ) ‚Äď respons√°vel por retornar os campos da class, ou seja, os campos que criamos no banco de dados;

 

getValues( ) ‚Äď retorna os dados que informamos ao nosso objeto do tipo Cliente.

 


Agora que temos nossa classe pronta para inserir um registro, vamos a parte mais complicada de nosso artigo, a instanciação do objeto..rsrs!!!

 

- Para isso, crie um novo projeto do tipo ConsoleApplication;

- Faça uam chamada ao namespace Persistencia;

- Insira o código abaixo no bloco Main() de nosso novo projeto

- Rode a aplicação.

 

using Persistencia;

 

                  Cliente c = new Cliente( );

                  c.Nome = "Jos√© da Silva";

                  c.Email = "jose@da.silva.com.br";

                  c.Telefone = "(99)9999-9999";

                  bool ok = c.Salvar( );

                  if ( ok )

                  {

                        Console.WriteLine( "Cadastrado com sucesso!" );

                  }

                  else

                  {

                        Console.WriteLine( "Erro ao cadastrar!" );

                  }

 

Pronto, seus dados serão gravados sem o menor problema no banco!! Não vou dizer que foi tão simples assim, mas a partir desta idéia vocês podem criar outras, implementar mais a classe de persistência. O que vale é a criatividade.

 

Caso queiram testar mais campos, basta incluir o campo no banco de dados e depois na classe cliente, n√£o precisando se preocupar com StoredProcedures, instru√ß√Ķes SQL nem nada, criou, inseriu!

 

Fazendo o download do projeto completo, voc√™s ir√£o encontrar uma implementa√ß√£o para update, onde passamos o ID e atualizamos o registro no banco, da mesma forma, sem nos preocuparmos com instru√ß√Ķes.

 

Existe tamb√©m um arquivo App.config, em nosso projeto, mas como falei, a parte de acesso a dados ‚Äúgenericamente‚ÄĚ vamos deixar para um proximo artigo, assim poderemos analizar bem as classes base do framwork (Dbxxxxxx) de onde se originam SqlConnection, OracleConnection, OdbcConnection, etc. Essa parte merece um artigo bem detalhado, pois assim como Reflection, podemos criar muita coisa a nosso favor.

 

Espero que tenham gostado!

 

Aguardo sugest√Ķes, e para aqueles que quiserem, fa√ßam modifica√ß√Ķes, implementa√ß√Ķes, crie, use a imagina√ß√£o e depois entre em contato, podemos criar muitas coisas legais e ao mesmo tempo ajudar a outros programadores.

Mas nunca se esqueçam de dar os méritos a quem de direito.

 

Para montar e apreder sobre, estudei vários códigos e sites, vou colocar aqui alguns onde encontrei informação muito boa.

Para baixar o código fonte, clique aqui.

 

www.msdn.microsoft.com/library

www.codeproject.com

livro: Beginning C# programado

 

Abraço a todos, e até o próximo artigo.

 

 

Luciano Lima.