Você está na página 1de 8

10/3/2014

C# - Convertendo um DataReader para uma lista genrica

C# - Convertendo um DataReader para uma lista genrica e exibindo em um TreeView


O objetivo principal deste artigo mostrar como podemos converter um DataReader para uma lista genrica de objetos. Eu vou usar um exemplo onde irei acessar uma tabela Alunos de um banco de dados SQL Server chamado Escola.mdf e exibir os dados em um controle TreeView . Vou aproveitar e mostrar como preencher o TreeView usando o modo tradicional com ADO .NET e o cdigo embutido no formulrio e o modo mais indicado onde irei criar uma classe que atuar como uma camada de acesso a dados e onde eu irei retornar uma coleo de objetos de forma que na camada de apresentao, o formulrio Windows Forms, eu no tenha que referenciar nada relacionado a acesso aos dados como comandos SQL ou objetos ADO .NET para acesso a dados. Para tornar o exemplo bem simples eu no vou criar a camada de negcios BLL e dessa forma algumas definies eu vou ter que fazer na camada de acesso a dados como a definio da string de consulta. Para um soluo mais robusta recomenda-se a complementao do exemplo criando pelo menos a camada de negcios. Eu vou usar o Visual Studio 2012 Express for Desktop e a linguagem C# e criar uma soluo (File->New Project) Windows Forms Application chamada PreenchendoTreeView . No formulrio form1.cs vamos os seguintes controles: GroupBox TreeView - treeView1 Button - btnCarregar - Carregar Dados (SQL) Button - btnCarga - Carregar Dados (Objetos) LinkLabel - lnkRecolherItens - Recolher Itens Label - lblItem ; Autosize = false O leiaute do formulrio form1.cs dever ter a seguinte aparncia:

Abaixo vemos a estutura da tabela Alunos do banco de dados Escola.mdf e alguns dados includos para o teste:

http://www.macoratti.net/13/09/c_tv1.htm

1/8

10/3/2014

C# - Convertendo um DataReader para uma lista genrica

Acessando dados e preenchendo um TreeView - Modo Tradicional


Vamos comear usando o mtodo tradicional, digo tradicional , porque o modo mais comumente usado onde todo o cdigo colocado no prprio formulrio. No formulrio form1.cs vamos colocar todo o cdigo necessrio para acessar os dados e exibir as informaes no TreeView . Para isso teremos que declarar os seguintes namespaces no incio do formulrio form1.cs: using using using using using System; System.Drawing; System.Windows.Forms; System.Data.SqlClient; System.Collections.Generic;

A seguir, logo aps a declarao da classe form1 devemos definir as variveis usadas para acessar os dados e a string de conexo SqlConnection Conn = new SqlConnection("Data Source=.\\SQLExpress; Initial Catalog=Escola; Integrated Security=True"); SqlDataReader rdr; SqlCommand cmd; No evento Click do boto Carregar Dados vamos incluir o cdigo que chama a rotina que acessa os dados e preenche o TreeView: private void btnCarregar_Click(object sender, EventArgs e) { treeView1.Nodes.Clear(); Carregar(); } A rotina Carregar() faz todo o trabalho : acessa o banco de dados executa a consulta obtendo um datareader e extrai os dados preenchendo o TreeView: private void Carregar() { lblItem.Text = ""; cmd = new SqlCommand("Select id,nome,email,curso From Alunos Order By id", Conn); Conn.Open(); rdr = cmd.ExecuteReader(); TreeNode parent = treeView1.Nodes.Add("Alunos"); TreeNode child; parent.ForeColor = Color.Red; while (rdr.Read()) { child = parent.Nodes.Add("Aluno ID: " + rdr.GetValue(0).ToString()); child.ForeColor = Color.Blue; child.Nodes.Add("Nome: " + rdr.GetValue(1).ToString()); child.Nodes.Add("Email: " + rdr.GetValue(2).ToString()); child.Nodes.Add("Curso: " + rdr.GetValue(3).ToString()); } parent.ExpandAll(); rdr.Close(); Conn.Close(); }
http://www.macoratti.net/13/09/c_tv1.htm 2/8

