1. Início

Dando continuidade à nossa série de artigos, neste post vamos mostrar alguns exemplos de como podemos estabelecer uma conexão e manipular dados no modelo desconectado. No artigo anterior já discutimos as vantagens e desvantagens deste modelo, vide url:

//www.devmedia.com.br/articles/viewcomp.asp?comp=11748

2. Objetos utilizados no modelo desconectado

A idéia do modelo desconectado é bastante simples: Recuperar dados do banco para a memória, e manipulá-los sem ser necessário fazer novas requisições à database.

Para fazer isto, nós utilizamos algumas classes já discutidas no post anterior, que são eles:

  1. SqlConnection – Objeto responsável por saber e mantér conexão com o banco de dados.
  2. DataSet – Estrutura complexa que irá representar nossos dados em memória.
  3. SqlDataAdapter – É uma ponte entre o Dataset e o SqlConnection. É o objeto responsável pelo fluxo de dados entre a memória e o banco, e vice-versa.

3. Começando a codificar

O fluxo básico de qualquer modelo desconectado é sempre o mesmo: Resgata Dados –> Manipula dados –> Sincroniza dados com banco.

Nesses nossos exemplos iremos utilizar a mesma estrutura do banco que o post anterior, ou seja, as nossas operações serão em cima de nossa tabela “Pessoa”.

Caso não tenha acompanhado o primeiro post, volto à repetir: deem, nem que seja rapidamente, uma lida no mesmo.

3.1 Resgatando dados do banco

Vamos ser bastante direto nesse post, enrolando menos e fazendo mais exercícios práticos. Logo, o nosso código para resgatar valores para a memória é:

   1: public static void Main(string[] args)
   2:        {
   3:            string connectionString = @"Data Source=.\SQL;Initial Catalog=EstudoBlog;Integrated Security=True;Pooling=False";
   4:            string query = @"SELECT * FROM Pessoa";
   5:
   6:            //
   7:            //Instância objeto que irá manipular e manter conexão com banco
   8:            //
   9:            SqlConnection conn = new SqlConnection(connectionString);
  10:
  11:            //
  12:            // Objeto que irá armazenar dados vindos do banco
  13:            //
  14:            DataSet ds = new DataSet();
  15:
  16:            //
  17:            //Objeto que servirá como ponte de uma determinada conexão e um dataset qualquer
  18:            //
  19:            SqlDataAdapter adapter = new SqlDataAdapter(query, conn);
  20:
  21:            //
  22:            //Atraves da ponte, preencho o dataset com dados buscados no banco
  23:            //
  24:            adapter.Fill(ds);
  25:        }

Viram como é simples? Pois é. Seis linha de código e já temos os dados em mãos. Acho que não tem nem o que explicar desse código, pois creio que todos entenderam que o SqlDataAdapter funciona como uma ponte de dados entre banco e memória.

- “Opa Felipe. Mas esse seu código vai dar erro!!! Você esqueceu de abrir a conexão!!!”

Calma meus caros leitores!!!

Ao se utilizar o DataAdapter não precisamos ficar preocupado em abrir e fechar conexões. Pois, por debaixo dos panos o adapter irá abrir a conexão, logo após irá executar o comando e em seguida irá fechar a conexão com o banco, sendo tudo isso invisível ao usuário.

Um fato importante no SqlDataAdapter é que após executar o comando, ele deixa a conexão no mesmo estado que ela se encontrava. Ou seja, caso a conexão se encontrasse aberta ao executar a instrução  “adapter.Fill(ds)” , o SqlDataAdapter permaneceria com a mesma aberta ao final do comando.

- “Humm… Certo! Entendi! Trouxemos os dados do banco para o dataset, mas como consigo listá-los?”

Como eu disse, o DataSet tem uma estrura semelhante ao banco, com tabelas, linhas, colunas. Da seguinte maneira:

ScreenShot014

