Postado em julho 16, 2010 às 2:35 am

Por que eu não uso datasets no ASP.NET

Eu particularmente sou um grande opositor de datasets. Existem muitas(e infinitamente melhores) formas de se fazer acesso a uma fonte de dados com o .NET framework, formas que superam os datasets em simplicidade e desempenho. Aqui vou explicar um pouco disso.

Datasets tipados x Datasets não tipados

Este artigo é sobre DataSets não-tipados. Por padrão, um DataSet não é tipado, pois as colunas de seus DataTables devem ser referenciadas por uma variável do tipo string, por exemplo: meuDataSet.Tables[0]["ColunaDesejada"]. Existem ainda os DataSets fortemente tipados, que são classes que extendem a classe DataSet(e a classe DataTable) e incluem propriedades e membros específicos aos dados representados pelos dataset. Apesar desse tipo de dataset sofrer(muito, diga-se de passagem) de problemas de performance(sobre os quais eu irei discorrer ao longo do artigo), eles ainda assim são mais utilizados do que os DataSets não tipados.

Os fundamentos do DataReader

Antes de explicar por que eu preferi utilizar DataReaders ao invés de DataSets nas minhas aplicações, é necessário que tenhamos conhecimento sobre os DataReaders e DataSets. Estes dois objetos tem regras diferentes: DataSets são projetados para ser uma cópia em memória da fonte de dado, enquanto um DataReader  é projetado para ser um “motoboy” entre a camada de banco de dados e uma aplicação .NET.

No ADO.NET, um provider é uma fonte de dados, e existem classes específicas para trabalhar com alguns tipos de providers. Temos as classes SqlConnection, SqlCommand, SqlDataAdapter, e SqlDataReader para trabalhar com o provider SqlClient ; Temos ainda as classes OleDbConnection, OleDbCommand, OleDbDataAdapter, e OleDbDataReader para trabalhar com o providerOleDb. Objetos prefixados com o nome de um provider (Sql, OleDb, Oracle, Odbc, etc.) são objetos específicos do provider. Eles são projetados para esse provider em particular. O DataReader é um exemplo disso (i.e., SqlDataReader, OleDbDataReader, etc.).

Para trabalhar com dados utilizando um DataReader, você deve inicialmente estabelecer uma conexão com a base de dados e especificar a query a ser executada. Depois, o DataReader é criado e age como uma ponte entre a aplicação  .NET e a fonte de dados. Por exemplo, podemos utilizar o códigi abaixo:

//Estabelece a conexão
var oConexao = new SqlConnection(connection string);
oConexao.Open();

//Cria o comando
var oComando = new SqlCommand(SQL query ou stored procedure, oConexao);

//Cria um DataReader para obter informação do banco de dados
SqlDataReader oReader ;
oReader = oComando.ExecuteReader();

//Itera o resultado
while(oReader.Read()){
//trabalha com o registro atual
}

//Fecha a conexão(automaticamente, fecha o DataReader)
oConexao.Close();

O DataReader carrega um registro da base de dados de cada vez. Cada vez que o métodoRead() method  é chamado, o DataReader discarta o registro atual, volta ao banco de dados e traz o próximo resultado do resultset. O método Read()  retorna true se uma linha é lida na base de dados, e false se  não houver mais nenhuma linha.

DataReaders  são objetos de dados conectados, pois necessitam de uma conexão com o banco de dados ativa. Lembre-se, o DataReader é somente um “motoboy”  entre a aplicação e a base de dados. Obviamente, não podem trazer informações do banco depois que a conexão foi fechada. Além disso, um DataReader é limitado a ser somente leitura, e só lê “para frente”, ou seja: As informações obtidas do banco de dados através do DataReader não podem ser modificadas por ele, nem serem trazidas em ordem aleatória. Ao invés disso, um  DataReader está limitado a acessar os registros em ordem sequencial, do primeiro ao último, um de cada vez.

Os fundamentos do DataSet

DataSets são objetos mais complexos e cheios de característica do que os DataReaders. Enquanto DataReaders simplesmente “passam” pelos dados de uma base de dados, DataSets podem ser pensados como uma base de dados em memória(e é exatamente isso que eles são – uma cópia do banco de dados na memória). Do mesmo jeito que uma base de dados é composta por um conjunto de tabelas, um DataSet é composto de uma coleção de objetos DataTable. Enquanto uma base de dados tem relacionamentos entre suas tabelas, extendidos através de várias constraints de integridade de dados nos campos das tabelas, um DataSet pode também ter relacionamentos entre seus DataTables e constraints nos campos dos DataTables.

