Ir para conteúdo

Arquivado

Este tópico foi arquivado e está fechado para novas respostas.

alexandremanowar

[Resolvido] Erro na consulta

Recommended Posts

E ae pessoal beleza

 

Eu estou fazendo uns testes com conexão com banco de dados e datareader, fiz uma class para conectar banco e executar query, a conexão esta funcionanado a consulta também, só não consigo exibir os dados, veja meu código:

 

Conexão e execução de query

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Configuration;
using System.Text;
using System.Data;
using System.Data.SqlClient;


/// <summary>
/// Summary description for Class1
/// </summary>
public class Class1
{
	public Class1()
	{
		
	}
	//Método para Abrid Conexão o Banco de Dados
	private static SqlConnection AbrirBanco()
	{

		string conn = ConfigurationManager.ConnectionStrings["SQLExpress2008"].ConnectionString; //"conn" é o nome do connection string no node do seu arquivo XML
		SqlConnection cn = new SqlConnection(conn);
		cn.Open();

		return cn;
	  

	}

	//Método para Fechar a Conexão com o  Banco de Dados
	public static void FecharBanco(SqlConnection cn)
	{

		if (cn != null && cn.State == ConnectionState.Open)
		{
			cn.Close();
		}

	}

	//Método para Executa um comando SQL, recebe uma string.

	public static void ExecutarComando(string strQuery)
	{

		SqlConnection cn = null;

		try
		{

			cn = AbrirBanco();

			SqlCommand cmd = new SqlCommand(strQuery, cn);

			cmd.ExecuteNonQuery();

		}

		finally
		{

			FecharBanco(cn);

		}

	}

	
	public SqlDataReader RetornarDataReader(string strQuery)
	{

		SqlConnection cn = null;

		try
		{

			cn = AbrirBanco();

			SqlCommand cmd = new SqlCommand(strQuery, cn);

			return cmd.ExecuteReader();

		}

		finally
		{

			FecharBanco(cn);

		}

	}


}

Chama a class e exibe dados:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Data;
using System.Data.SqlClient;

public partial class Default2 : System.Web.UI.Page
{
	protected void Page_Load(object sender, EventArgs e)
	{

	}
	protected void Button1_Click(object sender, EventArgs e)
	{
		
		Class1 ExecutaStringSQL = new Class1();

		SqlDataReader BuscaRG = null;


		BuscaRG = ExecutaStringSQL.RetornarDataReader("select top 1 * from senha ");



		Label1.Text = BuscaRG.GetString(0); 

	}

  
}

Esta dando erro nessa linha:

Label1.Text = BuscaRG.GetString(0);

Esta dando a seguinte mensagem:

System.InvalidOperationException estava sem tratamento pelo código do usuário

Message="Invalid attempt to call MetaData when reader is closed."

Source="System.Data"

StackTrace:

at System.Data.SqlClient.SqlDataReader.get_MetaData()

at System.Data.SqlClient.SqlDataReader.ReadColumn(Int32 i, Boolean setTimeout)

at System.Data.SqlClient.SqlDataReader.GetString(Int32 i)

at Default2.Button1_Click(Object sender, EventArgs e) in c:\asp\WebLogin\Default2.aspx.cs:line 43

at System.Web.UI.WebControls.Button.OnClick(EventArgs e)

at System.Web.UI.WebControls.Button.RaisePostBackEvent(String eventArgument)

at System.Web.UI.WebControls.Button.System.Web.UI.IPostBackEventHandler.RaisePostBa

ckEvent(String eventArgument)

at System.Web.UI.Page.RaisePostBackEvent(IPostBackEventHandler sourceControl, String eventArgument)

at System.Web.UI.Page.RaisePostBackEvent(NameValueCollection postData)

at System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)

InnerException:

 

Onde estou errando?

Compartilhar este post


Link para o post
Compartilhar em outros sites

DataReader é forward-only/read-only e trabalha conectado no banco, se você fechar a conexão, o datareader se torna inválido.

quando você fez o finally abaixo, você fechou a conexão.

finally
{
FecharBanco(cn);
}

Eu gosto de trabalhar com DataReader, minha camada de acesso a dados retorna o DataReader como a sua, mas eu não fecho ela na camada de dados.