Sabendo disso, podemos exibir nossos dados pegando a nossa primeira tabela do DataSet que é a tabela de pessoa, em seguida podemos iterar nas linhas da mesma, da seguinte maneira:

   1: public static void Main(string[] args)
   2:        {
   3:            string connectionString = @"Data Source=.\SQL;Initial Catalog=EstudoBlog;Integrated Security=True;Pooling=False";
   4:            string query = @"SELECT * FROM Pessoa";
   5:
   6:            //
   7:            //Instância objeto que irá manipular e manter conexão com banco
   8:            //
   9:            SqlConnection conn = new SqlConnection(connectionString);
  10:
  11:            //
  12:            // Objeto que irá armazenar dados vindos do banco
  13:            //
  14:            DataSet ds = new DataSet();
  15:
  16:            //
  17:            //Objeto que servirá como ponte de uma determinada conexão e um dataset qualquer
  18:            //
  19:            SqlDataAdapter adapter = new SqlDataAdapter(query, conn);
  20:
  21:            //
  22:            //Atraves da ponte, preencho o dataset com dados buscados no banco
  23:            //
  24:            adapter.Fill(ds);
  25:
  26:            //
  27:            // Pega a primeira tabela do dataset, que é a tabela de pessoa
  28:            //
  29:            DataTable dtPessoa = ds.Tables[0];
  30:
  31:            //
  32:            // iteração nas linhas da tabela de pessoa.
  33:            //
  34:            foreach (DataRow row in dtPessoa.Rows)
  35:            {
  36:                //
  37:                //exibição dos dados no console
  38:                //
  39:                Console.WriteLine(row["id"] + " "  + row["nome"] + " " + row["email"]);
  40:            }
  41:        }

Observe que as linhas estam indexadas. Ou seja, ao fazer        “row[‘nome’]”, estou pegando o nome de uma determinada linha.

3.2 Inserindo valores no DataSet

Como falamos anteriormente, os dados serão manipulados em memória, ou seja, iremos adicionar uma nova pessoa diretamente na DataTable.

- “E como sincronizo as alterações que fiz no DataSet com o meu banco?”

Isso é bastante simples de se fazer.  Básicamente, o objeto adapter possui um método chamado Update, que recebe um DataSet como parâmetro, e com base nesse DataSet o Adapter faz as modificações necessárias.

ScreenShot001

-“Certo! Mas como o SqlDataAdapter sabe as modificações que eu fiz no DataSet?”

A resposta para essa pergunta, também é simples. Cada DataRow possui uma propriedade chamada  “RowState” e de acordo com seu valor o SqlDataAdapter irá tomar as providências necessárias na hora da sincronização com o banco. A propriedade “RowState” pode assumer os seguintes valores:

  1. Added – Quando a linha é adicionada em uma DataRowCollection.
  2. Deleted -  A linha fica neste estado quando é chamado o método “Delete()” da mesma.
  3. Detached – Considero este valor da enumeração o mais complicado de se entender, mas isso não quer dizer que é impossível. Básicamente, a linha fica neste estado quando ela não pertende à nenhum DataRowCollection, ou seja, quando acabamos de criar uma linha e ela ainda não foi adicionada à estrutura alguma, ou quando removemos a linha de uma DataRowCollection.A linha se encontra neste estado, por exemplo, no seguinte caso:“DataRow dr = ds.Tables[0].NewRow();”Neste caso, temos a linha (dr), mas que não pertence a estrutura alguma. Ela passará para o estado de “added” quando eu fizer isto:ds.Tables[0].Rows.Add(dr);Agora sim a linha pertênce a primeira tabela de meu DataSet.
  4. Modified – Linha fica neste estado quando é modificado.
  5. Unchanged – Valor default das linhas quando veem do banco.

- “Que ótimo!!! Então quer dizer que só preciso chamar o Update() e pronto?”

Bem caros leitores, não é bem assim. Nem tudo é mar de rosas.

