C# - ADO .NET para iniciantes V - SqlDataAdapter e DataSet


Esta chegando agora ???

Então acompanhe os primeiros artigos:

Se você já conhece VB .NET e esta querendo a aprender C# sugiro que você leia o meu artigo:

O material necessário para acompanhar o curso é: (Irei utilizar o Visual C# 2008 Express Edition)

1- Visual C# 2008 Express Edition (vou dar preferência ao Visual C#)
2- SharpDevelop 2.2
(opcional)
3- SQL Server 2005 Express Edition

No artigo anterior escrevemos sobre o DataReader, e, embora o objeto DataReader seja rápido e muito simples de usar, em muitas situações, teremos que efetuar operações em nossos dados que vão além da exibição e movimentação somente para frente; muitas vezes teremos que realizar operações de inclusão, alteração, exclusão, etc., e, neste caso as classes DataSet e SqlDataAdapter são as mais indicadas para serem usadas.

A classe DataSet

O DataSet representa um conjunto de dados em memória retornados de uma fonte de dados e consiste de uma coleção de objeto DataTable que você pode inter-relacionar usando os objetos DataRelations.  Você também pode forçar a integridade dos dados em um DataSet usando os objetos UniqueConstraints e ForeignKeyConstraints.

Um DataSet é essencialmente um banco de dados em memória, contendo múltiplas tabelas, constraints, consultas, ordenações e a habilidade de persistir o seu estado em um arquivo XML.

Você pode usar um SqlDataAdapter  para preencher um DataSet com linhas de uma consulta feita em um banco de dados SQL Server. (Se acessar um banco de dados Access você usa um objeto OledbDataAdapter).

Uma vez populadas você pode realizar alterações no DataSet, incluindo e excluindo linhas (rows), efetuando ordenações, etc, e então usar o objeto SqlDataAdpater novamente para refletir estas alterações no banco de dados original usando um comando SQL apropriado como UPDATE, DELETE, INSERT.

Os dados estão contidos essencialmente no objeto DataTable mas é a coleção DataRelationCollection que permite que você a hierarquia da tabela. As tabelas estão contidas na coleção DataTableCollection que é acessada através da propriedade Tables.

Lembre-se que ao usar um DataSet você esta trabalhando com uma cópia dos dados em memória e por isso qualquer atualização feita nos dados tem que ser refletida na fonte de dados de origem, de outra forma , se você não atualizar os dados originais com as alterações a fonte de dados não será atualizada.

Os dados internos contidos em um DataSet são mantidos no formato XML e a estrutura do DataSet é definida pelo XSD (XML Schema Definition Language), ou seja, XML e DataSet estão intimamente ligados.

A principal característica do DataSet é totalmente desconectado, você pode usar um DataSet para armazenar dados de um banco de dados e pode também mover os dados de um DataSet para um banco de dados, mas o próprio DataSet não faz conexão alguma com o banco de dados, ele nem mesmo tem um objeto para realizar tal conexão .

Resumindo as funcionalidades do DataSet:

   - Funciona como um cache in-memory, ou seja, você terá as tabelas disponíveis para uso, podendo fazer acesso aleatório aos registros, inserir, alterar e deletar registros da tabela;
   - Opera desconectado do banco, assim , você trabalha com os registros sem usar uma conexão do banco de dados;
   - Opera com n tabelas por vez, e permite a possibilidade de relacionamento entre as mesmas além de funcionalidades como ordenação, filtragem, etc.

Nota: A conexão utilizada pelo DataSet não necessita ser aberta e/ou fechada manualmente. Ao chamar o método Fill do DataAdapter a conexão é aberta, são coletados os dados solicitados e a conexão é fechada. (No caso do DataReader você tem que abrir e fechar)

A classe SqlDataAdapter

A classe SqlDataAdapter serve como uma ponte entre o DataSet e o banco de dados SQL Server para retornar e salvar dados.

O SqlDataAdapter fornece esta ponte da seguinte forma:

  1. O método Fill que altera os dados no DataSet para coincidir com os dados da fonte de dados; Quando o método Fill é executado ele cria as colunas e tabelas necessárias para os dados retornados se eles não existirem;  a informação da chave primária não é incluída a menos que a propriedade MissingSchemaAction for definida para AddWithKey.
  2. O método Update o qual altera os dados na fonte de dados para coincidir com os dados do DataSet usando o comando T-SQL apropriado contra a fonte de dados.  A atualização é feita da seguinte forma: para cada linha inserida, modificada e deletada o método Update determina o tipo de alteração que foi realizada (Insert, Update ou Delete) e dependendo do tipo de alteração, o comando Insert, Update ou Delete é executado para propagar a linha modificada para a fonte de dados.

De forma geral em uma implementação com mais de uma camada os passos para criar e atualizar um DataSet e em seguida atualizar os dados originais são:

  1. Construir e preencher cada DataTable no DataSet com os dados da fonte de dados usando um objeto DataAdapter;
  2. Alterar os dados nos objetos DataTable individuais pela inclusão, atualização ou exclusão de objetos DataRow;
  3. Invocar o método GetChanges para criar um segundo DataSet que representa somente as alterações feitas;
  4. Chamar o método Update do DataAdapter passando o segundo DataSet como um argumento;
  5. Invocar o método Merge para mesclar as alterações a partir do segundo DataSet no primeiro;
  6. Invocar o método AcceptChanges no DataSet ou invocar o método RejectChanges para cancelar as alterações;

Criando um DataSet

Todos as classes relacionadas como DataSet, DataAdapter, DataTable, DataRow etc estão disponíveis no namespace System.Data.

A primeira coisa a fazer é definir o namespace que pode ser feito da seguinte forma:

Em uma página ASP .NET  Em um arquivo C#:
<%@ Import Namespace="System.Data" %>   using System.Data;

Para criar um objeto DataSet você usa a seguinte sintaxe:  DataSet <nomedoDataSet> = new DataSet();

DataSet dsClientes = new DataSet();

Observe que o construtor DataSet não requer nenhum parâmetro, porém existe um construtor sobrecarregado que aceita uma string para o nome do DataSet o qual é usado se você deseja serializar os dados para o formato XML.

Criando um SqlDataAdater

Um SqlDataAdapter trata os comandos SQL e o objeto Connection para leitura e escrita de dados.  Para inicializar um SqlDataAdaptar informamos um comando SQL e um objeto Connection que definia conexão com a fonte de dados:

SqlDataAdapter daClientes = new SqlDataAdapter("Select codigo, Nome from Clientes", conexaoBD);

No código acima criamos um novo SqlDataAdpater , daClientes onde o comando SQL Select especifica que iremos ler os dados para o DataSet e o objeto Connection , conexaoBD, que deverá já ter sido iniciado, mas não aberto.

É responsabilidade do SqlDataAdpater abrir e fechar a conexão durante a chamada dos métodos Fill e Update.

Para usar comandos SQL para incluir, atualizar e deletar temos duas maneiras distintas:

O objeto SqlDataAdapter não gera automaticamente comandos Transact-SQL necessários para ajustar as alterações feitas a um DataSet associado com uma instância do SQL Server.

Você pode , no entanto, criar um objeto SqlCommandBuilder para gerar automaticamente estes comandos para atualizações em uma tabela única . Veja abaixo um exemplo :

   SqlCommandBuilder cmdBldr = new SqlCommandBuilder(daClientes);
 

O comando SqlCommandBuilder é instanciado usando como parâmetro a instância do SqlDataAdatper, no caso daClientes, dessa forma o  SqlCommandBuilder irá ler os comandos SQL e atribuir os novos comandos as propriedades Insert, Update e Delete do SqlDataAdapter.

o SqlCommandBuilder tem a limitação de gerar os comandos apenas para uma única tabela, se você precisar trabalhar com mais de uma tabela ou usar um Join terá que usar as propriedades da classe SqlDataAdapter.

Preenchendo um DataSet

Depois de criar instâncias de um DataSet e de um SqlDataAdpater você precisa preencher o DataSet usando o método Fill do SqlDataAdapter:

daClientes.Fill(dsClientes, "Clientes");
 

O método Fill , usa dois parâmetros :

Nota: O método Fill possui uma sobrecarga que usa somente o DataSet; neste caso a tabela criada terá um nome padrão de "table1" para a primeira tabela.

Exemplos de utilização de um DataSet

Recursos usados nos exemplos a seguir:

1- Usando um DataSet com SqlDataAdapter para selecionar dados no SQL Server

Neste exemplo estamos apenas acessando o SQL Server 2005 e selecionando dados da tabela Products. Como não estamos alterando os dados não precisamos usar o objeto SqlCommandBuilder para preparar o DataSet para realizar tais operações.

- Crie um novo projeto no Visual C# com o nome uDataSet1 do tipo Windows Forms Application ;

- No formulário padrão form1.cs inclua um controle DataGridView - gdvProdutos e um controle Button - btnPreencheGrid ;

- Defina o namespace System.Data.SqlClient

- Declare as variáveis objetos que serão usadas no projeto:

//define os objetos SqlConnection, DataSet

//SqlDataAdapter e o nome da tabela

private SqlConnection conn;

private SqlDataAdapter daProdutos;

private DataSet dsProdutos;

private const string tabela = "Produtos";

-  Inclua o seguinte código no evento Click do controle Button:

private void btnPreencheGrid_Click(object sender, EventArgs e)

{

     //chama a rotina para iniciar o dataset

    IniciaDados();

    //exibe o dataset no controle datagridview - dgvProdutos

    dgvProdutos.DataSource = dsProdutos;

    dgvProdutos.DataMember = tabela;

}

- A rotina IniciaDados() possui o seguinte código:

// definição dos objetos dataset, sqldataadapter e sqlcommandbuilder

public void IniciaDados()

{

   try

   {

       //instancia uma nova conexão usando a string de conexão

         //com o banco de dados Northwind do SQL Server 2005 Express

      conn = new SqlConnection("Server=MAC\\SQLEXPRESS;DataBase=Northwind;Integrated Security=SSPI");


      // 1. instancia um novo DataSet

     dsProdutos = new DataSet();
 

      // 2. inicia o SqlDataAdapte passando o comando SQL para selecionar codigo e nome

         // do produto e a conexão com o banco de dados

      daProdutos = new SqlDataAdapter("Select ProductID, ProductName from Products", conn);

 

      // 3. preenche o dataset

      daProdutos.Fill(dsProdutos, tabela);

   }

   catch(Exception ex)

   {

       //se houver erro exibe a mensagem

           MessageBox.Show(ex.Message);

    }

}

O resultado obtido pode ser visto na figura abaixo:

2- Usando um DataSet com SqlDataAdapter para incluir, alterar e excluir dados no SQL Server

Vamos usar o exemplo anterior e incluir no formulário mais 3 botões de comandos para incluir, alterar e excluir dados da tabela Products;

Incluir - btnIncluir

Alterar - btnAlterar

Excluir- btnExcluir

Vamos definir a variável do codigo como do tipo String que irá armazenar o código do produto.

private String codigo = "";

A seguir temos o código do botão - Listar Dados - que após obter a lista de produtos habilita os botões de comando:

   private void btnPreencheGrid_Click(object sender, EventArgs e)
        {
            IniciaDados();
            //exibe o dataset no controle datagridview - dgvProdutos
            dgvProdutos.DataSource = dsProdutos;
            dgvProdutos.DataMember = tabela;
            btnIncluir.Enabled = true;
            btnExcluir.Enabled = true;
            btnAlterar.Enabled = true;
        }

 

No evento Click do botão Incluir temos o código que inclui um novo registro na tabela Products. Estamos usando o método NewRow() e incluindo uma nova linha na tabela; Estou incluindo um produto com o nome Macoratti_

Estamos usando o procedimento básico para incluir dados em um dataset : criando uma nova linha e em seguida adicionando a coleção DataRow do DataTable no DataSet

No nosso caso estamos fazendo isso em um DataSet não tipado chamando o método NewRow para criar uma nova linha vazia; esta linha herda a estrutura da coluna da coleção DataColumnCollection. Em seguida incluímos(add) a linha na coleção de linhas e atualizamos(update) o dataset.

      private void btnIncluir_Click(object sender, EventArgs e)
        {
            daProdutos = new SqlDataAdapter("SELECT ProductID, ProductName FROM Products", conn);
            SqlCommandBuilder cmdbldr = new SqlCommandBuilder(daProdutos);
            dsProdutos = new DataSet();
            daProdutos.Fill(dsProdutos);
            DataRow registro = dsProdutos.Tables[0].NewRow();
            registro["ProductName"] = "Macoratti_";
            dsProdutos.Tables[0].Rows.Add(registro);
            //atualiza o dataset
            daProdutos.Update(dsProdutos);
            MessageBox.Show("Registro incluido.");
        }

 

O código do evento Click do botão Alterar é visto a seguir.

- No código obtemos o valor do código do produto a partir da linha selecionada no DataGridView e armazenamos na variável codigo;
- Em seguida verificamos se o codigo possui um valor válido;
- Usamos o método Find do DataTable para localizar o registro com o código obtido e atualizamos o nome do produto com um sinal de #;

     private void btnAlterar_Click(object sender, EventArgs e)
        {
             daProdutos = new SqlDataAdapter("SELECT ProductID, ProductName FROM Products order by ProductID", conn);
            SqlCommandBuilder cmdbldr = new SqlCommandBuilder(daProdutos);
            dsProdutos = new DataSet();
            daProdutos.Fill(dsProdutos);
            DataColumn[] chave = new DataColumn[1];
            chave[0] = dsProdutos.Tables[0].Columns[0];
            dsProdutos.Tables[0].PrimaryKey = chave;
            codigo = dgvProdutos.CurrentRow.Cells["ProductID"].Value.ToString();
            if (codigo != null || !codigo.Equals(""))
            {
                DataRow registro = dsProdutos.Tables[0].Rows.Find(codigo);
                registro["ProductName"] = registro["ProductName"] + " # ";
                daProdutos.Update(dsProdutos);
                MessageBox.Show("Registro alterado.");
            }
            else
            {
                MessageBox.Show("Registro não localizado.");
            }
       }

O código do evento Click do botão Excluir é visto a seguir.

- No código obtemos o valor do código do produto a partir da linha selecionada no DataGridView e armazenamos na variável codigo;
- Em seguida verificamos se o codigo possui um valor válido;
- Usamos o método Find do DataTable para localizar o registro com o código obtido;
- Para excluir usamos o método Delete;

     private void btnExcluir_Click(object sender, EventArgs e)
        {
            daProdutos = new SqlDataAdapter("SELECT ProductID, ProductName FROM Products order by ProductID", conn);
            SqlCommandBuilder cmdbldr = new SqlCommandBuilder(daProdutos);
            dsProdutos = new DataSet();
            daProdutos.Fill(dsProdutos);
            DataColumn[] chave = new DataColumn[1];
            chave[0] = dsProdutos.Tables[0].Columns[0];
            dsProdutos.Tables[0].PrimaryKey = chave;
            codigo = dgvProdutos.CurrentRow.Cells["ProductID"].Value.ToString();
            if (codigo != null || !codigo.Equals(""))
            {
               DataRow registro = dsProdutos.Tables[0].Rows.Find(codigo);
               registro.Delete();
               daProdutos.Update(dsProdutos);
               MessageBox.Show("Registro excluido.");
            }
            else
            {
                MessageBox.Show("Registro não localizado.");
            }
        }

O resultado obtido pode ser visto na figura abaixo:

Com isso mostrei os procedimentos básicos de como atualizar um dataset não tipado usando ADO .NET;

Pegue o projeto completo aqui : uDataSet1.zip

No próximo artigo irei falar mais um pouco sobre dataset , mais particularmente dos datasets tipados.

Eu sei é apenas C# e 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