Array
(
)

DataTable ou SqlDataReader

Melantonio
|
MVP
Pontos: 300
    15 abr 2010

Boa tarde galera,

estou fazendo minha classe de acesso a dados e uma de conexao.

Na classe de conexao eu tenho um metodo que lista registros de uma determinada tabela.

Dúvida:
Qual a melhor forma de devolver os dados: SqlDataReader ou DataTable ?

Qual é o mais rápido para trabalhar ?

OBS: Não gostaria de usar o SqlDataReader em outra camada, pois não qro utilizar a System.Data.SqlClient, acho que não faz parte dessa camanda, e sim somente da classe do DB.

Falow.

Alexandre Lima.
   - 15 abr 2010

Tiago,

Existem duas formas de trabalhar com acesso à dados(no seu caso):

Conectado - SqlDataReader

Desconectado - DataSet e/ou DataTable usando TableAdapter para acesso ao banco

Tranbalhando de forma conectada à base de dados, é necessário uma conexão ativa com o banco de dados, isso pode gerar um pouco mais de processamento quando a massa de dados é relativamente grande.

Trabalhando de forma desconectada além de ser uma forma elegante de trabalhar com dados no .Net não é necessário uma conexão ativa durante a manipulação de dados.

Aconselho pesquisar sobre alguma ferramenta de ORM(Linq to Sql, Entity Framework, Nhibernate etc), isso irá  facilitar muito o desenvolvimento de sua aplicação.

Netasper
   - 16 abr 2010

Quem é que não gosta de fazer o menor esforço quando precisa codificar. Quanto se trata de acessar dados com VB .NET ou ASP .NET temos duas opções claras : DataSet e DataReader. E em se falando de pouco esforço quanto a codificação , com o DataSet podemos fazer o acesso basicamente com 3 linhas de código. Basta preencher o DataSet e a seguir fazer a interação usando um loop.

Mas e quanto ao desempenho ? Geralmente o desempenho é um requisito que vem em primeiro lugar , quer em aplicações Windows Forms quer em aplicações Web.

Quando se fala em desempenho a utilização de um DataReader leva vantagem sobre um DataSet. Com um DataReader você tem acesso aos dados assim que o objeto fica disponível e não necessita esperar , como no caso do DataSet , o seu preenchimento.

As vantagens do DataReader

A seguir temos um código que preenche um DataSet com o resultado de uma tabela e exibe o primeiro campo em cada linha:0

 SqlConnection conn = new SqlConnection(connectionString);
 SqlDataAdapter da = new SqlDataAdapter ("select * from tabela;",conn);

 DataSet ds = new DataSet();

 da.Fill(ds);

 foreach (DataRow dr in ds.Tables[0].Rows)
 {
     Console.WriteLine(dr[0].ToString());
 }

Dim conn As New SqlConnection(connectionString)
Dim da As New SqlDataAdapter("select * from tabela;", conn)

Dim ds As New DataSet()
da.Fill(ds)

Dim dr As DataRow
For Each dr In ds.Tables(0).Rows
    Console.WriteLine(dr(0).ToString())
Next dr
C# VB.NET

Observando o código você pode notar que a inspeção dos dados feita no loop foreach começa somente após o DataSet ter sido preenchido. Não dá para realizar outra tarefa enquanto o DataSet estiver sendo preenchido.

Vamos ver agora o código que realiza a mesma tarefa usando um DataReader:

 SqlConnection con = new SqlConnection(connectionString);
 SqlCommand cmd = new SqlCommand("select * from tabela", con);

 cmd.Connection.Open();

 SqlDataReader dr =   cmd.ExecuteReader(CommandBehavior.CloseConnection);

 while(dr.Read())  
 {
   Console.WriteLine(dr.GetString(0));
 }

 dr.Close();
 con.Close();
 
Dim con As New SqlConnection(connectionString)
Dim cmd As New SqlCommand("select * from tabela", con)

cmd.Connection.Open()

Dim dr As SqlDataReader = cmd.ExecuteReader(CommandBehavior.CloseConnection)

While dr.Read()
   Console.WriteLine(dr.GetString(0))
End While

dr.Close()
con.Close()
C# VB .NET

Neste caso a inspeção dos dados é feita tão logo os dados estiverem disponíveis através de um loop While , onde dr.Read() retorna false se não encontrar dados. O DataReader armazena um resultado por vez no cliente , e , com isto reduz o uso de memória e recursos do sistema.

É óbvio que dependendo da situação somente um DataSet irá resolver a questão : serializar o resultado ou passar uma consulta para a próxima camada da sua aplicação , vai requerer uma coleção e um DataSet fornece um modelo adequado para estas tarefas.

Na maioria das consultas empregadas nas aplicações Web , onde os dados é pesquisado , exibido e então descartado um DataReader irá aumentar o desempenho da sua aplicação de forma significativa.

Então usar um DataReader é mais rápido que usar um DataSet . Certo ?   Bem , sim e depende...

Será que não existe um modo de otimizar o desempenho de um DataSet para justificar sua utilização ?