Antes de chamarmos o método Update() devemos configurar o nosso DataAdapter para que ele aceite todos os comando (INSERT, UPDATE, DELETE).

Isso precisa ser feito, porque quando chamamos o método Update, ele sabe o que deve fazer com cada linha. Ele sabe que deve inserir um linha, remover outra e talvez atualizar outra, por exemplo. Mas o SqlDataAdapter por se só não sabe como fazer isto. Ou seja, ele sabe o que precisa fazer, mas não como fazer. É por isto que devemos configurar cada comando. E isto é feito da seguinte maneira:

   1: static void ConfiguraDataAdapter(SqlDataAdapter adapter, SqlConnection conn)
   2:        {
   3:            string insertQuery = "INSERT INTO Pessoa (nome, dataNascimento, email, sexo) VALUES (@nome, @dataNascimento, @email, @sexo)";
   4:            SqlCommand cmd = new SqlCommand(insertQuery, conn);
   5:            cmd.Parameters.Add("nome", SqlDbType.NVarChar, 100, "nome");
   6:            cmd.Parameters.Add("dataNascimento", SqlDbType.SmallDateTime, 15, "dataNascimento");
   7:            cmd.Parameters.Add("email", SqlDbType.NVarChar, 50, "email");
   8:            cmd.Parameters.Add("sexo", SqlDbType.NChar, 1, "sexo");
   9:            adapter.InsertCommand = cmd;
  10:        }

Este método é o respónsavel por configurar os comandos do SqlDataAdapter (Observe que fiz apenas o insert, os outros comandos deixo como atividade para vocês);

Então com base em tudo que conversarmos, vamos finalmente inserir uma nova linha em nossa DataRowCollection e em seguida sincronizar com o banco. Nosso código final ficará da seguinte maneira:

   1: static Pessoa GetPessoa()
   2:       {
   3:           Pessoa pessoa = new Pessoa();
   4:           Console.WriteLine("Nome ");
   5:           pessoa.Nome = Console.ReadLine();
   6:           Console.WriteLine("Email ");
   7:           pessoa.Email = Console.ReadLine();
   8:           Console.WriteLine("Sexo (M ou F) ");
   9:           pessoa.Sexo = Convert.ToChar(Console.ReadLine());
  10:           Console.WriteLine("Data de Nascimento");
  11:           pessoa.DataNascimento = Convert.ToDateTime(Console.ReadLine());
  12:           return pessoa;
  13:       }
  14:
  15:       static void ConfiguraDataAdapter(SqlDataAdapter adapter, SqlConnection conn)
  16:       {
  17:           //
  18:           // query de insert
  19:           //
  20:           string insertQuery = "INSERT INTO Pessoa (nome, dataNascimento, email, sexo) VALUES (@nome, @dataNascimento, @email, @sexo)";
  21:
  22:           //
  23:           // Criação do comando
  24:           //
  25:           SqlCommand cmd = new SqlCommand(insertQuery, conn);
  26:
  27:           //adiciona parametros
  28:           cmd.Parameters.Add("nome", SqlDbType.NVarChar, 100, "nome");
  29:           cmd.Parameters.Add("dataNascimento", SqlDbType.SmallDateTime, 15, "dataNascimento");
  30:           cmd.Parameters.Add("email", SqlDbType.NVarChar, 50, "email");
  31:           cmd.Parameters.Add("sexo", SqlDbType.NChar, 1, "sexo");
  32:
  33:           //informa quem vai ser o comando do insert do SqlDataAdapter
  34:           adapter.InsertCommand = cmd;
  35:       }
  36:
  37:       public static void Main(string[] args)
  38:       {
  39:           string connectionString = @"Data Source=.\SQL;Initial Catalog=EstudoBlog;Integrated Security=True;Pooling=False";
  40:           string query = @"SELECT * FROM Pessoa";
  41:
  42:           //
  43:           //Instância objeto que irá manipular e manter conexão com banco
  44:           //
  45:           SqlConnection conn = new SqlConnection(connectionString);
  46:
  47:           //
  48:           // Objeto que irá armazenar dados vindos do banco
  49:           //
  50:           DataSet ds = new DataSet();
  51:
  52:           //
  53:           //Objeto que servirá como ponte de uma determinada conexão e um dataset qualquer
  54:           //
  55:           SqlDataAdapter adapter = new SqlDataAdapter(query, conn);
  56:
  57:
  58:           //
  59:           // Chama método que irá configurar todos os comando do adapter 
  60:           //
  61:           ConfiguraDataAdapter(adapter, conn);
  62:
  63:           //
  64:           //Atraves da ponte, preencho o dataset com dados buscados no banco
  65:           //
  66:           adapter.Fill(ds);
  67:
  68:           //
  69:           // Pega a primeira tabela do dataset, que é a tabela de pessoa
  70:           //
  71:           DataTable dtPessoa = ds.Tables[0];
  72:
  73:           //
  74:           // Cria linha de acordo com esquema de linhas da tabela "dtPessoa"
  75:           // NESSE INSTANTE A LINHA SE ENCONTRA NO ESTADO DE "Detached" 
  76:           //
  77:           DataRow drNewPessoa = dtPessoa.NewRow();
  78:
  79:           //
  80:           // Preenche linha com dados da pessoa 
  81:           //
  82:           Pessoa pessoa = GetPessoa();
  83:           drNewPessoa["email"] = pessoa.Email;
  84:           drNewPessoa["dataNascimento"] = pessoa.DataNascimento;
  85:           drNewPessoa["nome"] = pessoa.Nome;
  86:           drNewPessoa["sexo"] = pessoa.Sexo;
  87:
  88:           //
  89:           // Adiciono nova linha de Pessoa a tabela de pessoa
  90:           // nesse momento a linha se encontra no estado "Added"
  91:           //
  92:           dtPessoa.Rows.Add(drNewPessoa);
  93:
  94:           //
  95:           // Sincronização com o banco
  96:           //
  97:           adapter.Update(ds);
  98:
  99:           Console.WriteLine("Sincronização realizada com sucesso!!!");
 100:       }