10/3/2014

C# - Convertendo um DataReader para uma lista genrica

A execuo do cdigo gera o seguinte resultado:

Apenas para conhecimento a seguir temos o cdigo que obtm um item selecionado do TreeView e o exibe em um controle Label no formulrio: private void treeView1_AfterSelect(object sender, TreeViewEventArgs e) { int IDSelecionado = treeView1.SelectedNode.Index; String NomeSelecinado = treeView1.SelectedNode.Text; lblItem.Text = NomeSelecinado.ToString(); } Obs: Estamos obtendo o ndice do item selecionado apenas para ilustrar como fazer isso. O cdigo para recolher os itens do TreeView que colocamos no evento Click do controle LinkLabel esta abaixo: private void lnkRecolherItens_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) { lblItem.Text = ""; treeView1.CollapseAll(); } Para limpar os itens do TreeView usamos: treeView1.Nodes.Clear(); Para expandir os itens do TreeView o cdigo : treeView1.ExpandAll(); Tudo muito simples, no mesmo ?

Acessando dados e preenchendo um TreeView - Usando as boas prticas


Voc no precisa ser um Phd em padres de projetos para usar as boas prticas no desenvolvimento de software. Existem aes que podem ser tomadas desde o incio do desenvolvimento at mesmo para aplicaes muito simples que garantem o mnimo de robustez e tornam o cdigo muito mais legvel e fcil de manter. O modo tradicional usado muito simples e funciona, mas ele nunca deve ser usado em aplicaes de produo pois ele oculta em sua simplicidade grandes problemas que iro aparecer quando voc precisar dar manuteno ou estender a aplicao com novas funcionalidades. A primeira coisa que chama a ateno que a camada de apresentao tem que conhecer como acessar os dados, e isso no a sua funo. A camada de apresentao deve apenas saber apresentar os dados ao usurio deixando a responsabilidade de saber como acessar e obter os dados para outra camada. Chamamos isso de separao das responsabilidades em uma aplicao e aplicamos essa separao trabalhando em camadas. Isso no um capricho ou uma moda, existe uma razo muito forte para se recomendar essa metodologia de trabalho pois ela facilita a manuteno e permite a reutilizao de cdigo. Vamos ento criar uma camada de acesso a dados que no nosso exemplo ser representada por uma classe chamada ConexaoDB. O mais correto seria criar um novo projeto do tipo Class Library e neste projeto definir as classes mas devido a simplicidade do nosso projeto irei criar apenas a classe que atuar como a camada de acesso a dados.
http://www.macoratti.net/13/09/c_tv1.htm 3/8

10/3/2014

C# - Convertendo um DataReader para uma lista genrica

No menu PROJECT clique em Add Class e informe o nome ConexaoDB.cs e a seguir defina o cdigo abaixo nesta classe: using using using using using System; System.Data; System.Data.SqlClient; System.Configuration; System.Collections.Generic;

namespace PreenchendoTreeView { public class ConexaoDB { private SqlConnection _connection; public ConexaoDB() { try { string strConnectionString = ConfigurationManager.ConnectionStrings["ConexaoSQL"].ToString(); _connection = new SqlConnection(strConnectionString); } catch (Exception ex) { throw ex; } } public List<Aluno> GetAlunosOB() { string consulta = "Select * from Alunos"; List<Aluno> _alunos = new List<Aluno>(); try { _connection.Open(); SqlCommand cmd = new SqlCommand(consulta, _connection); cmd.CommandType = CommandType.Text; SqlDataReader dr = cmd.ExecuteReader(); while (dr.Read()) { _alunos.Add(new Aluno() { Id = Convert.ToInt32(dr["Id"]), Nome = dr["Nome"].ToString(), Email = dr["Email"].ToString(), Curso = dr["Curso"].ToString() }); } dr.Close(); return _alunos; } catch (Exception ex) { throw ex; } finally { _connection.Close(); } } } }

http://www.macoratti.net/13/09/c_tv1.htm

4/8

10/3/2014

C# - Convertendo um DataReader para uma lista genrica