Ao contrário do DataReader, o DataSet é um objeto de dados neutro com relação a providers. Não existe SqlDataSet ou OleDbDataSet – apenas o bom e único DataSet. É responsabilidade do  provider do objeto DataAdapter “traduzir” os dados de um provider específico em um DataSet netro de providers. O trecho de código abaixo ilustra como popular um DataSet com os dados de uma query:

//Estabelece a conexão
var oConexao = new SqlConnection(connection string);
oConexao.Open();

//Cria o comando
var oComando = new SqlCommand(SQL query ou stored procedure, oConexao);

//Cria o  DataAdapter
var oDataAdapter = new SqlDataAdapter(oComando);

//Cria o DataSet
var oDataSet = new DataSet();

//Preenche o DataSet
oDataAdapter.Fill(oDataSet);

//Fecha a conexão
oConexao.Close();

//Trabalha com o conteúdo do DataSet

Como o trecho de código acima mostrou, o método Fill() do DataAdapter popula o DataSet com os resultados da query específica. Por trás disso, o DataAdapter está utilizando um DataReader para ler os resultados da query e popular o DataSet. O DataSet é um objeto de dados desconectado. Uma bez preenchido, a conexão é fechada e o conteúdo do DataSet pode ser manipulado e examinado.

Partindo do princípio que o DataSet representa uma coleção de dados separada e desconectada, não é surpresa nenhuma que seus dados sejam manipuláveis. Adicionalmente, o DataSet tem algumas capacidade de XML muito boas. Por exemplo, podemos serializar um DataSet em XML utilizando seu método WriteXml() ; Adicionalmente, pode-se popular um DataSet de um XML previamente formadado, utilizando o método ReadXml().

O “comércio” entre DataSet e DataReader

Independentemente de trazer ou não seus dados usando um DataSet ou um DataReader, podemos exibí-los em DataGrids, DataLists, or Repeaters utilizando exatamente o mesmo código. Na prática, definimos a propriedade DataSource dos Web Controls como um DataReader ou um DataSet, e então chamamos o método DataBind(). O ASP.NET transforma o trabalho com dados em algo tão fácil que os desenvolvedores talvez não parem pra pensar qual a melhor forma de se utilizar.”É tudo igual”, não importa o jeito de acessar.

Mas nem tudo é igual.Claramente, há uma grande diferença entre os atributos de um dataset e um datareader, logo também há uma diferença enorme de performance entre eles. Resumindo, a quantidade de atributos de um DataSet’s faz com que sua performance seja bem menor, comparada com o DataReader.

De acordo com este artigo, o DataReader é aproximadamente TRINTA VEZES mais rápido que o DataSet. Para volumes grande de dados, a diferença entre ambos é bem acentuada. O gráfico abaixo, por exemplo, ilustra os resultados de um teste efetuado pelo autor do artigo citado acima buscando de 100 a 1000 registros usando um DataSet (a linha rosa) e um DataReader (a linha azul marinho). Como mostra o gráfico, trazer 1000 registros com um DataReader é mais de 30 mais rápido do que utilizando um DataSet (0.28 segundos contra 8.89 segundos). Notável, não?
comparacao_performance

Outros gráficos de desempenho podem ser observados aqui, em comparações entre DataSet e DataReader em um cenário com um número comum de acessos. O resultado final é o mesmo. No entanto, o artigo diz:

Em todos os testes, percebemos que a performance doDataReader foi melhor que a do DataSet. Como mencionado anteriorment, o DataReader oferece uma performance melhor pois evita a sobrecarga de memória associada à criação do DataSet.(…) O DataReader é uma escolha melhor para aplicações que necessitam de leitura optizimada dos dados. O quanto antes os dados traduzidos do DataReader forem utilizados, o DataReader e a conexão fechados, maior será a performance.

Quando um DataSet é útil?

Apesar das limitações, o DataSet tem seus usos, caso contrário ele não seria um componente central do .NET Framework. No entando, creio que é raro(isto é, se existir) um uso para o DataSet em uma aplicação Web. Em toda a minha experiência, só vi uso para DataSets em duas ocasiões:

Em aplicações desktop(WinForms). Considere uma aplição desktop de entrada de dados. Um usuário pode iniciar o programa, carregar os dados de vendas do servidor de banco de dados, fazer algumas alterações, e então salvá-las. Esta é a situação ideal para se utilizar um DataSet. Ele permite serem lidos em um DataSet residente na memória do computador do usuário, o que confere ao usuário a habilidade de trabalhar com os dados sem fazer constantes consultas ao banco de dados. Após editar os dados, eles podem fazer um batch update, gerenciando de forma harmoniosa quaisquer mudanças ocorridas enquanto o usuário trabalhava desconectado. Além disso, considerando que o DataSet é uma armazenagem de dados desconectada, estes dados podem ser obtidos offline. Um vendedor visitando um cliente pode carregar estes dados e consultá-los em seu notebook enquanto está no trânsito, ou no escritório do cliente.

