Neste artigo irei mostrar como criar um DLL de acesso à dados Genérica, ou seja, servirá para qualquer banco que for utilizado no seu projeto.

 

Para facilitar a vida dos desenvolvedores com o novo modelo de provedor ADO.NET 2.0 nós podemos criar classes genéricas para acessar vários tipos de banco com a utilização da classe DbProviderFactory que através do nome do provedor passado na string de conexão ele identifica qual o banco utilizado e a partir daí criamos as classes DbConnection e DbCommand.

Vamos à prática. Abra o Visual Studio 2005 e crie um novo projeto Class Library como mostra a figura 1:

 

Figura 1 ? Criando o Projeto

Após criar o projeto GenericDataBase exclua a classe que já vem como padrão Class1.cs do projeto e adicione uma nova pasta com o nome Configuration e dentro da pasta adicione uma nova classe com o nome ConnectionDB. Veja figura 2:

C:\Documents and Settings\u83659\Meus documentos\Artigo\GenericDB\img2.JPG

Figura 2 ? Adicionando uma Classe

 

Antes de programarmos e falarmos sobre esta classe vamos adicionar outra pasta com o nome GenericDB e dentro dela a classe GenericDB.

A classe ConnectionDB deve ser declarada como publica e est√°tica para que possamos ter acesso a ela e as suas propriedades sem ter que criar uma inst√Ęncia nova dela. Seu namespace deve ser alterado para GenericDataBase. Esta classe servir√° apenas para armazenar a string de conex√£o e o nome do provedor que ser√° utilizado no projeto. O c√≥digo completo desta classe segue logo abaixo:

using System;

using System.Collections.Generic;

using System.Text;

using System.Configuration;

 

namespace GenericDataBase

{

    /// <summary>

    /// Classe de configura√ß√£o da Conex√£o com o BD

    /// </summary>

    public static class ConnectionDB

    {

        /// <summary>

        /// Construtor Default

        /// </summary>

        static ConnectionDB()

        {

            try

            {

                // Recebe do arquivo de configura√ß√£o Web.Config a string de conex√£o e o nome do provedor

                connectionString = ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString;

                providerName = ConfigurationManager.ConnectionStrings["ConnectionString"].ProviderName;

            }

            catch

            {

                throw new Exception("Erro ao receber dados da Conex√£o. Por favor verifique se a string de conex√£o est√° declarada corretamente.");

            }        }

 

        /// <summary>

        /// Field String de Conex√£o

        /// </summary>

        private static string connectionString;

       

        /// <summary>

        /// Field Nome do Provedor

        /// </summary>

        private static string providerName;

 

        /// <summary>

        /// Propriedade que apenas informa a String de Conex√£o

        /// </summary>

        public static string ConnectionString

        {

            get

            {

                return connectionString;

            }

        }

 

        /// <summary>

        /// Propriedade que apenas informa o Nome do Provedor

        /// </summary>

        public static string ProviderName

        {

            get

            {

                return providerName;

            }

        }

    }

}

O código está todo comentado e isso deve ser uma obrigação de cada programador ao desenvolver seus códigos.

 

Veja que no construtor default da classe eu faço uso da Classe ConfigurationManager para pegar a string de conexão no arquivo Web.Config e para isso não devemos esquecer de declarar seu namespace no início da classe :

using System.Configuration;

 

Obs.: Para utilização desta DLL será necessário declarar a string de conexão no arquivo Web.Config no seu projeto como ConnectionString.

Antes de programarmos a classe GenericDB vamos definir quais os tipos de comando que a DLL irá executar no banco de dados. Para isso vamos criar uma nova classe no projeto com o nome TypeCommand. Altere a declaração de class para enum igual está o código abaixo:

namespace GenericDB

{

    /// <summary>

    /// Tipos de Commandos a serem

    /// executados no BD

    /// </summary>

    public enum TypeCommand

    {

        ExecuteNonQuery,

        ExecuteReader,

        ExecuteScalar,

        ExecuteDataTable

    }

}

Abra a classe GenericDB e faça sua declaração da mesma forma que a classe ConnectionDB (Public e Static). Segue o código da declaração da classe:

using System;

using System.Collections.Generic;

using System.Text;

using System.Data.Common;

using System.Data;

 

namespace GenericDataBase

{

    /// <summary>

    /// Classe de execu√ß√£o de comandos no BD

    /// </summary>

    public static class GenericDB

    {

        /// <summary>

        /// Construtor Default

        /// </summary>

        static GenericDB()

        {

            //

            // TODO: Add constructor logic here

            //

        }

    }

 

}

Vamos agora adicionar um método para criar os objetos DbConnection e DbCommand utilizando o Factory de acordo com o nome de provedor passada na string de conexão. O código deste método comentado segue logo abaixo:

/// <summary>

        /// M√©todo que Cria um objeto DBCommand com os dados

        /// da classe ConnectionDB utilizando Factory

        /// </summary>

        /// <returns>DBCommand criado</returns>

        public static DbCommand createCommand(String cmmdText, CommandType cmmdType, List<DbParameter> listParameter)

        {

            // Cria um novo factories de acordo com o nome do provedor

            DbProviderFactory factory = DbProviderFactories.GetFactory(ConnectionDB.ProviderName);

           

            // Cria um objeto espec√≠fico de conex√£o de acordo com o nome do provedor

            DbConnection conn = factory.CreateConnection();

           

            // Atribui a String de Conex√£o

            conn.ConnectionString = ConnectionDB.ConnectionString;

           

            // Cria um objeto command espec√≠fico de acordo com o nome do provedor

            DbCommand comm = conn.CreateCommand();

 

            // Atribui o comando Text

            comm.CommandText = cmmdText;

            // Atribui o tipo de comando

            comm.CommandType = cmmdType;

 

            // Se existir algum par√Ęmetro ele adiciona

            // ao comando

            if (listParameter != null)

            {

                foreach (DbParameter param in listParameter)

                {

                    // Adicionando o par√Ęmetro

                    comm.Parameters.Add(param);

                }

            }           

            // Retorna o comando criado

            return comm;

        }

 

Este m√©todo recebe tr√™s par√Ęmetros, o primeiro serve para receber um comando SQL ou o nome da stored procedure o segundo √© o tipo de comando (Stored Procedure ou Text) e o terceiro √© uma lista de par√Ęmetros. Irei mostrar depois como criar essa lista de par√Ęmetros, pois ser√° necess√°rio criar um m√©todo para criar os par√Ęmetros. N√£o esque√ßa de fazer refer√™ncia ao namespace System.Collections.Generic para poder fazer uso das Listas Gen√©ricas.

Repare o uso da classe DbProviderFactory. Com ela eu crio um factory recebendo da classe ConnectionDB o nome do provedor.

DbProviderFactory factory = DbProviderFactories.GetFactory(ConnectionDB.ProviderName);

E é através deste factory criado que eu crio os objetos de conexão

DbConnection conn = factory.CreateConnection();

e de comando

DbCommand comm = conn.CreateCommand();

Após criado o comando eu atribuo ao comando o comando a ser executado (cmmdText) e o tipo de comando (cmmdType).

Para adicionar os par√Ęmetros ao comando eu verifico se a lista n√£o √© nula. Se n√£o for eu percorro toda a lista de par√Ęmetros e adiciono cada um ao comando.

    // Se existir algum par√Ęmetro ele adiciona

            // ao comando

            if (listParameter != null)

            {

                foreach (DbParameter param in listParameter)

                {

                    // Adicionando o par√Ęmetro

                    comm.Parameters.Add(param);

                }

            }

Vamos agora ao m√©todo para criar par√Ęmetros. N√≥s sabemos que todo par√Ęmetro sempre tem o nome, tipo e valor, ent√£o vamos criar um m√©todo que recebe como par√Ęmetro essas tr√™s vari√°veis. Veja o m√©todo abaixo:

/// <summary>

        /// M√©todo respons√°vel por criar um Par√Ęmetro

        ///     <example>

        ///         List<DbParameter> param = new List<DbParameter>();

        ///         param.Add(criaParameter(nome, tipo, valor));

        ///     </example>

        /// </summary>

        /// <param name="nameParameter">Nome do Par√Ęmetro</param>

        /// <param name="typeParameter">Tipo do Par√Ęmetro</param>

        /// <param name="valueParameter">Valor do Par√Ęmetro</param>

        /// <returns>Par√Ęmetro preenchido</returns>

        public static DbParameter criaParameter(String nameParameter, DbType typeParameter, Object valueParameter)

        {

            // Cria um novo factories de acordo com o nome do provedor

            DbProviderFactory factory = DbProviderFactories.GetFactory(ConnectionDB.ProviderName);

 

            // Cria o Par√Ęmetro e add seu valores

            DbParameter param = factory.CreateParameter();

            param.ParameterName = nameParameter;

            param.DbType = typeParameter;

            param.Value = valueParameter;

 

            // Retorna o Par√Ęmetro criado

            return param;

        }

 

            Este m√©todo √© bem simples mas o principal detalhe √© que para criar o DbParameter eu devo utilizar o factory tamb√©m.

DbParameter param = factory.CreateParameter();

 

Depois √© s√≥ adicionar os valores ao par√Ęmetro e retorn√°-lo.

Na hora de utilizar a DLL você deverá fazer assim:

List<DbParameter> param = new List<DbParameter>();

param.Add(criaParameter(nome1, tipo1, valor1));

param.Add(criaParameter(nome2, tipo2, valor2));

param.Add(criaParameter(nome3, tipo3, valor3));

E assim pra quantos par√Ęmetros tiver.

Agora vamos ao √ļltimo m√©todo e o mais importante que √© o m√©todo de execu√ß√£o. Ele receber√° os mesmos par√Ęmetros do m√©todo createCommand e mais um que √© o tipo de comando a ser executado. Segue o m√©todo:

/// <summary>

        /// M√©todo que cria um comando e executa esse comando.

        /// </summary>

        /// <param name="cmmdText">String SQL ou StoredProcedure</param>

        /// <param name="cmmdType">Tipo de Commando (Text ou Stored Procedure</param>

        /// <param name="listParameter">Lista de par√Ęmetros</param>

        /// <param name="typeCmmd">Comando a ser executado (ExecuteNonQuery, ExecuteReader, ExecuteScalar, ExecuteDataTable)</param>

        /// <returns>Object</returns>

        public static Object executeCommand(String cmmdText, CommandType cmmdType, List<DbParameter> listParameter, TypeCommand typeCmmd)

        {

            // Cria comando com os dados passado por par√Ęmetro

            DbCommand command = CreateCommand(cmmdText, cmmdType, listParameter);

 

            // Cria objeto de retorno

            Object objRetorno = null;

 

            try

            {

                // Abre a Conex√£o com o banco de dados

                command.Connection.Open();

 

                switch (typeCmmd)

                {

                    case TypeCommand.ExecuteNonQuery:

                        // Retorna o n√ļmero de linhas afetadas

                        objRetorno = command.ExecuteNonQuery();

                        break;

                    case TypeCommand.ExecuteReader:

                        // Retorna um DbDataReader

                        objRetorno = command.ExecuteReader(CommandBehavior.CloseConnection);

                        break;

                    case TypeCommand.ExecuteScalar:

                        // Retorna um objeto

                        objRetorno = command.ExecuteScalar();

                        break;

                    case TypeCommand.ExecuteDataTable:

                        // Cria uma tabela

                        DataTable table = new DataTable();

                        // Executa o comando e salva os dados na tabela

                        DbDataReader reader = command.ExecuteReader();

                        table.Load(reader);

                        // Fecha o Reader

                        reader.Close();

                        // Retorna a tabela

                        objRetorno = table;

                        break;

                }

            }

            catch (Exception ex)

            {

                throw ex;

            }

            finally

            {

                if (typeCmmd != TypeCommand.ExecuteReader)

                {

                    // Sempre fecha a conex√£o com o BD

                    command.Connection.Close();

                }

            }

 

            return objRetorno;

        }

