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:
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
{
///
/// Classe de configuração da Conexão com o BD
///
public static class ConnectionDB
{
///
/// Construtor Default
///
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.");
} }
///
/// Field String de Conexão
///
private static string connectionString;
///
/// Field Nome do Provedor
///
private static string providerName;
///
/// Propriedade que apenas informa a String de Conexão
///
public static string ConnectionString
{
get
{
return connectionString;
}
}
///
/// Propriedade que apenas informa o Nome do Provedor
///
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
{
///
/// Tipos de Commandos a serem
/// executados no BD
///
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
{
///
/// Classe de execução de comandos no BD
///
public static class GenericDB
{
///
/// Construtor Default
///
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:
///
/// Método que Cria um objeto DBCommand com os dados
/// da classe ConnectionDB utilizando Factory
///
/// DBCommand criado
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:
///
/// Método responsável por criar um Parâmetro
///
/// List param = new List();
/// param.Add(criaParameter(nome, tipo, valor));
///
///
/// Nome do Parâmetro
/// Tipo do Parâmetro
/// Valor do Parâmetro
/// Parâmetro preenchido
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 param = new List();
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:
///
/// Método que cria um comando e executa esse comando.
///
/// String SQL ou StoredProcedure
/// Tipo de Commando (Text ou Stored Procedure
/// Lista de parâmetros
/// Comando a ser executado (ExecuteNonQuery, ExecuteReader, ExecuteScalar, ExecuteDataTable)
/// Object
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.