Esta classe ser a responsvel pelo acesso ao banco de dados e pela extrao das informaes da tabela. Todo o conhecimento de como acessar dados com ela mesmo e ela deve ser uma especialista nisto e deve saber somente isso. Um dos princpios bsicos para desenho de software com o objetivo evitar as ms prticas o principio da responsabilidade nica ou SRP - Single Responsability Principle. Este princpio foi introduzido por Tom DeMarco em 1979 no seu livro Structured Analysis and Systems Specification, Yourdon Press Computing Series. O princpio da responsabilidade nica um princpio fundamental no projeto de software que reza o seguinte :

"Deve existir um e somente UM MOTIVO para que uma classe mude"


Portanto uma classe deve ser implementada tendo apenas um nico objetivo. Quando uma classe possui mais que um motivo para ser alterada por que provavelmente ela esta fazendo mais coisas do que devia, ou seja, ela esta tendo mais de um objetivo. Podemos ento inferir as seguintes premissas a partir da definio da responsabilidade nica: Baseado no princpio da coeso funcional, uma classe deve ter uma nica responsabilidade; Se uma classe possuir mais de uma responsabilidade, deve-se considerar sua decomposio em duas ou mais classes; Cada responsabilidade um eixo de mudana e as fontes de mudana devem ser isoladas; Este conceito fcil de entender mas difcil de ser posto em prtica. A nossa classe ConexaoDB deve portanto ter apenas um objetivo: acessar dados. Vamos ento cria a classe removendo o cdigo responsvel pelo acesso aos dados do formulrio. Nesta nossa classe temos o construtor iniciando a string de conexo que esta sendo obtida do arquivo de configurao, App.Config , usando a classe ConfigurationManager: public ConexaoDB() { try { string strConnectionString = ConfigurationManager.ConnectionStrings["ConexaoSQL"].ToString(); _connection = new SqlConnection(strConnectionString); } catch (Exception ex) { throw ex; } } Assim temos que declarar no arquivo App.Config a string de conexo da seguinte forma: <?xml version="1.0" encoding="utf-8" ?> <configuration> <startup> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" /> </startup> <connectionStrings> <add name="ConexaoSQL" connectionString="Data Source=.\SQLExpress; Initial Catalog=Escola; Integrated Security=True;" /> </connectionStrings> </configuration> O mtodo GetAlunos() da classe ConexaoDB esta definindo uma string de consulta usando o comando SQL - Select * from Alunos -, e, na verdade isso no deveria estar definido nesse mtodo mas deveria estar definido na camada de negcios. Como eu no criei esta camada, para ficar mais simples, eu embuti a definio da consulta do mtodo, mas NUNCA faa isso em uma aplicao de produo, primeiro por que nunca devemos usar Select * visto que isso retorna TODOS os registros da tabela. Imagine se a tabela possuir 1.000.000.00 de registros. Por isso sempre restringimos a quantidade de dados retornados de uma tabela usando a classe Where com um critrio de filtro: Ex: Select id, nome, email from Alunos Where id < 10. public List<Aluno> GetAlunosOB() { string consulta = "Select * from Alunos";
http://www.macoratti.net/13/09/c_tv1.htm 5/8

10/3/2014

C# - Convertendo um DataReader para uma lista genrica