Sim , existe e eu vou mostrar como você pode fazer isto. Antes vou mostrar o porque você quereria usar um DataSet.

Os DataSet possuem um conjunto de funcionalidades que os DataReaders não possuem , dentre eles destacamos:

  • Relacionamento Mestre-Detalhe com verificação de integridade
  • Podemos efetuar operações com as colunas do lado do cliente (Soma, Média , etc..) que podem ser persistidas mesmo que os dados se alterem.
  • Ordenação e Filtragem de dados no lado do cliente sem a necessidade de um round-trip ao Banco de dados
  • Uma variedade de opções na vinculação de dados: DataGrid , WinForms e .NET Controls.
  • Integração com o DataAdapter para atualização automática de atualizações.
  • Poder ler e escrever diretamente uma representação XML de dados relacionados.

Escolher usar um DataReader somente olhando a questão do desempenho irá fazer com que você não tenha acesso a estas funcionalidades.

Existem outros aspectos negativos em um DataReader que talvez o façam mudar de idéia.  Em sistemas multiusuários , enquanto  os DataSets/DataAdapters liberam suas conexões e bloqueios assim que o preenchimento via método Fill termina , os DataReaders estão gerenciando a conexão e qualquer bloqueio aberto durante todo o tempo que durar o loop de inspeção de dados. (dr.Read()). Isto pode levar a uma maior contenção do seu banco de dados e com isto diminuir o desempenho .

A única justificativa para usar um DataSet seria então melhorar seu desempenho.

Melhorando o desempenho de um DataAdapter

Você pode aproximar o desempenho de um DataAdapter de um DataReader desativando temporariamente algumas das funcionalidades avançadas padrão de um DataSet durante o processamento. Creio que os pontos chave em matéria de desempenho durante o processamento de um DataSet são a integridade referencial e a indexação que o DataSet usa para manter os dados internamente.

Abaixo temos um código que mostra o preenchimento de um Dataset , via DataAdapter , que possui duas tabelas do banco de dados Northwind.mdb  : Customers e Employees

DataSet ds= new DataSet();

string strConn="Server=(local);dataBase=Northwind;user id=sa;password=;";

SqlConnection cn = new SqlConnection(strConn);

string strSQL="select * from Customers;select * from employees";

cn.Open();

SqlDataAdapter da = new SqlDataAdapter( strSQL,cn);

ds.Tables.Add("Customers");
ds.Tables.Add("Employees");

ds.EnforceConstraints =false;

ds.Tables["Customers"].BeginLoadData();
da.Fill(ds.Tables["Customers"]);
ds.Tables["Customers"].EndLoadData();

ds.Tables["Employees"].BeginLoadData();
da.Fill(ds.Tables["Employees"]);
ds.Tables["Employees"].EndLoadData();

dataGrid1.DataSource=ds.Tables["Customers"];
dataGrid2.DataSource=ds.Tables["Employees"];

cn.Close();

Neste código tomamos algumas medidas para melhorar o desempenho do DataSet. Vejamos quais:

A primeira delas foi definir a propriedade - EnforceConstraints - como false ; isto desabilita a verificação das restrições durante a operação e pode tornar a operação mais rápida. Você pode voltar a definir o valor como True depois que os dados forem retornados dentro um loop try/Catch e tratando a exceção ConstraintException.

Outro fator que onera o desempenho de um DataSet é o estabelecimento de uma chave primária. Podemos também desabilitar temporariamente a indexação e notificação interna. Para isto fazemos o seguinte:

1- Executamos o método BeginLoadData antes de usar o método Fill para desabilitar a notificação , indexação.
2- Executamos o método EndLoadData depois de usar o método Fill para habilitar a indexação a notificação.

Estes métodos são membros da classe DataTable e por isso você vai precisar chamá-los para a DataTable particular que você esta preenchendo.

Com isto melhoramos o desempenho do DataSet e com isto justificamos sua utilização afim de podermos usar seus recursos.

Danilo Mendes
   - 20 abr 2010

Amigo, já que você fala em camadas e classes, pq não avançar para uma outra coisa mais madura tipo o nHibernate ou até mesmo o EntityFramework?

Hoje pouca gente gente usa isso (dataset ou datareader) em sistemas novos por trazerem vários problemas na hora da manutenção.

Melantonio
|
MVP
Pontos: 300
    20 abr 2010

Mas muitos lugares ainda usam o FrameWork 2.0, gostaria de usar procedures e não trabalhar como o linq, pois qualquer alteração precisa gerar uma nova dll.

Já trabalhei com o LINQ e não gostei muito da performace dele, acho q não tem nada igual ao SqlClient.

Netasper
   - 23 abr 2010

Amigo Danilo, vc esta inteiramente equivocado. Falar em maturidade com Linq e nHibernate é brincadeira ne?!?!

Procure ler um pouco mais sobre grandes empresas e sistemas robustos e veja qual usa Linq ou mesmo nHibernate. Nenhuma....

Geralmente, quando a empresa tem condições, eles desenvolvem seu proprio framework. Mas na maioria das vezes é usado Enterprise Library. DAAB.     []s