Faça assim na sua aplicação final:

 

using(SqlDataReader BuscaRG = ExecutaStringSQL.RetornarDataReader("select top 1 * from senha "))
{
if (BuscaRG.Read())
Label1.Text = BuscaRG.GetString(0); 
}

O using vai assegurar que sua conexão feche sozinho e volte ao pool.

 

Att,

Guilherme Oenning

Compartilhar este post


Link para o post
Compartilhar em outros sites

E ae cara beleza valeu pela dica coloquei como você falou mas continuou dando erro:

 

Invalid attempt to call Read when reader is closed

 

Acredito que é o que você falou mesmo ao fechar perdo os dados! Será que tem outra forma de puxar isso? Ele deveria deixar os dados no datareaer porque já busquei no banco e o que eu fecho é o banco e não o datareader. Como posso corrigir isso?

Compartilhar este post


Link para o post
Compartilhar em outros sites

E a e beleza

 

Certo então o problema é que fecho a conexão mas como eu iria alimentando os campos em variáveis se fechei a conexão? Será que se eu criar uma matriz dentro da função que gerar o datareader e passar os dados a partir dela seria uma boa jogada?

Compartilhar este post


Link para o post
Compartilhar em outros sites

Neste exemplo que você passou, como é só um registro é tranquilo.

Eu utilizo classes Models, que são classes muito parecidas com a estrutura banco de dados, quando executo um reader eu crio uma lista desses models e vou adicionando na lista.

Depois de acabar de adicionar, eu fecho a conexão.

 

Se você realmente for usar matriz, seria melhor você usar DataSet Tipado ao invés de DataReader.

 

Att,

Guilherme Oenning.

Compartilhar este post


Link para o post
Compartilhar em outros sites

E ae cara

 

beleza valeu pela atenção

 

Cara agora fiquei meio perdido, será que você não tem um exemplo de como montar ou usando a class que você falou ou usando o dataset pra o array?

Compartilhar este post


Link para o post
Compartilhar em outros sites

Imagina que você tenha uma base de dados com todos os paises.

Você pode fazer uma classe assim:

public class Pais
{
	public int Codigo { get; set; }
	public string Nome { get; set; }
}

Essa classe representa um pais.

 

Ai você pode ter um método assim, em uma outra classe:

 

public List<Pais> GetPaises()
{
	List<Pais> paises = new List<Pais>();
	Pais pais = null;
	using (DbDataReader dr = dataAccess.ExecuteNonQuery("SELECT COD_PAIS,NOM_PAIS FROM PAIS"))
	{ 
		while(dr.Read())
		{
			pais = new Pais();
			pais.Codigo = dr.GetInt32(dr.GetOrdinal("COD_PAIS"));
			pais.Nome = dr.GetString(dr.GetOrdinal("NOM_PAIS"));
			paises.Add(pais);
		}
	}
	return paises;
}

Ai, em qualquer lugar do sistema que você precisa ter uma lista de paises, carregar um combobox por exemplo, você pode usar o método GetPaises();

Assim você terá uma lista de objetos concretos (Pais) ao invés de trabalhar com um array de strings ou dataset, fica mais OO.

 

Att,

Guilherme Oenning.

Compartilhar este post


Link para o post
Compartilhar em outros sites

E ae beleza

 

Certo entendi o que você quiz dizer, porém e quanto a conexão com o banco? No caso você usou direto:

using (DbDataReader dr = dataAccess.ExecuteNonQuery("SELECT COD_PAIS,NOM_PAIS FROM PAIS"))

No meu caso eu usuaria naquele esquema que criei? Ou seja ficaria assim:

public List<Pais> GetPaises()
{
	List<Pais> paises = new List<Pais>();
	Pais pais = null;
	using (DbDataReader dr = ExecutaStringSQL.RetornarDataReader("SELECT COD_PAIS,NOM_PAIS FROM PAIS"))
	{ 
		while(dr.Read())
		{
			pais = new Pais();
			pais.Codigo = dr.GetInt32(dr.GetOrdinal("COD_PAIS"));
			pais.Nome = dr.GetString(dr.GetOrdinal("NOM_PAIS"));
			paises.Add(pais);
		}
	}
	return paises;
}

Acessando minha class de conexão que tem uma função datareader dentro, ou não?