List<Aluno> _alunos = new List<Aluno>(); try { _connection.Open(); SqlCommand cmd = new SqlCommand(consulta, _connection); cmd.CommandType = CommandType.Text; SqlDataReader dr = cmd.ExecuteReader(); while (dr.Read()) { _alunos.Add(new Aluno() { Id = Convert.ToInt32(dr["Id"]), Nome = dr["Nome"].ToString(), Email = dr["Email"].ToString(), Curso = dr["Curso"].ToString() }); } dr.Close(); return _alunos; } catch (Exception ex) { throw ex; } finally { _connection.Close(); } } Outro detalhe importante neste mtodo que estamos retornando uma coleo de objetos Aluno e no um datareader. Fiz isso nesta classe por que no criei a bendita camada de negcios que seria a responsvel por este tratamento. Dessa forma a nossa camada de apresentao vai receber um coleo de objetos e no um datareader o que no seria muito indicado pois ela teria que saber como tratar um datareader e isso S camada de acesso a dados deve saber. Precisamos ento definir uma classe que ir representar o nosso modelo de dados e faremos isso criando a classe Aluno (PROJECT>Add Class) e definindo o cdigo desta classe conforme abaixo: namespace PreenchendoTreeView { public class Aluno { public int Id { get; set; } public string Nome { get; set; } public string Email { get; set; } public string Curso { get; set; } } } Esta uma classe POCO - Plain Old CLR Object - e ela mapeia os campos definidos na tabela Alunos, onde para cada campo definimos uma propriedade com o mesmo nome. Essa classe vai agir como um DTO - Data Transfer Object - servindo apenas para que possamos passar os dados entre as camadas. No nosso exemplo entre a classe ConexaoBD e a camada de apresentao. Finalmente na camada de apresentao no evento Click do boto Carregar Dados temos o cdigo que vai receber os objetos e usando um lao foreach vai percorrer e exibir as informaes no TreeView : private void btnCarga_Click(object sender, EventArgs e) { lblItem.Text = ""; treeView1.Nodes.Clear(); try { ConexaoDB cdb = new ConexaoDB();
http://www.macoratti.net/13/09/c_tv1.htm 6/8

10/3/2014

C# - Convertendo um DataReader para uma lista genrica

TreeNode parent = treeView1.Nodes.Add("Alunos"); TreeNode child; parent.ForeColor = Color.Red; List<Aluno> _alunos = new List<Aluno>(); _alunos = cdb.GetAlunosOB(); foreach (Aluno _aluno in _alunos) { child = parent.Nodes.Add("Aluno ID: " + _aluno.Id.ToString()); child.ForeColor = Color.Blue; child.Nodes.Add("Nome: " + _aluno.Nome.ToString()); child.Nodes.Add("Email: " + _aluno.Email.ToString()); child.Nodes.Add("Curso: " + _aluno.Curso.ToString()); } treeView1.ExpandAll(); } catch (Exception ex) { MessageBox.Show("Erro : " + ex.Message); } } Note que tivemos que criar uma instncia da classe ConexaoDB para poder usar o mtodo GetAlunosOB() pois o mtodo um mtodo de instncia e no um mtodo esttico. Poderamos ter criado um mtodo esttico na classe ConexaoDB de forma a no precisar criar uma instncia da classe para acess-lo. Ao criar uma instncia da classe no formulrio acabamos criando uma dependncia , ou seja um acoplamento entre ela e o formulrio, e isso no bom mas pode se evitado usando a injeo de dependncia. Assunto que eu no vou tratar neste artigo mas vou citar nas referncias para voc saber como se faz. A execuo vai produzir o mesmo resultado :

Vimos neste artigos coisas importantes, partindo de uma abordagem tradicional e avanando na direo das boas prticas apenas criando uma classe e separando o cdigo da camada de apresentao; j um grande avano mas o caminho longo e para percorr-lo precisamos de mais artigos. Pegue o projeto completo aqui: PreenchendoTreeView.zip (sem o banco de dados)

Joo 12:26 Se algum me quiser servir, siga-me; e onde eu estiver, ali estar tambm o meu servo; se algum me servir, o Pai o honrar.
Referncias: Seo VB .NET do Site Macoratti.net
http://www.macoratti.net/13/09/c_tv1.htm 7/8

10/3/2014

C# - Convertendo um DataReader para uma lista genrica

Super DVD .NET - A sua porta de entrada na plataforma .NET Super DVD Vdeo Aulas - Vdeo Aula sobre VB .NET, ASP .NET e C# Seo C# do site Macoratti.net Usando o Controle TreeView - Macoratti.net C # - Usando o controle TreeView - Macoratti.net Exibindo tabelas e registros em um controle TreeView - Macoratti.net Populando o controle TreeView com tabelas e ... - Macoratti.net VB .NET - Vinculando um DataTable ao um TreeView - Macoratti.net
Jos Carlos Macoratti

http://www.macoratti.net/13/09/c_tv1.htm

8/8

Você também pode gostar