A primeira coisa a fazer neste método é chamar o método que criamos para criar o comando: DbCommand command = CreateCommand(cmmdText, cmmdType, listParameter);

Este mesmo método servirá para excutar todos os tipos de comando no banco de dados e para cada comando ele retorna um objeto diferente, veja tabela abaixo:

Comando

Objeto

ExecuteNonQuery

Inteiro

ExecuteReader

DbDataReader

ExecuteScalar

Objeto

ExecuteDataTable

DataTable

 

Veja que utilizo o switch case para verificar qual o tipo de comando e fa√ßo a execu√ß√£o. Ap√≥s a execu√ß√£o no bloco finally eu verifico se n√£o √© do tipo DbDataReader para poder fechar a conex√£o porque se eu fechar a conex√£o agora n√£o ser√° poss√≠vel utilizar o DbDataReader ap√≥s a consulta. Mas veja tamb√©m que para isso √© necess√°rio acrescentar no m√©todo ExecuteReader o par√Ęmetro CommandBehavior.CloseConnection que quer dizer que quando eu fechar o DbDataReader ele automaticamente fecha a conex√£o. Por isso √© importante observar que toda vez que voc√™ executar o comando ExecuteReader n√£o deve-se esquecer de fechar o DbDataReader ap√≥s o seu uso.

 

Bom agora basta apertar CTRL + SHIFT + B para compilar o projeto e se n√£o houver nenhum erro ser√° criado na pasta bin do projeto um arquivo chamado GenericDataBase.dll. Agora crie um novo projeto para poder fazer os testes e verificar o funcionamento desta dll.

Neste artigo n√£o irei mostrar o funcionamento dela para n√£o ficar extenso de mais. Mas logo irei publicar outro artigo com um exemplo de uso desta dll de acesso a dados. Para quem for fazer os testes segue exemplo de como utiliz√°-la:

Primeiramene deve adicionar a dll como referência da seguinte forma:

Clique com o bot√£o direito do mouse em cima do projeto e em Add Reference... Selecione a dll e clique em Ok.

Depois adicione no início da classe o uso do namespace GenericDataBase.

using GenericDataBase;

Ex1.:   GridView.DataSource = (DataTable)GenericDB.executeCommand("Nome da Procedure", CommandType.StoredProcedure, null, TypeCommand.ExecuteDataTable);

Ex2.: // Cria Lista de Par√Ęmetros

            List<DbParameter> param = new List<DbParameter>();

            // Adiciona os par√Ęmetros

            param.Add(GenericDB.criaParameter("@NOME", DbType.String, nome));

            param.Add(GenericDB.criaParameter("@ENDERECO", DbType.String, endereco));

            param.Add(GenericDB.criaParameter("@TELEFONE", DbType.String, telefone));

GenericDB.executeCommand("stpAddPessoa", CommandType.StoredProcedure, param, TypeCommand.ExecuteNonQuery);

Ex3.: // Cria string SQL

                    string selectSQL = "SELECT * FROM TBPESSOA WHERE ID = " + id;

                    // Executa o comando

                    DbDataReader dr = (DbDataReader)GenericDB.executeCommand(selectSQL, CommandType.Text, null, TypeCommand.ExecuteReader);

Este exemplo √© pequeno e bem simples e pode servir de in√≠cio para quem ainda est√° iniciando. Agora voc√™s podem acrescentar m√©todos que utilizam transaction, m√©todos de email, valida√ß√Ķes e muito mais de acordo com a necessidade e criatividade de cada um. Quero deixar claro tamb√©m que est√° dll n√£o √© uma regra dizendo que sempre tem que ser desse jeito. Um exemplo √© que para cada tipo de execu√ß√£o (ExecuteNonQuery, ExecuteReader, ExecuteScalar, ExecuteDataTable)  poderia ter sido criado um m√©todo diferente que retorne o seu objeto espec√≠fico ao inv√©s de um m√©todo s√≥ como foi feito neste artigo.

√Č isso ai espero ter acrescentado algum conhecimento a mais √† muita gente. Qualquer d√ļvida podem entrar em contato atrav√©s do email: ebenezer05@gmail.com

Abraços e até a próxima.