Usando factories de Provedores

José Carlos Macoratti (e-mail) é referência em Visual Basic no Brasil e autor dos livros "Aprenda Rápido: ASP" e "ASP, ADO e Banco de Dados na Internet". Mantenedor do site macoratti.net.

Dentre as inúmeras novidades que a plataforma .NET trouxe na versão 2.0 com relação ao acesso a dados, temos o novo conceito de provider e de factory.

Até o momento, quando pretendíamos efetuar uma conexão com uma base de dados, criávamos os objetos diretamente. Assim, para acessar o SQL Server, usávamos as classes SqlConnection, SqlDataAdapter, SqlXXX; para acessar uma base de dados Access usávamos os provedores OleDbConnection, OleDbDataAdapater, OleDbCommand, OleDbXXX, e assim por diante; ou seja, tínhamos uma série de classes específicas de provedores que eram usadas conforme a fonte de dados a ser acessada.

O problema é quando desejamos poder acessar tanto uma base SQL Server, como uma base Oracle ou Access usando código genérico. Isto era possível pois cada classe implementava uma interface genérica IDbConnection, IDbDataAdapter, IDbCommand, etc.

O novo modelo de provedor ADO.NET 2.0 esta baseado em um série de classes base no namespace System.Data.Comom. A classe DBProviderFactories permite a realização de dois tipos de tarefas:

01. Obter uma lista de todos os provedores existentes, via método estático GetFactoryClasses;

02. Criar uma instância de um determinado Factory conforme o seu tipo, via método GetFactoryClass;

Uma classe base de provedor é um objeto factory que é usado para criar um conjunto de objetos relacionados como SqlConnection e SqlCommand. Eles retornam um tipo de classe abstrata do tipo DBConnection. As classes de provider factory são derivadas de uma classe base abstrata: System.Data.Common.DbProviderFactory, e, para cada tipo de base de dados a ser acessado, temos uma nova classe derivada desta classe base abstrata. A classe DbDataProvider define o número de funções  que esta subclasse precisa implementar:

CreateComand()

Cria um objeto Command derivado de DBCommand.

CreateCommandBuilder()

Cria um objeto derivado de DbCommandBuilder

CreateConnection()

Cria um objeto derivado de DbConnection.

CreateConnectionStringBuilder()

Cria um objeto derivado de DbConnectionStringBuilder

CreateDataAdapter()

Cria um objeto derivado de DbDataAdapter.

CreateDataSourceEnumerator()

Cria um objeto derivado de DbDataSourceEnumerator.

CreateParameter()

Cria um objeto derivado de DbParameter.

CreatePermission()

Cria um objeto derivado de CodeAccessPermission,

Funções DbDataProvider

Desde que o DbProviderFactory apropriado foi criado, a função na tabela acima pode ser usada para criar o objeto apropriado ao invés de usar o operador New, como anteriormente. (Para determinar todas as classes DbProviderFactory disponíveis e criar suas classes adicionais é fornecido a classe System.Data.Common.DbProviderFactories).

Cada provedor de dados faz o registro de uma classe ProviderFactory no arquivo machine.config da plataforma .NET. A classe base DbProviderFactory e a classe ProviderFactories podem retornar um DataTable de informações sobre os diferentes provedores registrados no arquivo machine.config, e podem recuperar a ProviderFactory conforme a sequência do provedor fornecida ou um DataRow de um DataTable.

Com base nisto podemos descobrir quais os provedores de dados instalados usando o código abaixo:

Imports System.Data.common

Public Class Form1

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click

Dim factory As DbProviderFactory = Nothing
Dim dtFactories As DataTable = DbProviderFactories.GetFactoryClasses()
Dim vetor As ArrayList = New ArrayList
Dim i As Integer = 0

For Each drFactory As DataRow In dtFactories.Rows
  Try
     factory = DbProviderFactories.GetFactory(drFactory)
     vetor.Add(factory)
     ListBox1.Items.Add(vetor(i))
     i = i + 1
  Catch ex As Exception
     factory = Nothing
  End Try
Next
End Sub
End Class

semtitulo3.gif
Exibindo os provedores de dados instalados

No código acima, DbProviderFactories.GetFactoryClasses() retorna um DataTabe com todos os possíveis provedores de dados.