Uma situação como esta pode surgir em uma aplicação web. Porém, é um caso específico. Eu aconselharia nesse caso um dataset em sessão, utilizando as mesmas técnicas de batch editing e updating, ou – o mais indicado – uma cópia dos dados não em um dataset, mas em um XML local(por exemplo, consumindo um WebService que automaticamente serialize esse DataSet em um XML).

Agora, quantas vezes você já fez isso no seu desenvolvimento ASP.NET diário? Quase sempre, eu aposto que é porque você provavelmente não deveria estar usando DataSets! Enquanto os controles ASP.NET não diferenciam quais objetos estão sendo sua fonte de dados, você provavelmente deve estar sofrendo coma perda de performance escolhendo usar DataSets.

Razões para utilizar um DataSet… e razões pelas quais você provavelmente não deve utilizá-lo

Neste artigo, eu implicitamente afirmei: “Utilizem DataReades em aplicações web, não DataSets!” Existem alguns cenários em uma aplicação web em que o DataSet parece ser a única saída. Por exemplo, imagine que você quer armazenar em cache algumas informações do banco de dados que serão utilizadas em várias páginas ao longo do site. Estes dados podem ser específicos de um usuário, e armazenados em sessão, ou podem ser os mesmos para todos os usuários ou podem ser os mesmos para todos os usuários e, portanto, armazenados no cache de dados. No entanto, um DataReader não pode ser armazenado em cache porque ele é um objeto de dados conectado, e conexões com um banco de dados deve ser de curta duração. Ou seja, a última coisa na vida que queremos é uma conexão aberta armazenada no cache. Portanto, se você quiser ter dados armazenados em cache do banco de dados pode parecer que a única opção é usar um DataSet.

Mas não é a única opção. Você pode, ao invés disso, criar uma classe que tem as propriedades dos campos da tabela que você está armazenando no DataSet(em resumo, um objeto de negócio). E então, quando precisar você precisar armazenar os dados, utilizará um DataReader para ler as linhas e para cada iteração, criará um novo objeto dessa classe, e adicionará a um List do tipo criado. Você pode assim guardar essa coleção em cache. Este método não é apenas mais eficiente, mas eu pessoalmente acho que aumenta a facilidade de manutenção do código, diminuindo o acoplamento. Além disso, os DataSources dos WebControls do .NET são do tipo Object, logo, a chamada do método Bind() funcionará igualmente.

Outra razão pela qual você poderá pensar em utilizar um DataSet é acessar aleatoriamente os dados de uma busca, no caso de serem dados utilizados repetidamente. Por exemplo, popular uma DropDownList em função de outra. Neste caso, ainda assim é melhor acessar o banco com um DataReader, executando a query utilizando como parâmetro o item selecionado na DropDownList “pai”, manipular os dados como descrito acima(com uma coleção tipada), e popular a DropDownList dependente com os resultados.

Conclusão
Neste artigo, examinamos os fundamentos de dois tipos de acesso a dados fornecidos pelo ADO.NET: O DataReader e o DataSet. Ambos os objetos tem seus usos em aplicações .NET mas, na minha opinião, os DataSets são raramente(quando muito) úteis em aplicações ASP.NET. Existem sempre exceções, mas na ampla maioria dos casos, apenas DataReaders devem ser utilizados.

Nota: Apesar de existirem formas mais fáceis de se acessar dados atualmente(Como o facílimo LINQ to Entities), a forma mais utilizada(e performática) de acesso a dados ainda é com objetos de ADO.NET puros.

É isso. Em breve, vem a segunda parte do artigo. Abraços e keep coding!

2 Respostas para “Por que eu não uso datasets no ASP.NET”

  1. Tiago em agosto 3rd, 2010 at 04:34 says:

    “Existem ainda os DataSets fortemente tipados, que são classes que extendem a classe DataSet(e a classe DataTable) e incluem propriedades e membros específicos aos dados representados pelos dataset. Apesar desse tipo de dataset sofrer(muito, diga-se de passagem) de problemas de performance(sobre os quais eu irei discorrer ao longo do artigo), eles ainda assim são mais utilizados do que os DataSets não tipados.”

    Por que?

  2. Bruno Bemfica em agosto 3rd, 2010 at 12:31 says:

    Olá Tiago, obrigado pelo comentário. Os DataSets tipados são mais utilizados por oferecerem um controle maior dos dados ao desenvolvedor, já que na verdade são uma classe que extende a classe DataSet, com atributos próprios, ou seja: Conseguimos visualizar claramente quais são as propriedades desta classe, o que a torna mais facilmente manipulável na hora de codificar.

    Abraços e keep coding.

Deixar um comentário