ADO .NET - DataReader - Melhorando o Desempenho (C#)


A grande maioria das aplicações feitas com a plataforma .NET utilizam um banco de dados relacional; atualmente mesmo que a plataforma .NET disponibilize ferramentas que ajudam o desenvolvedor a realizar esta tarefa, como o Entity Framework e o LINQ to SQL, os recursos da ADO .NET ainda são muito usados.

Dentre os objetos ADO .NET o DataReader é um dos principais recursos que permitem o acesso aos dados.

Por permitirem o acesso aos dados no modo somente leitura para frente - forward-only - ele é usado para obter rapidamente dados sendo por isso muito rápido. (O DataReader não permite alterar ou atualizar a fonte de dados original pois o seu acesso é conectado)

Mas mesmo sendo mais rápido que o objeto DataSet podemos melhorar o desempenho de outras formas e neste artigo eu vou mostrar duas maneiras de fazer isso, a saber:

  1. Usando os acessores tipados (Typed Accessors)
  2. Usando as Colunas Ordinais

Os recursos usados neste artigo são:

Vamos lá...

1- Melhorando o desempenho com o uso dos Typed Accessors

Podemos usar os Typed Acessors para para melhorar o desempenho de um DataReader, eliminando assim as repetidas operações de boxing e unboxing dos objetos de dados para os tipos de dados da plataforma .NET.

Você pode acessar os dados em uma linha de um DataReader usando um nome de coluna, uma coluna ordinal ou um método de acesso tipado, como GetInt32 () e GetString().

O acessador tipado permite que um valor de coluna seja acessado em seu tipo de dados nativo, reduzindo a quantidade de conversão entre tipos necessárias ao se recuperar um valor de coluna.

Quando o tipo subjacente é conhecido, isso reduz o esforço de conversão entre tipos necessárias ao recuperar o valor da coluna e, assim, melhora o desempenho.

A tabela a seguir temos uma lista com os principais acessores tipados para o SQL Server e seus correspondentes na plataforma .NET:

Tipo de dados SQL Server Tipos de dados .NET Framework Acessor Tipado .NET Framework Acessor tipado SQLType
binary Byte[] GetBytes() GetSqlBinary()
bit Boolean GetBoolean() GetSqlBit()
char String Char[] GetString() GetChars() GetSqlString()
datetime DateTime GetDateTime() GetSqlDateTime()
decimal Decimal GetDecimal() GetSqlDecimal()
float Double GetDouble() GetSqlDouble()
image Byte[] GetBytes() GetSqlBinary()
int Int32 GetInt32() GetSqlInt32()
money Decimal GetDecimal() GetSqlMoney()
nchar String Char[] GetString() GetChars() GetSqlString()
ntext String Char[] GetString() GetChars() GetSqlString()
numeric Decimal GetDecimal() GetSqlDecimal()
nvarchar String Char[] GetString() GetChars() GetSqlString()
real Single GetFloat() GetSqlSingle()
smalldatetime DateTime GetDateTime() GetSqlDateTime()
smallint Int16 Int16 GetSqlInt16()
smallmoney Decimal GetDecimal() GetSqlDecimal()
sql_variant Object GetValue() GetSqlValue()
text String Char[] GetString() GetChars() GetSqlString()
timestamp Byte[] GetBytes() GetSqlBinary()
tinyint Byte GetByte() GetSqlByte()
uniqueidentifier Guid GetGuid() GetSqlGuid()
varbinary Byte[] GetBytes() GetSqlBinary()
varchar String Char[] GetString() GetChars() GetSqlString()

Cada assessor tipado recebe um único argumento: a coluna baseada em zero ordinal da coluna para o qual recuperar o valor.

Uma exceção IndexOutOfRangeException é gerada se o valor ordinal não for válido.

Um exceção InvalidCastException é gerada se o método de acesso especifica uma conversão inválida.

Se a coluna pode conter um valor inexistente ou nulo, chame o método IsDBNull() antes de chamar o método de acesso tipado para evitar o levantamento de uma exceção no caso de o valor da coluna é equivalente a DBNull.

Vejamos como funciona na prática...

Criando o programa C# para comparação

Abra o Visual C# 2010 Express Edition e crie um novo projeto (File->New Project) do tipo Console Application com o nome DataReader_TypedAccessors e clique em OK;

Vamos acessar a tabela Employees do banco de dados Northwind e realizar um determinando número de interações usando um DataReader e comparando os 3 tipos de acesso:

  1. via Acessor Tipado - Ex: dr.GetInt32(0);
  2. via Coluna Ordinal - Ex: Convert.ToInt32(dr[0]);
  3. via Nome da Coluna - Ex: Convert.ToInt32(dr["EmployeeID"]);

O código usado neste projeto pode ser visto a seguir:

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

namespace DataReader_TypedAccessors
{
    class Program
    {
        static void Main(string[] args)
        {
            int NumeroInteracoes = 8000;

            int EmployeeID;
            string LastName;
            string FirstName = null;
            string city;

            int tempoInicio = 0;
            int tempoDecorrido;

            string sqlConnectString = @"Data Source=.\SQLEXPRESS;Initial Catalog=Northwind;Integrated Security=SSPI;";
            string sqlSelect = "SELECT EmployeeID, LastName, FirstName, City FROM Employees";

            Console.WriteLine("---Tipo de Acesso ao DataReader , {0} No. Iterações ---\n", NumeroInteracoes);

            SqlConnection connection = new SqlConnection(sqlConnectString);
            // Cria o comando e abre a conexão
            SqlCommand command = new SqlCommand(sqlSelect, connection);
            connection.Open();

            tempoDecorrido = 0;
            for (int i = 0; i < NumeroInteracoes; i++)
            {
                // Cria o DataReader e retorna todos os campos para
                // cada registro usando o acessor tipado com a coluna ordinal
                using (SqlDataReader dr = command.ExecuteReader())
                {
                    tempoInicio = Environment.TickCount;
                    while (dr.Read())
                    {
                        EmployeeID = dr.GetInt32(0);
                        LastName = dr.GetString(1);
                        FirstName = dr.IsDBNull(2) ? null : dr.GetString(2);
                        city = dr.GetString(3);
                    }
                    tempoDecorrido += Environment.TickCount - tempoInicio;
                }
            }

            Console.WriteLine("Acessor Tipado: Tempo = {0} ",tempoDecorrido);

            tempoDecorrido = 0;
            for (int i = 0; i < NumeroInteracoes; i++)
            {
                // Cria o DataReader e retorna os campos para 
                // cada registro usando a coluna ordinal
                using (SqlDataReader dr = command.ExecuteReader())
                {
                    tempoInicio = Environment.TickCount;
                    while (dr.Read())
                    {
                        EmployeeID = Convert.ToInt32(dr[0]);
                        LastName = Convert.ToString(dr[1]);
                        FirstName = Convert.ToString(dr[2]);
                        city = Convert.ToString(dr[3]);
                    }

                    tempoDecorrido += Environment.TickCount - tempoInicio;
                }
            }
            Console.WriteLine("Coluna Ordinal: Tempo = {0}", tempoDecorrido);

            tempoDecorrido = 0;
            for (int i = 0; i < NumeroInteracoes; i++)
            {
                // Cria o DataReader e retorna os campos para 
                // cada registro usando o nome do campo da coluna
                using (SqlDataReader dr = command.ExecuteReader())
                {
                    tempoInicio = Environment.TickCount;
                    while (dr.Read())
                    {
                        EmployeeID = Convert.ToInt32(dr["EmployeeID"]);
                        LastName = Convert.ToString(dr["LastName"]);
                        FirstName = Convert.ToString(dr["FirstName"]);
                        city = Convert.ToString(dr["city"]);
                    }

                    tempoDecorrido += Environment.TickCount - tempoInicio;
                }
            }
            Console.WriteLine("Nome da Coluna: Tempo = {0}", tempoDecorrido);
            Console.WriteLine("\\nPressione algo para continuar.");
            Console.ReadKey();
        }
    }
}

Executando o projeto temos o seguinte resultado:

Como podemos observar o acesso ao DataReader usando os acessores tipados é bem mais rápido que os outros métodos.

Pegue o projeto completo aqui: DataReader_TypedAccessors.zip

Veja os Destaques e novidades do SUPER CD VB 2012 (sempre atualizado) : clique e confira !

Quer migrar para o VB .NET ?

Veja mais sistemas completos para a plataforma .NET no Super CD .NET e no Super DVD .NET , confira...

Quer aprender C# ??

Chegou o Super DVD C# 2012 com exclusivo material de suporte e vídeo aulas com curso básico sobre C#.

 

Tito 1:10 Porque há muitos insubordinados, faladores vãos, e enganadores, especialmente os da circuncisão,

Tito 1:11 aos quais é preciso tapar a boca; porque transtornam casas inteiras ensinando o que não convém, por torpe ganância.

Tito 1:12 Um dentre eles, seu próprio profeta, disse: Os cretenses são sempre mentirosos, bestas ruins, glutões preguiçosos.

Tito 1:13 Este testemunho é verdadeiro. Portanto repreende-os severamente, para que sejam são na fé,

Tito 1:14 não dando ouvidos a fábulas judaicas, nem a mandamentos de homens que se desviam da verdade

Referências:


José Carlos Macoratti