28 de set. de 2010

Um pouco sobre ADO.NET

Como se conectar a um banco de dados com o C# ou outra linguagem da plataforma .NET?

Bem, a maioria das aplicações comerciais desenvolvidas precisam de interação com uma base de dados, para persistir informações, processá-las conforme a necessidade e disponibilizá-las posteriormente.

Dentro do .NET Framework (Plataforma com bibliotecas por trás do C#), temos um subconjunto de bibliotecas, ou um outro framework conhecido como ADO.NET (ActiveX Data Objects). Ele é responsável pela comunicação com alguma base de dados, seja em nível mais primitivo ou robusto.

Como ele faz isso? Através de classes simples e intuitivas, de fácil utilização. Também é possível plugar drivers de conexão com banco de dados nele. Isso o torna bem extensível, ou seja, é possível eu conectar com qualquer base de dados que tenha um driver escrito para o ADO.NET. Na maioria das vezes o fabricante da base de dados acaba fornecendo esse driver.

O driver é o conjunto de bibliotecas necessárias para se comunicar com a base de dados. Apesar dos bancos de dados serem os mais variados possíveis, o ADO.NET impõe um padrão, então apenas alguns detalhes irão mudar nos acessos. No geral o padrão é o mesmo. Mas o ADO.NET expande bastante esse conceito de drivers, e pela imposição do padrão, dentro do .NET eles acabam se tornando nossos Provedores (Providers) de dados.

Os provedores de dados ficam responsáveis por fornecer meio de conexão, leitura e “escrita” em um banco de dados.

Há duas formas básicas (primitivas) de se obter dados de uma consulta a uma base de dados. Uma delas é com DataSet. A outra é com DataReader.

O DataSet é um mapeamento em memória das tabelas, relacionamentos e linhas solicitada na consulta. Ele mantém um controle sobre o estado dos objetos na memória, logo ele tem a capacidade de persistir as mudanças no banco de dados.

O DataReader é um leitor de item a item (linha a linha) retornada da consulta. Ele é mais rápido por não mantém todos os metadados que o DataSet mantém. Porém é somente leitura.

No .NET temos alguns provedores nativos. São eles:

  • SQL Server (System.Data.SqlClient),
  • OLE DB (System.Data.OleDb),
  • ODBC (System.Data.Odbc),
  • Oracle (System.Data.OracleClient – Apenas até o VS 2008/.NET 3.5).

Como já comentado antes, esse provedores são expansíveis, uma prova disso é que a própria Oracle tem um provedor para o ADO.NET (e como eles conhecem melhor o caminho das pedras da base de dados deles, acaba sendo mais eficiente o provedor nativo).

Na figura abaixo é exposto a estrutura básica de um provedor:

Os objetos principais de um provedor são:

  • Connection: Gerencia a conexão com a fonte de dados.
  • Command: Executa comados na fonte de dados, podendo ser utilizado para recuperar um DataSet ou um DataReader.
  • DataReader: Lê as informações (item a item) de uma consulta na fonte de dados.
  • DataAdapter: Utilizado para preencher um DataSet após uma consulta. Como já dito anteriormente, ele também tem a capacidade de replicar a atualização dos dados (após as manipulações necessárias) na fonte de dados.

Observe que o DataSet não está citado acima, pois ele é o mesmo objeto para qualquer provedor. Como? Ele só mantem em memória os dados fornecidos pelo DataAdapter, desconhecendo assim o banco de dados, e deixando essa responsabilidade a cargo do DataAdapter.

Vejamos alguns exemplos de conexão com banco de dados:

image

Importante observar três coisas na figura acima:

A primeira são os namespaces que foram incluídos. Um para cada provedor.

A segunda é a string de conexão. Esta é responsável por localizar o banco de dados e passar as credenciais de conexão. Para compreendermos melhor o conceito de string de conexão, imagine o seguinte: para você se conectar a um site, você tem de informar a URL do mesmo, talvez passar alguns parâmetros pela URL, e em algum momento se autenticar no site. A string de conexão faz isso! para saber mais sobre strings de conexão do ADO.NET acesse: http://www.connectionstrings.com/

A terceira é o fato do padrão de nomenclatura.

Vamos trabalhar com um exemplo simples, porém mais concreto. Após instalar o Visual Studio e SQL server em uma máquina, abra o Visual Studio e localize a window “Server Explorer”:

image

Esta window do Visual Studio gerencia as conexões as bases de dados que você informar. Vamos criar uma nova base de dados no SQL Server, clicando com o Botão direito em “Data Connections” > “Create new SQL Server Database”:

image

image

Configure a janela que aparecer com o nome da sua instância de SQL Server, e o nome da base que será criada.

Após criar a base vamos adicionar uma Tabela “Pessoa”. Clique com o botão direito na pasta “Table” > “Add New Table”.

image

Crie as colunas com os tipos da imagem abaixo. Na coluna “Codigo”, clique com o botão direito em Set Primary Key, para definir esta coluna como chave primária.

image

Para tornar a coluna código Auto-Incremento, selecione-a e na window de “Column Properties”, defina “Is Identity = Yes”, como na imagem abaixo:

image

Quando for salvar defina o nome “Pessoa”:

image

Vamos inserir algumas linhas nessa tabela, para isso, clique com o botão direito sobre a tabela e selecione “New Query”:

image

Em seguida digite alguns comandos de “insert” e clique no ponto de exclamação em vermelho, como indicado na figura:

image

Após inserir alguns dados, vamos criar uma aplicação de console que irá se conectar na base de dados. Neste primeiro exemplo, iremos listar os dados usando o objeto DataReader, observe:

image

Explicando:

Na linha 16 é definida a variável que representa a string de conexão com o SQL Server, ou seja, qual o caminho para eu conectar na base de dados, com suas devidas configurações e parâmetros de autenticação. Basicamente ela é composta de servidor (podendo ser um IP, assim, poderíamos ter o SQL server em outra máquina que não a de desenvolvimento) com instância do SQL Server, a base de dados inicial e o tipo de autenticação.

Na linha 14 é criado o objeto de conexão com SQL Server passando a string de conexão no construtor. Perceba o uso da palavra reservada “using”. Ela faz o descarte automático do objeto de conexão quando o bloco que ela define for totalmente executado. Isso significa que ela desconecta da base de dados e retira todas as informações desnecessárias da memória, pois abrir e manter uma conexão com uma base de dados é muito custoso. E fica a dica: É possível utilizar o “using” em qualquer objeto que implementa a interface IDisposable, ou seja, todo objeto que pode ser descartado da memória.

Chegou a hora de eu passar o comando SQL criado, para isso utilizamos a classe SqlCommand, passando a string com a instrução SQL, e o objeto de conexão, na linha 18.

Agora que esta tudo pronto para executar o comando, vamos abrir a conexão com o método “Open()”, na linha 20, e resgatar o DataReader (linha 22).

E, enquanto for possível ler linhas que a instrução retornou (linha 24), imprimimos isto na tela, obtendo os dados pelo tipo e índice da coluna (linha 26). Fica registrado que há várias formas de obter a coluna de um DataReader, fique à vontade para explorá-los.

Sendo assim, eis nosso resultado:

image

Agora um exemplo utilizando DataSet:

image

Antes de mais nada a classe DataSet fica no namespace “System.Data”, como na linha 3 indicada na figura acima.

Agora, na linha 19, aparece o DataAdapter recebendo a instrução SQL e a conexão. Ele é que será responsável em recuperar as informações e preencher o DataSet.

Na linha 21 é criado o DataSet, abaixo é aberta a conexão e o DataAdapter preenche o DataSet (linha 24).

Para listar os dados no console, foi feito um “foreach”, obtendo um DataRow (objeto do DataSet que representa uma linha). Percebam que o DataSet tem a capacidade de gerenciar várias tabelas em memória, então, como só temos uma tabela mapeada, pegamos as linhas (Rows) da tabela de índice “0”.

Recuperada a linha, para selecionar a coluna, da linha, é só passar o nome, por string entre colchetes, para o objeto que representa a linha (“item”).

O resultado é o mesmo.

Podemos disponibilizar estes dados facilmente na web. Para tal vamos adicionar uma ASP.NET Web Application na solução:

image

image

Agora, na pagina Default.aspx vamos adicionar um controle que esta apto para receber uma coleção de registros, proveniente da base de dados. Para tal, escolhi a GridView.

image

Basicamente, uma vez que fornecermos uma fonte de dados (coleção de registros) para a GridView, ela irá renderizar uma tabela HTML com as informações. Para forneceremos as informações, vamos para o code behind da página Default.aspx, e no evento Page_Load podemos conectar no banco de dados, resgatar os registros com um select, e de alguma forma fornecer esta coleção para a GridView.

Com o DataSet é mais fácil, apenas atribuímos ele na propriedade DataSouce da GridView, e realizar um DataBind(), método responsável por renderizar a informação vinculada.

Alguns detalhes importantes, para acessar o code behind, abra o arquivo Default.aspx.cs:

image

Temos de tornar esta aplicação a principal, pois o console não nos interessa mais, para isso clique com o botão direito no projeto web > “Set as StartUp Project”:

image

Após estes detalhes, resta a codificação. Vejamos o exemplo da codificação e seu resultado:

image

image

Podemos trabalhar também com o DataReader, porém temos de criar uma lista de objetos. O papel da lista será armazenar os registros em memória e atribuí-la no DataSource da GridView.

Vamos adicionar uma classe “Pessoa” que modelará os objetos da lista,

image

image

image

Agora entramos em um conceito interessante: A interação de um sistema, baseado em linguagem Orientada a Objetos, com uma base de dados relacional.

Já temos nossa classe, resta criar uma lista e os objetos para cada linha recuperada do banco de dados, e adicioná-los em uma Lista tipada (List<>). Vejamos:

image

Visualmente, o resultado é o mesmo, mas lembrando que performaticamente o DataReader é mais ágil.

Não foi muito diferente do exemplo no console, a diferença é que não utilizamos o Console.WriteLine().

Para concluir, vamos voltar na aplicação console, pois agora é hora de explicar como efetuar um comando na base de dados, ganhando assim o poder de inserir, atualizar e apagar registros da base.

image

Para qualquer que seja o comando que deverá ser executado na base de dados (insert, delete, update), devemos utilizar um objeto SqlCommand e o seu método “ExecuteNonQuery()”, que retornará o número de linhas afetadas pelo comando.

Acompanhe o exemplo abaixo:

image

image

Uma instrução de insert esta definida na linha 17. Perceba que ela não recebe diretamente os valores que serão inseridos no banco de dados, pois ela esta recebendo parâmetros. Estes são importantes para evitarmos problema de SQL Injection (injeção de comandos indesejados na string do comando). Então nas linhas 21 e 22 são adicionados parâmetros ao comando, provenientes da classe SqlParameter, passando, no construtor, o nome do parâmetro (com “@”, uma característica do SQL Server, podendo variar a maneira de passar parâmetro, dependendo do Provedor de Dados), e seu valor. O valor é tratado pelo parâmetro, assim evitando dados truncados (no caso de uma Data e Hora) e injeção indevida de SQL (SQL Injection).

Finalmente, na linha 25 executamos o “ExecuteNonQuery()”, inserindo assim mais um registro no SQL Server.

Lembrando que o “ExecuteNonQuery()” da suporte para os comandos de delete e update também.

Para comprovar, podemos voltar no exemplo web, que está com o select, e executar a aplicação.

image

Percebam que o registro inserido, no exemplo acima, esta na linha de Codigo 4.

Espero que tenham gostado, pois o objetivo aqui, foi dar um overview do ADO.NET, focando na conectividade nativa e primitiva do mesmo.

Para quem quiser se aprofundar no assunto, o ADO.NET suporta uma série de frameworks que ajudam a abstrair a camada de dados, e facilitar a interação com o mundo das linguagens Orientadas a Objeto, como o Entity Framework, LINQ to SQL, NHibernate etc… Inclusive DataSets Tipados.

E fica a dica: pense como disponibilizar registros e conectividade com o banco de dados após explorar este artigo de arquitetura, seja de forma primitiva, ou explorando uma solução/framework mais robusto. Se achar que de primeira está difícil, tente criar funções ou classes com métodos que generalizam e o ajudariam com a base de dado.

E este foi um grande overview sobre a base do ADO.NET… Abraços, até mais Smiley mostrando a língua

4 comentários:

Tester disse...

Parabéns pelo ótimo artigo, Spoky.

Muito bom!!

Abraços,
Rodolfo

Eduardo Spaki disse...

Que bom que gostou, espero que ajude! :)

1000ton disse...

Ótimo artigo man, parabens.

Realmente sem ADO aplicativo nenhum funcionaria

Anônimo disse...

Pena que não funciona da erro no using.