VB.NET - Preenchendo um DataTable com um DataReader


Para preencher um DataTable em um DataSet geralmente você usa um DataAdapter. certo ?

E se você precisar preencher um DataTable de um DataSet a partir de um DataReader ?  Acha que é possível ?

Neste momento é que os conhecimentos básicos das propriedades e características OOP da linguagem VB.NET se fazem valer. Se você anda meio esquecido vou fazer uma reprise destes conceitos.

O ADO.NET é implementando em várias classes que podem ser divididas em dois grupos :

  1. Data Classes - Contém os dados mas não sabem a sua origem nem como obtê-los. Funcionam  como uma representação em memória de dados relacionais e não estão conectados aos banco de dados diretamente. As principais classes desta categoria são :
    1. DataSet
    2. DataTable
    3. DataRows
    4. DataColumns
    5. DataRelation
  2. Managed Providers - Não contém dados ; fazem o acesso aos dados , sabem ler e escrever dados em banco de dados. São o conjunto de classes concretos que implementam três interfaces principais que ditam comportamentos específicos, são elas:
    1. IDbConnection - Diz como deve ser o comportamento de uma classe que gerencia a conexão com o banco de dados. Muito semelhante a classe Connection da ADO.  
    2. IDbCommand -  encapsula o comportamento de um comando SQL. Muito semelhante a classe Command da ADO.   
    3. IDataReader -   encapsula o cursor somente leitura e somente para frente. Representa um conjunto de resultados SQL. Equivalente ao Recordset ADo Fo/Ro. Não suporta navegação nem atualização.
    4. IDbDataAdapter - Tem como principal função intermediar a interação a classe DataSet e banco de dados . Para isto ela contém um comando SQL(SelectCommand) de consulta que é usado para preencher uma tabela dentro de um DataSet. Possui também os comandos de inserção, exclusão e atualização. O método Fill  preenche o DataSet executando a conexão ao banco de dados a consulta e a desconexão.

O xis da questão é que para preencher um DataTable usamos o método Fill do objeto DataAdapter e que o DataReader não possui o método Fill.

Mas se olharmos com mais atenção para a classe DbDataAdpater , classe a partir da qual todos os tipos xxxDataAdapter herdam, iremos perceber que ela expõe um método sobrecarregado e protegido para o método Fill. E , o melhor da estória,  este método pode tomar um objeto IDataReader como argumento. Sua sintaxe é :

Overloads Protected Overridable Function Fill(DataSet, String, IDataReader, Integer, Integer) As Integer

Podemos então usar estes métodos . Certo ?

Sim , podemos usar estes métodos usando Reflection pois eles são protected.

Podemos então criar um método , função ou procedure para efetuar o preenchimento de um DataTable a partir de um DataReader. O código da rotina poderia ser o descrito abaixo:

  Sub PreencheDataSetDeDataReader(ByVal ds As DataSet, ByVal table As String, ByVal dr As IDataReader)
        ' Cria um  xxxDataAdapter do mesmo tipo de um DataReader
        Dim tipoDataReader As Type = CObj(dr).GetType
        Dim nomeTipo As String = tipoDataReader.FullName.Replace("DataReader", "DataAdapter")
        Dim tipoDataAdapter As Type = tipoDataReader.Assembly.GetType(nomeTipo)
        Dim da As Object = Activator.CreateInstance(tipoDataAdapter)
        ' invoca o método protegido Fill que toma um objeto IDataReader
        Dim args() As Object = {ds, table, dr, 0, 999999}
        tipoDataAdapter.InvokeMember("Fill", BindingFlags.InvokeMethod Or BindingFlags.NonPublic Or BindingFlags.Instance, Nothing, da, args)
        ' fecha o DataReader
        dr.Close()
    End Sub

Vamos então , usando a rotina acima mostrar como podemos preencher um DataGrid com os dados de um DataTable preenchido a partir de um DataReader.

Inicie um novo projeto no VS.NET do tipo Windows Application usando a linguagem VB.NET e no formulário padrão insira um componente DataGrid , um componente Button e outro Label.

Declare os seguintes imports no projeto:

Imports System.data.sqlclient
imports
System.Reflection

Agora  no evento Click do botão de comando insira o código que irá usar a rotina PreencheDataSetDeDataReader().:

  Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Dim cn As New SqlConnection("server=(local);trusted_connection=true;database=Northwind;Integrated Security=SSPI")
        cn.Open()
        Dim cm As New SqlCommand("SELECT * FROM Employees", cn)
        Dim dr As SqlDataReader = cm.ExecuteReader
        Dim ds As New DataSet
        PreencheDataSetDeDataReader(ds, "Empregados", dr)
        DataGrid1.DataSource = ds.Tables("Empregados")
    End Sub

Estou efetuando uma conexão com o banco de dados Northwind do SQL Server e preenchendo o DataTable com os dados da tabela Employees.

Observe que estou passando o DataSet - ds , a tabela Empregados , e o DataReader - dr , que foi obtido anteriormente. Executando o projeto teremos o resultado abaixo:

Sem ter as noções de orientação a objeto jamais chegaríamos a esta solução.

Eu sei é apenas VB , mas eu gosto...


José Carlos Macoratti