Então agora o noso código já adiciona registros no banco. Não vou fazer as próximas operações, espero que vocês façam. Qualquer dúvidam podem comentar, ou então entrar em contato por email.

4. Finalizando

Para finalizar vamos fazer um exemplo do que podemos fazer manipulando os estados das linhas.

Sabendo que a DataRow possui um método chamado “AcceptChanges()” que básicamente coloca o estado da linha em “Unchanged”. O que você acha que vai acontecer se eu fizer uma série de alterações em linhas de uma DataTable, mas antes de dar um Update no SqlDataAdapter, eu der um AcceptChanges em todas as linhas?

Isso mesmo!!! Nenhuma linha será atualizada, pois o SqlDataAdapter só irá modificar no banco as linhas que estiverem com valores diferentes de “Unchanged”. Para vocês verem que isso acontece, rodem o seguinte código:

   1: static Pessoa GetPessoa()
   2:       {
   3:           Pessoa pessoa = new Pessoa();
   4:           Console.WriteLine("Nome ");
   5:           pessoa.Nome = Console.ReadLine();
   6:           Console.WriteLine("Email ");
   7:           pessoa.Email = Console.ReadLine();
   8:           Console.WriteLine("Sexo (M ou F) ");
   9:           pessoa.Sexo = Convert.ToChar(Console.ReadLine());
  10:           Console.WriteLine("Data de Nascimento");
  11:           pessoa.DataNascimento = Convert.ToDateTime(Console.ReadLine());
  12:           return pessoa;
  13:       }
  14:
  15:       static void ConfiguraDataAdapter(SqlDataAdapter adapter, SqlConnection conn)
  16:       {
  17:           //
  18:           // query de insert
  19:           //
  20:           string insertQuery = "INSERT INTO Pessoa (nome, dataNascimento, email, sexo) VALUES (@nome, @dataNascimento, @email, @sexo)";
  21:
  22:           //
  23:           // Criação do comando
  24:           //
  25:           SqlCommand cmd = new SqlCommand(insertQuery, conn);
  26:
  27:           //adiciona parametros
  28:           cmd.Parameters.Add("nome", SqlDbType.NVarChar, 100, "nome");
  29:           cmd.Parameters.Add("dataNascimento", SqlDbType.SmallDateTime, 15, "dataNascimento");
  30:           cmd.Parameters.Add("email", SqlDbType.NVarChar, 50, "email");
  31:           cmd.Parameters.Add("sexo", SqlDbType.NChar, 1, "sexo");
  32:
  33:           //informa quem vai ser o comando do insert do SqlDataAdapter
  34:           adapter.InsertCommand = cmd;
  35:       }
  36:
  37:       public static void Main(string[] args)
  38:       {
  39:           string connectionString = @"Data Source=.\SQL;Initial Catalog=EstudoBlog;Integrated Security=True;Pooling=False";
  40:           string query = @"SELECT * FROM Pessoa";
  41:
  42:           //
  43:           //Instância objeto que irá manipular e manter conexão com banco
  44:           //
  45:           SqlConnection conn = new SqlConnection(connectionString);
  46:
  47:           //
  48:           // Objeto que irá armazenar dados vindos do banco
  49:           //
  50:           DataSet ds = new DataSet();
  51:
  52:           //
  53:           //Objeto que servirá como ponte de uma determinada conexão e um dataset qualquer
  54:           //
  55:           SqlDataAdapter adapter = new SqlDataAdapter(query, conn);
  56:
  57:
  58:           //
  59:           // Chama método que irá configurar todos os comando do adapter 
  60:           //
  61:           ConfiguraDataAdapter(adapter, conn);
  62:
  63:           //
  64:           //Atraves da ponte, preencho o dataset com dados buscados no banco
  65:           //
  66:           adapter.Fill(ds);
  67:
  68:           //
  69:           // Pega a primeira tabela do dataset, que é a tabela de pessoa
  70:           //
  71:           DataTable dtPessoa = ds.Tables[0];
  72:
  73:           //
  74:           // Cria linha de acordo com esquema de linhas da tabela "dtPessoa"
  75:           // NESSE INSTANTE A LINHA SE ENCONTRA NO ESTADO DE "Detached" 
  76:           //
  77:           DataRow drNewPessoa = dtPessoa.NewRow();
  78:
  79:           //
  80:           // Preenche linha com dados da pessoa 
  81:           //
  82:           Pessoa pessoa = GetPessoa();
  83:           drNewPessoa["email"] = pessoa.Email;
  84:           drNewPessoa["dataNascimento"] = pessoa.DataNascimento;
  85:           drNewPessoa["nome"] = pessoa.Nome;
  86:           drNewPessoa["sexo"] = pessoa.Sexo;
  87:
  88:           //
  89:           // Adiciono nova linha de Pessoa a tabela de pessoa
  90:           // nesse momento a linha se encontra no estado "Added"
  91:           //
  92:           dtPessoa.Rows.Add(drNewPessoa);
  93:
  94:           drNewPessoa.AcceptChanges();
  95:
  96:           //
  97:           // Sincronização com o banco
  98:           //
  99:           adapter.Update(ds);
 100:
 101:           Console.WriteLine("Sincronização realizada com sucesso!!!");
 102:       }

Note que a linha jamais será adicionada, pois estou dando um “AcceptChanges()” antes de dar um “Update()” no Adapter!!!

Para baixar o código de exemplo, acesse o seguinte link:

http://cid-2d6d3503299ba131.skydrive.live.com/self.aspx/Artigos/AcessoBancoDadosII.rar


Abraços!


Este artigo encontra-se, também, em meu blog: http://fpimentel88.wordpress.com