C# - ADO .NET para iniciantes IV - DataReader (SqlDataReader)


Esta chegando agora ???

Então acompanhe os primeiros artigos:

O objeto DataReader da ADO .NET pertence ao namespace System.Data e apresenta as seguintes especificidades:

Você vai encontrar na leitura técnica a designação de que o um DataReader é forward-only (somente para frente) e read-only (somente-leitura).

O resultado gerado por um DataReader é retornado pela execução da consulta e é armazenada no buffer da rede no cliente até que seja requisitado através do método Read().

Resumindo as funcionalidades do DataReader:

   - É um objeto somente leitura e para frente, ou seja, não você não pode navegar aleatoriamente;
   - Opera conectado ao banco, assim, enquanto estivermos utilizando-o para obter os dados do banco, estaremos com a conexão do banco aberta;
   - Opera apenas com uma tabela por vez; você deve configurar a string SQL para a primeira tabela, conectar no banco, coletar suas informações e desconectar do banco. Para a segunda tabela você deverá repetir o processo;

Nota:  A conexão utilizada pelo DataReader deve ser aberta e fechada manualmente.(exceto quando você informa que ela deverá ser fechada de forma automática usando a enumeração CommandBehavior.CloseConnection)

Você deve usar um DataReader em sua aplicação quando:
  • Você precisar trabalhar somente com uma tabela de dados por vez
  • você não precisar usar cache de dados
  • você precisar somente exibir os dados de uma tabela
  • você precisar acessar de forma rápida e de uma vez os dados de uma forma somente-leitura e somente-para-frente
  • você precisar processar uma quantidade de dados muito grande para caber na memória

Nota: O DataAdapter usa o DataReader par preencher o DataSet. Desta forma , o desempenho ganho pela utilização do DataReader é que você salva na memória os dados que o DataSet irá consumir.

O roteiro básico para utilização de um objeto DataReader é o seguinte:

Abaixo temos um exemplo bem básico do código usando SqlDataReader:

using System.Data;
using System.Data.SqlClient;

string connString = "Data Source=server;Initial Catalog=database;Persist Security Info=True;User ID=sa;Password=xxx"
SqlConnection adoConn = new SqlConnection(connString);
adoConn.Open();

// novo command
string sql = "SELECT * FROM Tabela";
SqlCommand adoCmd = new SqlCommand(sql, adoConn);

SqlDataReader adoDR = adoCmd.ExecuteReader();

if (a
doDR.HasRows)
{
     while (adoDR.Read())
     {
         Response.Write(adoDR["0"].ToString());
     }
}

adoDR.Close();
adoDR.Dispose();
adoCmd.Dispose();
adoConn.Close();
adoConn.Dispose();

Observe as linhas em negrito onde temos:

1-) A criação de um objeto SqlCommand usando uma instrução SQL e a conexão

SqlCommand adoCmd = new SqlCommand(sql, adoConn);

2-) A criação de um objeto SqlDataReader e utilização do método ExecuteReader() do objeto SqlCommand;

SqlDataReader adoDR = adoCmd.ExecuteReader();

3-) Utilização do método Read() para ler o resultado

 while (adoDR.Read())

Veja a seguir outra forma de obter o mesmo resultado:

using System.Data.SqlClient;

public void exibirDados()
{
    string consulta = "SELECT * FROM Tabela";
    SqlConnection conexao = new SqlConnection("conString");
    SqlCommand comando = new SqlCommand(consulta, conexao);
    SqlDataReader dr = null;
    try
    {
          conexao.Open();
          dr = comando.ExecuteReader();
          while (dr.Read())
          {
                Console.WriteLine(dr.GetString(1));
           }
     }
     catch{
        Console.WriteLine("Erro.");
     }
     finally
     {
        dr.Close();
        conexao.Close();
    }
}

Observe que abrimos uma conexão com a fonte de dados e após executar a consulta usando o método ExecuteReader() atribuímos o resultado ao objeto SqlDataReader dr.

Para exibir o resultado usamos um laço While onde percorremos o SqlDataReader usando o método Read(), este método avança sempre para o próximo registro e retorna True enquanto existir um registro a ser lido.