Outra dúvida é a seguinte você encheu uma lista chamada paises e no final deu um return paises, esse tipo de return não é usado apenas em função? Se não como eu capturo os valore desse return para exibir na label?

 

Obrigado mais uma vez pela força

Compartilhar este post


Link para o post
Compartilhar em outros sites

A primeira dúvida está correta, você usa sua classe de conexão.

 

Quanto a segunda, também está correta, GetPaises(); é uma função, eu coloquei só a função, mas ela precisa estar dentro de uma classe.

E você usaria assim na sua aplicação:

PaisService serv = new PaisService();
List<Pais> paises = serv.GetPaises();
foreach(Pais p in paises)
{
	 meuLabel.Text += p.Nome + ",";
}

Nesse exemplo acima, eu coloquei todos os paises do meu banco em um label.

Você poderia, por exemplo, pesquisar dentro dessa lista por um determinado pais, usando as estruturas de comparação, e mostrar na tela.

 

Att,

Guilherme Oenning;

Compartilhar este post


Link para o post
Compartilhar em outros sites

E ae cara beleza, desculpe a demora é que estive com problemas e só agora consegui voltar ao estudos com .net, então estou entendendo o que você montou mas não estou conseguindo fazer funcionar, não consigo acertar as class será que tem como você me ajudar a montar esse esquema usando a minha class? Se puder eu agradeço, valeu pela atenção

Compartilhar este post


Link para o post
Compartilhar em outros sites

Não entendi muito bem, pode postar seu código da maneira que está hoje?

Compartilhar este post


Link para o post
Compartilhar em outros sites

E a e beleza

 

Então cara o meu código esta assim:

 

Class de conexão e executa strings sql

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Configuration;
using System.Text;
using System.Data;
using System.Data.SqlClient;

/// <summary>
/// Summary description for Conexao
/// </summary>
public class Conexao
{
	public Conexao()
	{
		//
		// TODO: Add constructor logic here
		//
	}

	//Método para Abrid Conexão o Banco de Dados
	private static SqlConnection AbrirBanco()
	{

		string conn = ConfigurationManager.ConnectionStrings["SQLExpress2008"].ConnectionString; //"conn" é o nome do connection string no node do seu arquivo XML
		SqlConnection cn = new SqlConnection(conn);
		cn.Open();

		return cn;


	}

	//Método para Fechar a Conexão com o  Banco de Dados
	public static void FecharBanco(SqlConnection cn)
	{

		if (cn != null && cn.State == ConnectionState.Open)
		{
			cn.Close();
		}

	}

	//Método para Executa um comando SQL, recebe uma string.

	public static void ExecutarComando(string strQuery)
	{

		SqlConnection cn = null;

		try
		{

			cn = AbrirBanco();

			SqlCommand cmd = new SqlCommand(strQuery, cn);

			cmd.ExecuteNonQuery();
		  

		}

		finally
		{

			FecharBanco(cn);
			

		}

	}

	
	public SqlDataReader RetornarDataReader(string strQuery)
	{

		SqlConnection cn = null;

		try
		{

			cn = AbrirBanco();

			SqlCommand cmd = new SqlCommand(strQuery, cn);

			return cmd.ExecuteReader();
		 }

	}

}

Evento do botão que teria que exibir o resultado do banco na label, mas esta dando a seguinte mensagem de erro:

Tentativa inválida de leitura quando não existem dados.

nessa linha: Label1.Text = BuscaRG.GetString(0);

 

código:

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Data;
using System.Data.SqlClient;

public partial class Home : System.Web.UI.Page
{
	protected void Page_Load(object sender, EventArgs e)
	{

	}
	protected void Button1_Click(object sender, EventArgs e)
	{
	 
		Conexao ExecutaStringSQL = new Conexao();

		SqlDataReader BuscaRG = null;


		BuscaRG = ExecutaStringSQL.RetornarDataReader("select * from senha ");

		  Label1.Text = BuscaRG.GetString(0); 
	}
}

Essa é a linha de conexão no web.config

<connectionStrings>
	<add name="SQLExpress2008" connectionString="Data Source=NOTEBOOK\SQLEXPRESS2008;Initial Catalog=AccessPortaria;Integrated Security=True" providerName="System.Data.SqlClient"/>
	</connectionStrings>

