Dicas de performance com o ADO.NET
Trabalhar com banco de dados sempre foi uma funcionalidade interessante no tocante à forma de ler e pesquisar dados. Quando o ADO.NET surgiu, vi muitos desenvolvedores completamente perdidos sem saber qual é o tipo mais adequado de classe e qual método usar em determinada situação. O objetivo deste artigo é esclarecer as melhores funcionalidades do ADO.NET neste ponto. Mostrarei diversas formas de se pesquisar dados nas tabelas no Northwind do SQL Server, mas cabe ressaltar que a fonte de dados, ou seja, onde estão os dados é irrelevante, pois a classe é a mesma para todos, seja SQL Server, Oracle, Access, etc.
Execute Reader
O desenvolvedor sempre deve ter em mente: “Qual é a melhor forma de ler várias colunas de dados?”. Note que a pergunta está muito clara, ou seja, se refere apenas à leitura de várias colunas de dados. A resposta é: DataReader. O DataReader é a forma mais rápida de se ler dados em uma fonte de dados. Ela foi criada especialmente para esta finalidade. O que você deverá fazer é ler os dados e preencher o devido controle para exibi-los.
Para quem já trabalhou com Recordset, o DataReader é o que mais se aproxima disso, pois é somente forward-only e você não consegue lê-lo mais que uma vez, ou seja, uma vez lido já era. A performance é a melhor possível, pois você precisa apenas dos seguintes dados: uma conexão, um command (instrução SQL), método ExecuteReader.
Vamos a um exemplo. Abra o Visual Studio .NET 2003 e crie um novo projeto chamado ADO.NET do tipo Windows Forms, usando a linguagem Visual Basic .NET (poderia ser em C#, é apenas uma questão de sintaxe, aliás se você programa em C#, as classes e os métodos usados são idênticos, portanto, esteja à vontade quanto à linguagem).
Crie um novo formulário chamado DataReader com o seguinte layout (Figura 1), contendo: 3 Buttons e 1 ListBox (lstDados).
Pressione F7 para exibir a janela de códigos e como iremos acessar o SQL Server, declare o devido namespace na lista de Imports na primeira linha da janela. Esta classe foi criada especialmente para o SQL Server versão 7.0 ou superior.
Imports System.Data.SqlClient
Em seguida, dentro da declaração da classe, declare uma variável chamada conexao para a conexão com o banco de dados. É importante ressaltar que você deverá ajustar esta string de conexão de acordo com a sua configuração, adicionando, se houver, password, segurança integrada, etc. Como aqui é apenas um artigo, deixei da forma mais simples possível.
Public Class DataReader
Inherits System.Windows.Forms.Form
Dim conexao As String = "Server=(local);Database=Northwind;user id=sa"
Em seguida, crie o código para o botão LerNomeCampo (btnLerNomeCampo) (ver Listagem 1). Note que existe um bloco de Try/Catch para tratar possíveis erros. Se ocorrer algum erro dentro do bloco do Try, então o mesmo é capturado dentro do Catch e é exibida a mensagem de erro.
Private Sub btnLerNomeCampo_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles
btnLerNomeCampo.Click
Try
Dim conn As New SqlConnection(conexao)
Dim sql As String = "Select CustomerID, CompanyName FROM Customers"
Dim cmd As New SqlCommand(sql, conn)
conn.Open()
Dim reader As SqlDataReader = cmd.ExecuteReader()
lstDados.Items.Clear()
While reader.Read()
lstDados.Items.Add(reader("CustomerID") + " - " + _reader("CompanyName"))
End While
reader.Close()
conn.Close()
Catch ex As SqlException
MessageBox.Show(ex.Message)
End Try
End Sub
Veja a explicação detalhada. É definida a variável conn para instanciar a classe SqlConnection que recebe a string de conexão já definida acima.
Dim conn As New SqlConnection(conexao)
É definida a instrução SQL e um Command, onde a variável cmd é o Command que irá executar exatamente o Sql da respectiva conexão. Em seguida, é aberta a conexão com o método Open.
Dim sql As String = "Select CustomerID, CompanyName FROM Customers"
Dim cmd As New SqlCommand(sql, conn)
conn.Open()
Para montar um DataReader, basta instanciar uma variável com o objeto SqlDataReader e usar o método ExecuteReader. É exatamente neste momento que todos os dados contidos na instrução SQL vão para a memória e ficam armazenados no objeto reader. Isto ocorre apenas na memória.
Dim reader As SqlDataReader = cmd.ExecuteReader()
Como iremos exibir os dados no ListBox lstDados, então, o mesmo é limpo e em seguida é montado um looping do tipo While que percorre todos os dados do DataReader Reader. Observe que não existe um método MoveNext, conforme tínhamos no Recordset, isto está encapsulado no método Read(). Observe como referenciar os campos do DataReader, ou seja, a primeira forma é através do próprio nome do campo (entre aspas “campo”). Apesar de não ser a forma mais rápida, é a mais indicada em muitas situações. A cada item lido, o mesmo é adicionado como um novo item no lstDados.
lstDados.Items.Clear()
While reader.Read()
lstDados.Items.Add(reader("CustomerID") + " - " + _reader("CompanyName"))
End While
Por fim, são fechados o Reader e a conexão.
reader.Close()
conn.Close()
Outra forma de ler os dados no DataReader é através do número ordinal. Digite o código da Listagem 2 para o botão btnLerOrdinal.
Private Sub btnLerOrdinal_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles
btnLerOrdinal.Click
Dim conn As New SqlConnection(conexao)
Dim sql As String = "Select CustomerID, CompanyName FROM Customers ORDER BY CompanyName"
Dim cmd As New SqlCommand(sql, conn)
conn.Open()
Dim reader As SqlDataReader = cmd.ExecuteReader()
Dim intCustomerIdOrdinal As Integer = reader.GetOrdinal("CustomerID")
Dim intCompanyNameOrdinal As Integer = reader.GetOrdinal("CompanyName")
lstDados.Items.Clear()
While reader.Read()
lstDados.Items.Add(reader(intCustomerIdOrdinal) + " - " + reader(intCompanyNameOrdinal))
End While
reader.Close()
conn.Close()
End Sub
Basicamente é igual ao anterior, exceto na forma de trabalhar com os dados. Note que cada campo que será usado é declarado com o respectivo tipo de variável, neste caso integer, e o mesmo é referenciado com o reader.GetOrdinal seguido do nome do campo. No looping você referencia a variável o qual foi armazenado o respectivo campo. A vantagem disto é que dependendo do número de campos e dados a serem lidos, pode tornar-se mais rápido justamente pela não necessidade de ficar convertendo campos, pois o tipo já está declarado na variável. Veja ainda outra forma de você referenciar os campos na Listagem 3. Este é o código do botão btnLerObjeto.
Private Sub btnLerObjeto_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles
btnLerObjeto.Click
Dim conn As New SqlConnection(conexao)
Dim sql As String = "Select CustomerID, CompanyName FROM Customers ORDER BY CustomerID"
Dim cmd As New SqlCommand(sql, conn)
conn.Open()
Dim reader As SqlDataReader = cmd.ExecuteReader()
Dim intCustomerIdOrdinal As Integer = reader.GetOrdinal("CustomerID")
Dim intCompanyNameOrdinal As Integer = reader.GetOrdinal("CompanyName")
lstDados.Items.Clear()
While reader.Read()
lstDados.Items.Add(reader.GetString(intCompanyNameOrdinal) + _
" - " + reader.GetString(intCustomerIdOrdinal))
End While
reader.Close()
conn.Close()
End Sub
Basicamente idêntico ao anterior, exceto na referência dos campos. É usado o GetOrdinal, mas no looping o objeto é capturado usando o GetString. Cabe ressaltar que você também pode se referenciar pelo índice (0, 1, 2, etc) e, apesar de eu não gostar disto por uma questão de acessibilidade e documentação, é a forma mais rápida de referenciar uma coluna do DataReader.
Salve o projeto, defina-o como sendo o Startup Project e pressione F5 para executá-lo (Figura 2). Faça o teste com os três botões.
ExecuteScalar
O método ExecuteScalar é outra maneira de ler dados em uma fonte, no entanto, tenha em mente a seguinte questão: “Preciso ler apenas uma linha e uma coluna de uma fonte de dados”. O ExecuteScalar foi criado para este tipo de situação, onde você precisa retornar apenas uma linha e uma coluna. Atenção, eu não escrevi duas linhas ou duas colunas, apenas uma linha e coluna. São casos em que você precisa saber o valor de um produto, a quantidade de estoque, o saldo bancário, o status de um cliente, enfim, são situações em que o retorno é usado para tal finalidade. Vale dizer que o ExecuteScalar retorna um tipo Object e você precisa convertê-lo para o tipo adequado no momento do uso.
O ExecuteScalar é o meio mais rápido existente para qualquer tipo de pesquisa. Não existe nenhum método mais rápido que ele nestas situações em select em tabelas. Além disto, cabe ressaltar que instruções do tipo Agreggate (Sum, Count, Max, Min, Avg, etc) também são aceitas. Assim como o ExecuteReader, o ExecuteScalar precisa apenas de uma conexão, um Command e o método ExecuteScalar.
Crie um novo formulário chamado ExecuteScalar contendo os seguintes controles, conforme a Figura 3: um ListBox (lstProdutos) e dois Buttons.
Pressione F7 para exibir a janela de códigos e como iremos ler um banco de dados, na primeira linha da janela, digite a referência ao namespace para acessar o SQL Server.
Imports System.Data.SqlClient
Declare a string de conexão com o banco de dados Northwind do SQL Server.
Dim conexao As String = "Server=(local);Database=Northwind;user id=sa"
Como o ListBox deverá ser preenchido assim que o formulário for inicializado, então declare a rotina (MontaProdutos) que irá montar o ListBox com todos os produtos da tabela Products (ver Listagem 4).
Public Sub New()
MyBase.New()
'This call is required by the Windows Form Designer.
InitializeComponent()
'Add any initialization after the InitializeComponent() call
MontaProdutos()
End Sub
Digite a respectiva rotina que irá montar um DataReader com os nomes dos produtos da tabela Products e no looping, irá preencher o lstProdutos com cada nome (ver Listagem 5).
#Region " Monta a lista de produtos "
Private Sub MontaProdutos()
Try
Dim conn As New SqlConnection(conexao)
Dim sql As String = "Select ProductName FROM Products"
Dim cmd As New SqlCommand(sql, conn)
conn.Open()
Dim reader As SqlDataReader = cmd.ExecuteReader()
lstProdutos.Items.Clear()
While reader.Read()
lstProdutos.Items.Add(reader("ProductName"))
End While
reader.Close()
conn.Close()
Catch ex As SqlException
MessageBox.Show(ex.Message)
End Try
End Sub
#End Region
Digite o código para o botão btnQtde, o qual irá pesquisar a quantidade de produtos existentes na tabela Products. Note que a instrução SQL é uma função de agregação Count(*) (ver Listagem 6). Observe ainda que é chamada a função GetExecuteScalar a ser criada por nós porque iremos implementar esta mesma funcionalidade nos demais controles. Esta rotina precisa de dois argumentos, a instrução SQL e o título da janela a ser exibido.
Private Sub btnQtde_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnQtde.Click
Try
GetExecuteScalar("Select Count(*) FROM Products", "Produtos cadastrados: ")
Catch ex As Exception
MessageBox.Show(ex.Message)
End Try
End Sub
Digite a função GetExecuteScalar, a qual recebe dois argumentos do tipo String: sql, que é a respectiva instrução SQL; title, que é o título do MessageBox. Note que existe um tratamento de erro com o Try/Catch e, caso dê algum erro, o Throw no Catch é que avisará a rotina que a chamou que houve um erro. Neste caso, você precisará tratar o erro no Catch da respectiva rotina.
Nesta rotina temos a abertura da conexao com o banco de dados usando a variável conexao aberta anteriormente, um SqlCommand para executar a instrução SQL da respectiva conexão definida, e por fim, a abertura da conexão.
Em seguida é exibido um MessageBox.Show para exibir o resultado. No entanto, este resultado é justamente a execução do método ExecuteScalar do objeto cmd (SqlCommand). Note que ele é convertido para ToString (no C# use o Convert.ToString()) porque o ExecuteScalar retorna um tipo Object. Por fim, é fechada a conexão (ver Listagem 7).
Private Sub GetExecuteScalar(ByVal sql As String, ByVal title As String)
Try
Dim conn As New SqlConnection(conexao)
Dim cmd As New SqlCommand(sql, conn)
conn.Open()
MessageBox.Show(title + cmd.ExecuteScalar().ToString(), "SQL Magazine")
conn.Close()
Catch ex As SqlException
Throw
End Try
End Sub
Com a rotina GetExecuteScalar criada, digite o código para ser disparado quando você clicar no botão btnEstoque ou selecionar um produto no ListBox (ver Listagem 8). Note que no VB.NET isto é definido no Handles da linha Private Sub, facilitando a maneira de chamar rotinas, ou seja, basta declarar o objeto seguido do evento. Neste caso, temos o objeto lstProdutos seguido do evento SelectedIndexChanged e o objeto btnEstoque seguido do evento Click.
Adicione um IF para saber se algum produto está selecionado, caso contrário, exiba uma mensagem ao usuário e abandone a rotina. Caso tenha um produto selecionado, implemente a rotina de tratamento de erro Try/Catch e chame a rotina GetExecuteScalar passando como argumento a instrução SQL, o qual seleciona o campo UnitsInStock da tabela Products com a condição Where ProductName igual ao produto selecionado.
Observe o uso do Replace na condição, pois se algum produto contiver o apóstrofo, o mesmo será trocado por dois apóstrofos para evitar erros de pesquisa.
Private Sub btnEstoque_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles
lstProdutos.SelectedIndexChanged, btnEstoque.Click
If lstProdutos.SelectedIndex = -1 Then
MessageBox.Show("Por favor, selecione um produto")
Exit Sub
End If
Try
GetExecuteScalar("Select UnitsInStock FROM Products Where ProductName='" + _
lstProdutos.SelectedItem.ToString.Replace("'", "''") + "'", _
"Qtde em estoque: ")
Catch ex As Exception
MessageBox.Show("O produto contém caracteres inválidos")
End Try
End Sub
Salve o projeto e execute-o para ver o resultado (Figura 4). Faça os testes com os botões e caso queira, aplique um contador para saber o tempo de resposta fantástico que terá.
DataSet
DataSet é um conceito fácil de entender. Imagine que você tenha um repositório na memória RAM contendo quantas tabelas forem necessárias, e cada uma destas tabelas podem ter origens de dados diferentes, seja um banco de dados SQL Server, outra do Oracle, outra de um arquivo XML, outra da pasta Contatos do Outlook, enfim, a origem dos dados não importa neste caso, o importante é saber que o DataSet é um espaço na memória que pode conter diversas tabelas (chamadas DataTables). Qual é o limite de DataTables no DataSet? É o limite da memória!
Um DataSet é utilizado em situações onde você precisa manter os dados na memória, seja por motivos de atualizações dos mesmos, relacionamentos, paginações (no caso do ASP.NET), enfim, os dados estarão sempre na memória e nas respectivas DataTables.
Uma DataTable contém linhas (Rows) e colunas (Columns) e não necessariamente devem ter uma fonte de dados, você pode criar e manipular uma DataTable somente na memória. Cada linha de uma DataTable contém o status da mesma, informando se foi adicionada, excluída, alterada, etc. Com isso, você poderá usar o método Update do DataAdapter para sincronizar os dados com a tabela na fonte de dados.
Tudo o que você precisa para criar um DataSet é uma conexão, um DataAdapter, um DataSet, uma instrução SQL (no caso de ser uma tabela oriunda de um banco de dados) para preencher a DataTable com o comando Fill no respectivo DataSet.
Como disse no início, o foco deste artigo é abordar as diversas maneiras de consultar dados, mas o objeto DataAdapter permite instruções Insert, Delete, Select e Update. Pesquise sobre este objeto para saber maiores informações.
Crie um novo formulário chamado DataSetGeral contendo os seguintes controles (Figura 5): dois Buttons, dois DataGrids (gridCategorias e gridProdutos), um ListBox (lstCategorias) e um TextBox (txtClientes, TextMode=Multiline).
Pressione F7 para exibir a janela de códigos e adicione a referência ao namespace do SQL Server na primeira linha.
Imports System.Data.SqlClient
Digite a string de conexão que usaremos nos dois botões.
Dim conexao As String = "Server=(local);DataBase=Northwind;User ID=sa;Password="
Digite o código para o botão btnDataTable (ver Listagem 9), o qual irá preencher todos os controles com os respectivos dados oriundos de cada uma das tabelas declaradas contidas no Northwind.
Private Sub btnDataTable_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles
btnDataTable.Click
Dim conn As New SqlConnection(conexao)
'instruções SQL
Dim sqlCategories As String = "Select CategoryID, CategoryName From Categories"
Dim sqlCustomers As String = "Select CompanyName, ContactName From Customers"
Dim sqlProducts As String = "Select * From Products"
'DataAdapters
Dim adapterCategories As New SqlDataAdapter(sqlCategories, conn)
Dim adapterCustomers As New SqlDataAdapter(sqlCustomers, conn)
Dim adapterProducts As New SqlDataAdapter(sqlProducts, conn)
'define o DataSet
Dim ds As New DataSet
conn.Open()
'Fill no DataSet
adapterCategories.Fill(ds, "categorias")
adapterCustomers.Fill(ds, "clientes")
adapterProducts.Fill(ds, "produtos")
conn.Close()
gridCategorias.DataSource = ds.Tables("categorias").DefaultView
With lstCategorias
.DataSource = ds.Tables("categorias").DefaultView
.DisplayMember = "CategoryName"
.ValueMember = "CategoryID"
End With
txtClientes.Text = String.Empty
Dim cliente As DataRow
For Each cliente In ds.Tables("clientes").Rows
txtClientes.Text += cliente("CompanyName").ToString() + _
" - " + cliente("ContactName") + vbNewLine
Next
gridProdutos.DataSource = ds.Tables("produtos").DefaultView
End Sub
Veja a explicação detalhada do código. É declarada a string de conexão para abrir o banco de dados e existem três variáveis do tipo string contendo as três instruções SQL que selecionam os dados de diferentes tabelas. Note que associei o nome de cada variável conforme a tabela no Select.
Dim conn As New SqlConnection(conexao)
'instruções SQL
Dim sqlCategories As String = "Select CategoryID, CategoryName From Categories"
Dim sqlCustomers As String = "Select CompanyName, ContactName From Customers"
Dim sqlProducts As String = "Select * From Products"
São declarados os três DataAdapters das respectivas instruções SQL e conexão. Cada DataAdapter precisa ser declarado para que o DataSet saiba qual DataTable conterá os respectivos dados usados no método Fill, a seguir.
Dim adapterCategories As New SqlDataAdapter(sqlCategories, conn)
Dim adapterCustomers As New SqlDataAdapter(sqlCustomers, conn)
Dim adapterProducts As New SqlDataAdapter(sqlProducts, conn)
É declarado o DataSet e aberta a conexão. Note que temos apenas um DataSet e três DataAdapters.
Dim ds As New DataSet
conn.Open()
Aqui usamos o método Fill do respectivo DataAdapter para preencher a DataTable na memória. É exatamente nesta hora que a tabela é preenchida no DataSet. A declaração dos nomes das tabelas não é obrigatório, mas desejável para facilitar o entendimento. Caso queira omitir os nomes das tabelas, quando precisar usá-las terá que se referir pelo índice. Apesar do uso do índice ser mais rápido, nem sempre é a melhor opção quando temos diversas tabelas, simplesmente por uma questão de entendimento. Em seguida, como os dados já estão na memória, você deve fechar a conexão com o método Close.
adapterCategories.Fill(ds, "categorias")
adapterCustomers.Fill(ds, "clientes")
adapterProducts.Fill(ds, "produtos")
conn.Close()
Veja como associar a origem do gridCategorias a respectiva tabela contida no DataSet chamada “categorias”. Basta você usar o nome do DataSet (ds) seguido do Tables e do nome da respectiva tabela. Caso opte pelo uso do índice, a sintaxe seria ds.Tables(0). O uso do DefaultView não é obrigatório, pois em situações onde você alterou a estrutura da DataTable na memória, o uso do DefaultView recupera a estrutura original somente para efeito de exibição de dados.
gridCategorias.DataSource = ds.Tables("categorias").DefaultView
No caso do ListBox lstCategorias, temos a mesma forma de definir a origem dos dados, ou seja, no DataSource é declarado DataSet (ds) seguido da DataTable “categorias”. Neste controle é preciso informar qual o campo será exibido (displayMember = CategoryName) e o campo a ser armazenado (ValueMember = CategoryID).
With lstCategorias
.DataSource = ds.Tables("categorias").DefaultView
.DisplayMember = "CategoryName"
.ValueMember = "CategoryID"
End With
Já no TextBox MultiLine, o mesmo é limpo e, em seguida é feito um looping para percorrer todas as linhas contidas na tabela clientes. Para cada linha lida é exibido o conteúdo no txtClientes. Note a forma de referenciar um campo, ou seja, cliente (que é o objeto DataRow) seguido do nome da coluna. Em seguida é usada a constante vbNewLine para provocar uma quebra de linha.
txtClientes.Text = String.Empty
Dim cliente As DataRow
For Each cliente In ds.Tables("clientes").Rows
txtClientes.Text += cliente("CompanyName").ToString() + _
" - " + cliente("ContactName") + vbNewLine
Next
Por fim, resta montar a origem do gridProdutos, que é a tabela produtos contida no DataSet.
gridProdutos.DataSource = ds.Tables("produtos").DefaultView
Já o código do botão btnUmDataSet tem uma novidade, mesmo para quem já trabalhou com ADO.NET ou já montou diversos códigos conforme o outro botão (ver Listagem 10). Este código usa o recurso que o DataAdapter contém para criar um único DataSet com diversas instruções Select no mesmo DataAdapter. Neste caso, a memória terá um conjunto de DataTables associadas por um índice, e indico mapear a memória com as DataTables para facilitar o uso.
Private Sub btnUmDataSet_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles
btnUmDataSet.Click
Dim conn As New SqlConnection(conexao)
Dim sql As New System.Text.StringBuilder
sql.Append("Select CategoryID, CategoryName From Categories;")
sql.Append("Select CompanyName, ContactName From Customers;")
sql.Append("Select * From Products;")
Dim adapter As New SqlDataAdapter(sql.ToString(), conn)
Dim ds As New DataSet
conn.Open()
adapter.TableMappings.Add("Dados", "Categorias")
adapter.TableMappings.Add("Dados1", "Clientes")
adapter.TableMappings.Add("Dados2", "Produtos")
adapter.Fill(ds, "Dados")
conn.Close()
gridCategorias.DataSource = ds.Tables("Categorias").DefaultView
With lstCategorias
.DataSource = ds.Tables("Categorias").DefaultView
.DisplayMember = "CategoryName"
.ValueMember = "CategoryID"
End With
txtClientes.Text = String.Empty
Dim cliente As DataRow
For Each cliente In ds.Tables("Clientes").Rows
txtClientes.Text += cliente("CompanyName").ToString() + _
" - " + cliente("ContactName") + vbNewLine
Next
gridProdutos.DataSource = ds.Tables("Produtos").DefaultView
End Sub
Veja a explicação detalhada. A conexão é definida e instanciada na variável conn. Como existem diversas instruções SQL a serem executadas, e todas farão parte da mesma instrução Fill do DataAdapter, então, é indicado usar a classe System.Text.StringBuilder. Toda e qualquer manipulação de strings com o StringBuilder é mais rápida que qualquer outro meio. Neste caso, observe todas as instruções SQL Select.
Dim conn As New SqlConnection(conexao)
Dim sql As New System.Text.StringBuilder
sql.Append("Select CategoryID, CategoryName From Categories;")
sql.Append("Select CompanyName, ContactName From Customers;")
sql.Append("Select * From Products;")
Em seguida é definido apenas um DataAdapter, o qual será executado com as respectivas instruções SQL na conexão atual. É definido o DataSet que conterá todas as DataTables e, por fim, é aberta a conexão.
Dim adapter As New SqlDataAdapter(sql.ToString(), conn)
Dim ds As New DataSet
conn.Open()
Agora é a hora de mapear a memória com as DataTables. Para isso, use o adapter.TableMappings.Add, o qual possui dois argumentos: o nome da Table atual na memória e o novo nome. É importante saber como as Tables são nomeadas na memória e a regra é simples. O método Fill informa ao DataSet o nome da DataTable, neste caso, Dados. Sendo assim, na memória são criadas as Tables chamadas Dados1, Dados2 e assim sucessivamente, ou seja DadosN. Este número (índice) é de acordo com o número de Selects executados nas instruções SQL. Apesar de parecer estranho mapear antes de preencher, o DataAdapter requer isto. Sendo assim, você já sabe como as DataTables são nomeadas e a regra para renomea-las.
adapter.TableMappings.Add("Dados", "Categorias")
adapter.TableMappings.Add("Dados1", "Clientes")
adapter.TableMappings.Add("Dados2", "Produtos")
adapter.Fill(ds, "Dados")
O restante do código é idêntico ao anterior, onde usei as tabelas nomeadas para preencher os controles. Caso não tivessem renomeadas, bastaria usar o nome original contido no DataSet, ou seja, Dados, Dados1 e Dados2. Salve o projeto e execute-o para ver o resultado (Figura 6).
Este tipo de execução com diversos Selects no mesmo DataSet tem vantagens de performance em algumas situações. Por exemplo, precisamos abrir a conexão apenas uma vez, assim como dispensamos a declaração de vários DataAdapters e do Fill.
Conclusão
Saber identificar qual é o tipo de classe e método A ser utilizado para pesquisar dados no ADO.NET é fundamental e pode determinar o sucesso ou o fracasso da sua aplicação. É importante que você aplique os recursos utilizados neste artigo com o objetivo de medir a performance da sua aplicação antes e depois. Ou seja, caso a sua aplicação esteja diferente, faça uma medição do tempo de resposta atual e em seguida troque para as técnicas aprendidas aqui e reavalie o tempo de resposta.