Para ler os valores retornados podemos usar o nome da coluna, o seu índice ou os métodos Get do SqlDataReader:

O objeto SqlDataReader possui alguns métodos para obter os dados das colunas como: GetString(), GetValue(), getDateTime(), GetDouble(), GetChar(), GetGuid(), GetInt16(), GetInt32(), etc..

Cada um destes métodos usa um valor inteiro como índice baseado em zero e representa a coluna a ser obtida. Assim dr.GetString(0) retorna o valor da primeira coluna.

Para detalhes veja o link: http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqldatareader_members.aspx

No primeiro exemplo antes de iniciarmos o laço e a leitura do DataReader usamos o método HasRows que é usado para verificar se existem linhas no DataReader, se existir ele retorna True, se não retorna False e neste caso nem iniciaremos a leitura do mesmo.

O enumerador CommandBehavior possui o item CloseConnection() que pode ser usado com o método ExecuteReader do objeto SqlCommand() da seguinte forma:

dr = comando.ExecuteReader(CommandBehavior.CloseConnection)

neste caso estamos passando o CommandBehavior.CloseConnection() como parâmetro para o objeto ExecuteReader e com isso não temos a necessidade de fechar a conexão explicitamente com a fonte de dados, pois ao percorrer os dados o próprio DataReader irá fechar a conexão quando o método Close() do DataReader for chamado.

Veja um exemplo usando este recurso:

using System.Data.SqlClient;

public void exibirDados(){

SqlConnection conexao = new SqlConnection("connString");
SqlCommand comando = new SqlCommand("SELECT * FROM TABELA", conexao);
SqlDataReader dr=null;

 try
 {
    conexao.Open();
   
 dr = comando.ExecuteReader(CommandBehavior.CloseConnection);
     while (dr.Read())
    {
           Console.WriteLine(dr.GetString(1));
     }
  }
  catch (Exception ex)
  {
    Console.WriteLine("Erro.");
   }
   finally
   {
    
dr.Close();
   }
}

Observe que neste caso não fechamos a conexão explicitamente com o banco de dados. Para verificar se a conexão ainda esta aberta podemos verificar o seu estado usando o código a seguir:

if (conexao.State != ConnectionState.Closed)
        conexao.Close();

Exemplo prático usando DataReader

No exemplo a seguir vamos criar uma aplicação C# com uma interface bem simples que permite aos usuários procurar pelo nome dos produtos na tabela Products do banco de dados Northwind.mdf. Este exemplo usará o seguintes recursos:

Abra o Visual C# Express Edition e crie um novo projeto do tipo Windows Application com o nome uDataReader;

A seguir no formulário padrão inclua os controles : Label, Button - btnProcurar , ListBox - lbDados e TextBox - txtcriterio conforme o leiaute abaixo:

Agora defina o namespace :  using System.Data.SqlClient;

No evento Click do botão de comando Procurar insira o seguinte código:

    private void btnProcurar_Click(object sender, EventArgs e)
        {
            //define os objetos DataReader, Connection e Command
            SqlDataReader sqldr = null;
            SqlConnection con = null;
            SqlCommand cmd = null;
            try
            {
                // Abre a conexão com o banco de dados Northwind no SQL Server 2005 Express
                // .\SQLExpress é o nome default do servidor
                // initial Catalog - indica o banco de dados
                // String usando Windows Authentication (Trusted Connection):
                string ConnectionString = "Data Source=.\\SQLEXPRESS;Initial Catalog=Northwind;Integrated Security=SSPI";
                con = new SqlConnection(ConnectionString);
                con.Open();
                // define um comando para selecionar os produtos e preços da tabela products
                string CommandText = "SELECT ProductName,UnitPrice " +
                                                 "FROM Products " +
                                                 "WHERE (ProductName LIKE @criterio)";
                //associa comando a conexão
                cmd = new SqlCommand(CommandText);
                cmd.Connection = con;
                // Define o parâmetro @criterio e seu tipo de dados
                cmd.Parameters.Add(
                    new SqlParameter(
                    "@criterio",                                  // o nome do parametro
                    System.Data.SqlDbType.NVarChar, // o tipo de dado SqlDbType
                    40,                                              // o tamanho do parametro
                    "ProductName"));                          // o nome da coluna na tabela a qual se aplica
                // Preenche o valor do parâmetro com o texto informado
                // na caixa de texto : txtcriterio
                cmd.Parameters["@criterio"].Value = txtcriterio.Text+"%";
                // executa a consulta
                sqldr = cmd.ExecuteReader();
                lbDados.Items.Clear();
                // preenche o listBox com os valores retornados
                // usa o método read() para percorrer o datareader
                while (sqldr.Read())
                {
                    lbDados.Items.Add(sqldr["ProductName"].ToString() + " - "  + sqldr["UnitPrice"].ToString());
                }
            }
            catch (Exception ex)
            {
                // exibe mensagem de erro
                MessageBox.Show(ex.Message);
            }
            finally
            {
                // fecha o data reader e a conexão
                if (sqldr != null)
                    sqldr.Close();
                if (con.State == ConnectionState.Open)
                    con.Close();
            }
        }