Você sabe onde estou errando?

Compartilhar este post


Link para o post
Compartilhar em outros sites

A entendi, faz assim:

BuscaRG = ExecutaStringSQL.RetornarDataReader("select * from senha ");
if (BuscaRG.HasRows)
{
BuscaRG.Read();
Label1.Text = BuscaRG.GetString(0); 
}

O método Read vai avançar para o primeiro registro.

Tome cuidade pois se o comando sql não retornar nada, o método Read vai lançar o erro.

Compartilhar este post


Link para o post
Compartilhar em outros sites

E a e beleza

 

Certo cara entendi, valeu pela força porém é o seguinte se você reperar na minha função RetornarDataReader ela não esta fechando a conexão, o certo não seria eu conectar o banco encher o datareader exibir os dados na tela e depois fechar o banco? Então para isso adicionei para testar um finally na função mas da erro, é a seguinte mensagem:

 

Tentativa inválida de chamar HasRows quando o leitor estava fechado.

 

A função ficou assim:

 

public SqlDataReader RetornarDataReader(string strQuery)
	{

		SqlConnection cn = null;

		try
		{

			cn = AbrirBanco();

			SqlCommand cmd = new SqlCommand(strQuery, cn);

			return cmd.ExecuteReader();
		   
		}

		finally
		{

			FecharBanco(cn);

		}

	}

Dúvidas:

 

1)Precio de opinião é aconselhável fechar o banco após a operação certo? Se for como eu corrijo esse problema?

2)Será que a forma que estou trabalhando é boa? A idéia é ter uma class que eu possa usar em todo o meu projeto para ler as string sql, você pode observar na class completa (postei acima) que ela faz a conexão com banco tenho uma funcão que vou usar para armazenar,alterar e exluir e tenha a função usando datareader que vou usar para consultas como estamos fazendo e você me ajudou. Qual sua opinião existe um modo melhor de usar essas tarefas ou essa que estou fazendo atende bem? Sei que ainda falta melhorar a parte de SQL usando variáveis tipadas,stored procedures e outros mas no momento estou me concentrando apenas em criar a class que vai realizar o trabalho depois melhoro o sql.

 

Mais uma vez agradeço ajuda

Compartilhar este post


Link para o post
Compartilhar em outros sites

1) Ai que está o erro, o DataReader é considerado forward-only/read-only. Isso significa que você só consegue ler os dados para frente (método Read do datareader), você não consegue voltar uma linha do resultado. E outra característica é que o datareader trabalha de forma conectada, você precisa ter uma conexão ativa quando usar o método Read para ir de linha em linha. No seu caso, antes de retornar o DataReader você está fechando a conexão, assim o datareader não irá funcionar. Você deve fechar a conexão, mas esse não é o lugar. Eu tenho uma classe assim também, veja meu método:

public SqlDataReader ExecuteReader(string sql)
{
	this.OpenConnection();
	this.Command.CommandText = sql;
	return this.Command.ExecuteReader(CommandBehavior.CloseConnection);
}

public int ExecuteNonQuery(string sql)
{
	this.OpenConnection();
	this.Command.CommandText = sql;
	int rows = this.Command.ExecuteNonQuery();
	this.CloseConnection();
	return rows;
}
Perceba que eu não fechei a conexão no ExecuteReader, mas no ExecuteNonQuery eu fechei. Quando eu chamo ele, eu uso assim:

DatabaseHelper db = new DatabaseHelper();
using(SqlDataReader dr = db.ExecuteReader("SELECT id, nome FROM aluno"))
{
		if (dr.Read())
		...
}
O using vai se encarregar de chamar o método Dispose do meu datareader dr. O método dispose do dr vai fechar a conexão :)

 

 

2) Sim, está correto. Apenas um comentário, fuja de métodos estáticos! Isso pode causar problemas com concorrência. No seu caso todos os métodos podem ser não-estáticos, não haverá problemas.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Cara essa explicação foi ótima entendi como funciona, obrigado mesmo pela força funcionou e entendi o funcionamento, obrigao por tudo.

Compartilhar este post


Link para o post
Compartilhar em outros sites

×

Informação importante

Ao usar o fórum, você concorda com nossos Termos e condições.