Usamos DbProviderFactories.GetFactory para criar um DbProviderFactory baseado no nome da DataRow do DataTable obtido acima.

Poderíamos usar o código abaixo para tentar efetuar a conexão com a base de dados MSDE. Como o provedor de dados é o Data.SqlClient, haveria 3 tentativas frustradas com exibição de mensagem de erros, pois a string de conexão usada refere-se ao provedor SqlClient que é o último data provider registrado no machine.config.

Imports System.Data.common

Public Class Form1

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click

Dim factory As DbProviderFactory = Nothing
Dim conn As DbConnection = Nothing
Dim dtFactories As DataTable = DbProviderFactories.GetFactoryClasses()
Dim i As Integer = 0

For Each drFactory As DataRow In dtFactories.Rows
Try
   factory = DbProviderFactories.GetFactory(drFactory)
   conn = factory.CreateConnection()
   conn.ConnectionString = "Integrated Security=SSPI;Persist Security Info=False;Initial Catalog=Northwind; _
   User ID=sa;password=;Data Source=MACORATI\VSDOTNET"
   conn.Open()
   conn.Close()
   conn = Nothing
    Exit For
Catch ex As Exception
   conn = Nothing
   factory = Nothing
   MsgBox(ex.Message)
End Try
Next
End Sub
End Class

Vamos mostrar um exemplo onde iremos trabalhar com classes de uma base de dados sem definirmos o tipo das classes. Vamos preencher um DataGridViw com os dados da tabela Orders do banco de dados Northwind do MSDE (compatível com SQL Server).

Obs: Utilize uma string de conexão compatível com a sua base de dados.

Crie um novo projeto no Visual Basic 2005 Express com o nome de acessoBD, e, no formulário principal, inclua um componente DataGridView (dgv1) e um botão de comando (btnPreencherGrid), conforme figura abaixo:

semtitulo2.gif
Defina o seguinte import no código :

Imports System.Data.Common

Agora, clique duas vezes no botão de comando do formulário e inclua o seguinte código:

Private Sub btnPreencherGrid_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnPreencherGrid.Click

Dim provider As String = "System.Data.SqlClient"

Dim factory As DbProviderFactory = DbProviderFactories.GetFactory(provider)
Dim cnn As DbConnection = factory.CreateConnection

cnn.ConnectionString = "Integrated Security=SSPI;Persist Security Info=False;Initial Catalog=Northwind;User ID=sa;password=;Data Source=MACORATI\VSDOTNET"

Dim cmd As DbCommand = factory.CreateCommand

cmd.Connection = cnn
cmd.CommandText = "select * from Orders"
cmd.CommandType = CommandType.Text

cnn.Open()

Dim reader As DbDataReader = cmd.ExecuteReader(CommandBehavior.CloseConnection)
Dim info As DataTable = New DataTable

info.Load(reader)

reader.Close()

dgv1.DataSource = info
End Sub

Nota: Poderíamos ter armazenado a string de conexão e a  informação do provedor de dados no arquivo de configuração web.config:
Factory.JPG
Para obter os valores do web.config teríamos que usar o seguinte código:

Dim dbfactory As DbProviderFactory = DbProviderFactories.GetFactory(ConfigurationSettings.AppSettings.Get("DbProvider"))
dbconn.ConnectionString = ConfigurationSettings.AppSettings.Get("ConnectionString")

Este exemplo ilustra como você pode trabalhar com as classes de uma base de dados sem especificar o tipo das classes; perceba que, embora eu esteja acessando uma base de dados MSDE (compatível com SQL Server), e que usa a classe SQLClient, eu não precisei especificar nada a respeito desta classe.

As classes do tipo Factory são capazes de criar os objetos associados que permitem o acesso a base de dados. Foi necessário apenas definir o provedor na linha de código :

Dim provider As String = "System.Data.SqlClient"

A seguir, usando o método GetFactory, criamos um DbProviderFactory baseado no provedor fornecido acima.

Dim factory As DbProviderFactory = DbProviderFactories.GetFactory(provider)

Dim cnn As DbConnection = factory.CreateConnection

Observe que criamos um DataReader e preenchemos o um DataTable usando o método Load.

Dim info As DataTable = New DataTable

info.Load(reader)

Executando o projeto teremos o seguinte resultado:

semtitulo.gif

Pegue o código do projeto aqui: acessoBD.zip

Até breve!