Note que estamos usando o parâmetro @criterio para receber o valor informado na caixa de texto na instrução de comando SQL :

SELECT ProductName,UnitPrice FROM Products WHERE (ProductName LIKE @criterio)";

Ao atribuir o valor ao parâmetros incluímos o caractere % ao final do valor digitado:    cmd.Parameters["@criterio"].Value = txtcriterio.Text+"%";

Após executar o comando via método ExecuteReader() percorremos o DataReader com o método Read() e obtemos os valores usando a sintaxe: sqldr["ProductName"].ToString() + " - "  + sqldr["UnitPrice"].ToString()

O resultado para a letra C pode ser visto abaixo:

Pegue o projeto completo aqui: uDataReader.zip

Usando uma Stored Procedure

Podemos também gerar um DataReader executando uma stored procedure. Supondo que você tenha uma stored procedure criada no banco de dados com o nome listaClientes que obtêm os clientes para um determinado id.

O código usado para definir o comando e passar o parâmetro pode ser escrito assim:

1 SqlCommand storedProcCommand =   new SqlCommand ("listaClientes", con);
2 storedProcCommand.CommandType = CommandType.StoredProcedure;
3 storedProcCommand.Parameters.Add("@ID",clienteID);


4 SqlDataReader reader = storedProcCommand.ExecuteReader();
5 while (reader.Read())
  {//}

- Na primeira linha construímos o objeto SqlCommand com o nome da stored procedure usando a conexão com o banco de dados;
- Na linha 2 o objeto Sqlcommand define que estará executando uma stored procedure;
- Na linha 3 incluímos o parâmetro no objeto SqlCommand. Observe o uso do símbolo @ usado para identificar um parâmetro no SQL Server.
- Na linha 4 usamos o método ExecuteReader() para executar a stored procedure e retornar um DataReader;
- Na linha 5 iniciamos a leitura do DataReader;

Lembrete:

Um DataReader é sempre mais rápido que um DataSet ???

A resposta é : DEPENDE...

A vantagem do DataReader  e não ter que carregar todo o conteúdo da tabela em memória, e retornar os registros à medida que são disponibilizados pelo banco de dados; enquanto que o DataSet aguarda o fim da execução da  consulta para prosseguir a execução.

Quando você usa um DataSet, todos os registros retornados são carregados na memória, para depois serem processados.

O DataReader carrega na memória apenas o registro atual. Por isso,  não é possível acessar registros anteriores.(Ele é forward-only lembra-se ?)

Então, se você usar o DataReader para ler TODOS os registros do banco de dados e carregá-los na memória esta fazendo com que o seu DataReader se comporte como um DataSet.

Ora !!  neste caso é melhor um DataSet pois exige menos código para ser tratado.

Concluímos assim esta apresentação sobre o objeto DataReader e sua utilização com o SqlDataReader.

No próximo artigo irei falar sobre o objeto SqlDataAdapter e DataSet.

Eu sei é apenas ADO .NET, mas eu gosto...

Veja os Destaques e novidades do SUPER DVD Visual Basic (sempre atualizado) : clique e confira !

Quer migrar para o VB .NET ?

Quer aprender C# ??

 

             Gostou ?   Compartilhe no Facebook   Compartilhe no Twitter
 

Referências:


José Carlos Macoratti