I am trying to read from datareader but i am gettin the error "Invalid attempt to call Read when reader is closed." The stored procedure is working fine but when i try to read fom datareader it throws error.Plz help me
protected void CheckDatabase()
{
SqlConnection conn = new SqlConnection(GetConnectionString());
conn.Open();
SqlParameter[] param = new SqlParameter[2];
param[0]= new SqlParameter("#EmpID", SqlDbType.Int);
param[0].Value = txtEmpId.Text;
param[1]= new SqlParameter("#Date", SqlDbType.VarChar,50);
param[1].Value = txtDate.Text;
SqlDataReader reader = DNDatabase.ExecuteStoredProcedureReader("RetrieveDeatails", param);
while (reader.Read())
{
gridConfirm.DataSource = reader;
gridConfirm.Columns[0].Visible = false;
gridConfirm.DataBind();
Session["Task_List"] = reader;
}}
here is stored Procedure code
public static SqlDataReader ExecuteStoredProcedureReader(string procedureName, SqlParameter[] parameters)
{
SqlConnection _conn = new SqlConnection(DNDatabase.SQLConnectionString);
SqlCommand cmd = new SqlCommand(procedureName, _conn);
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = procedureName;
cmd.CommandTimeout = 300;
try
{
foreach (SqlParameter param in parameters)
{
cmd.Parameters.Add(param);
}
_conn.Open();
return cmd.ExecuteReader(CommandBehavior.CloseConnection);
}
catch (Exception sqlExc)
{
throw new Exception("An error occured", sqlExc);
}
finally
{
if (_conn != null)
_conn.Close();
}
}
This looks like a bad idea to me:
Session["Task_List"] = reader;
You execute that and then call reader.Read() again, continuing until there's nothing left to read. Presumably you also close the reader at some point (I'd hope - ideally with a using statement). Fundamentally, a SqlDataReader is a connected resource - it's like a stream to the database. It's not something you should be holding onto for longer than you need to, and it's certainly not something you should be putting in a session, even if you didn't effectively invalidate it with the subsequent Read() call.
I assume that data binding works by fetching the data when you call DataBind(), but there's no indication of what you're trying to achieve by putting a reference to the reader itself into the session.
I suggest that:
You isolate whether this is actually to do with the session or the databinding
If it's the session, you pull all of the data you need out (e.g. into a DataTable or some other "disconnected" form)
If it's the databinding, you'll need to do something similar - but as I say, you can hope that that's not the problem, to start with - I assume you saw this sort of databinding elsewhere?
Additionally, you should consider why you want to loop - currently you're basically going to loop over all the results and only the last one is going to be displayed (because you're overwriting the databinding each time). Is that really what you want?
check DNDatabase.ExecuteStoredProcedureReader code may it close the reader after fetchdata
and another thing i should say is:
you can'nt use Session["Task_List"] = reader; because reader is forwardonly
and if you want to keep current value of reader in a seesion load in data table and keep data row of table
DataTable dt = new DataTanle();
dt.Load(dr);
and loop over data table rows
Your method ExecuteStoredProcedureReader has a finally statement which closes the connection. Hence you cannot read anything from the reader. Either close the reader in your method where you call ExecuteStoredProcedureReader or merge both the methods to have them in a single method.
Related
I want to search data from another SQL Server table (Stocks_Item) to this form (IC) dataGridView1, I can view that data (from Stocks_Item) in this dataGridView1 but I couldn't search. Please help me.
Here is my code:
private void button5_Click(object sender, EventArgs e)
{
conn.Close();
try
{
conn.Open();
SqlCommand selectCommand = new SqlCommand("Select * from Stocks_Item where Stock_No = #Stocks_no", conn);
selectCommand.Parameters.Add(new SqlParameter("Stocks_no", txtsearch_stock_no.Text.ToString()));
SqlDataReader reader = selectCommand.ExecuteReader();
bool rowFound = reader.HasRows;
SqlDataAdapter data = new SqlDataAdapter("Select * from Stocks_Item", conn);
DataTable dt = new DataTable();
data.Fill(dt);
dataGridView1.DataSource = dt;
/* SqlDataAdapter sda;
DataTable dt1;
sda = new SqlDataAdapter("select * FROM colombo_branch ",conn);
dt1 = new DataTable();
sda.Fill(dt1);
dataGridView.DataSource = dt1;*/
MessageBox.Show("Search Found", "Form", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
conn.Close();
}
There are many flaws in your code, but the one that is specifically causing your error is because you are not properly closing the SqlDataReader here:
SqlDataReader reader = selectCommand.ExecuteReader();
bool rowFound = reader.HasRows;
As a result, the following line, which also attempts to open a data reader internally throws the exception:
data.Fill(dt);
Normally, you want to use a SqlDataReader inside a using block, something like:
bool rowFound;
using(SqlDataReader reader = selectCommand.ExecuteReader())
{
rowFound = reader.HasRows;
}
... to ensure proper and timely disposal. But in this case, I fail to see the point of that block of code anyways. So why not just remove it altogether?
Additional comments:
Avoid global connections. The fact that the method begins by closing the connection is not a good sign.
Always use using blocks around your SqlConnection, SqlCommand, SqlDataReader, SqlDataAdapter, etc... instances to make sure you don't run into these types of errors or leak resources.
I believe that SqlDataAdapter opens/closes a connection for you. Since you're doing that earlier in your code and using a reader against it, that would be the cause. You could use either the reader or the adapter, but typically wouldn't want to mix them in one connection without properly closing/disposing.
FYI, the MSDN SqlDataAdapter code example utilizes this auto open/close behavior of SqlDataAdapter
I have a trouble with my coding. This is a source code what I coding for this program.
OracleConnection kon;
public Form2()
{
InitializeComponent();
FillCombo();
}
void FillCombo()
{
OracleConnection kon = Koneksi.getKoneksi();
OracleCommand cmd;
try
{
kon.Open();
cmd = new OracleCommand();
cmd.Connection = kon;
cmd.CommandText = "SELECT * FROM JOBS";
OracleDataReader reader = cmd.ExecuteReader();
comboBox1.Items.Add(reader.GetString(reader.GetOrdinal("JOB_ID")));
}
catch (Exception ex)
{
MessageBox.Show("Data has been failed to show: " + ex.Message);
}
finally
{
kon.Close();
kon.Dispose();
}
}
}
}
When I running this program, system will show dialog "operation is not valid due to the current state of the object".
When I running the Program
There's No Error
How to solve this error? I'll bind data to comboBox from database. I mean, I want to add JOB_ID to combo Box such as AD_VP, HR_REP, etc btw.
Btw, I'm sorry if my English is poor.
One obvious thing that's missing is that you didn't call reader.Read() before attempting to read from your data reader. When you call OracleDataReader reader = cmd.ExecuteReader();, at that moment, the data reader is positioned before the first record. You need to call reader.Read() to move it to the first record. This can easily explain the error you got.
OracleDataReader reader = cmd.ExecuteReader();
reader.Read(); // Add this. Check for a return value of false if necessary.
comboBox1.Items.Add(reader.GetString(reader.GetOrdinal("JOB_ID")));
Apart from that, it would also be a good idea to dispose of the data reader properly. You may want to use a using block for this.
So what youa re trying to do is Adding the ordial number of the JOB_ID column to a combobox. As you probably know GetOrdinal returns in int.
Why don't you just go like this:
...
comboBox1.Items.Add(Convert.ToString(reader.GetOrdinal("JOB_ID")));
...
If I want to run multiple SELECT queries on different tables, can I use the same SqlDataReader and SqlConnection for all of them?? Would the following be wise?? (I typed this up fast, so it lacks try/catch):
MySqlCommand myCommand = new MySqlCommand("SELECT * FROM table1", myConnection);
myConnection.Open();
SqlDataReader myDataReader = myCommand.ExecuteReader();
while(myReader.Read())
{
//Perform work.
}
myCommand.commandText = "SELECT * FROM table2";
myReader = myCommand.ExecuteReader();
while(myReader.Read())
{
//Perform more work
}
myReader.Close();
myConnection.Close();
Thanks a lot.
You can use the same connection for each of them, as long as you do not try to execute multiple queries concurrently on the same connection from different threads.
As for the data reader, you are not actually re-using the reader, each call to ExecuteReader returns a new instance of a new reader, all you are re-using is the variable that maintains the reference to the reader. Here in lies a problem, you are only explicitly closing the last reader and leaving the first to be GC'd at some later time.
You can reuse the Command as well, but remember if you supply parameters etc. you will need to clear them for the next query unless they apply to the next query as well.
You should use try/finally blocks to ensure that you clean up the resources, or here is a quick change to your code to use using statements to ensure resource clean-up even if there is an exception that prevents the rest of the code from executing.
using (var myConnection = GetTheConnection())
{
myConnection.Open();
var myCommand = new MySqlCommand("SELECT * FROM table1", myConnection))
using (var myDataReader = myCommand.ExecuteReader())
{
while(myReader.Read())
{
//Perform work.
}
} // Reader will be Disposed/Closed here
myCommand.commandText = "SELECT * FROM table2";
using (var myReader = myCommand.ExecuteReader())
{
while(myReader.Read())
{
//Perform more work
}
} // Reader will be Disposed/Closed here
} // Connection will be Disposed/Closed here
Note: GetTheConnection is just a place holder function for what ever mechanism you are using to get your connection instance.
I generally use Adapters so I am rusty on the details of the reader, but I think you are on the right track.
One item of note in your code is that each call to ExecuteReader should be generating a new data reader. You may be reusing the variable name, but the reference to the existing reader is discarded and replaced by a new one on each call. Point being, close the previous reader before using ExecuteReader to get a new one.
1) I have the following codes:
private static sqlDataReader gCandidateList = null;
public SqlDataReader myCandidateList
{
set
{
gCandidateList = value;
}
get
{
return gCandidateList;
}
}
2) In FormA I have:
sqlConn.ConnectionString = mySettings.myConnString;
sqlConn.Open();
SqlCommand cmdAvailableCandidate = new SqlCommand(tempString, sqlConn);
SqlDataReader drAvailableCandidate = cmdAvailableCandidate.ExecuteReader();
mySettings.myCandidateList = drAvailableCandidate;
sqlConn.Close();
3) In FormB I want to reuse the data saved in myCandidatList so I use:
SqlDataReader drCandidate = mySettings.myCandidateList;
drCandidate.Read();
4) I then got the error "Invalide attempt to call Read when reader is closed."
5) I tried mySettings.myCandidateList.Read() in (3) above and again received the same error message.
6) How can I re-open SqlDataReader drCandidate to read data?
7) Would appreciate very much for advise and help, please.
You can't read reader once the connection is closed or disposed. If you want to use those rows (fetch result) later in your code you need to create a List or DataTable.
For instance,
System.Data.DataTable dt = new System.Data.DataTable();
dt.Load(drAvailableCandidate);
If you want to use the datareader at later stage, you have to specify the same as a parameter to the ExecuteReader Method. Your code in FormA should be changed as below.
sqlConn.ConnectionString = mySettings.myConnString;
sqlConn.Open();
SqlCommand cmdAvailableCandidate = new SqlCommand(tempString, sqlConn);
SqlDataReader drAvailableCandidate = cmdAvailableCandidate.ExecuteReader(CommandBehavior.CloseConnection);
mySettings.myCandidateList = drAvailableCandidate;
sqlConn.Close();
Make sure to dispose the datareader once it is used, as the connection to the database will be held open till the datareader is closed. Better change your code in FormB as below.
using (mySettings.myCandidateList)
{
mySettings.myCandidateList.Read();
}
You're closing the connection before you attempt to read from the reader. That won't work.
When you call Close on the SqlConnection object (sqlConn.Close();) it closes the connection and your data reader. That is why you are getting the error when you try to read from your SqlDataReader from FormB.
What you need to do is change the definition of your myCandidateList property to instead return a representation of the data that you have extracted from the your drAvailableCandidate reader.
Essentially what you need to do is iterate through the rows in the drAvailableCandidate object, extract the values and cache them in your property for later retrieval.
Just to add to the answers already given, if you're using async/await then it's easy to get caught out with this by not awaiting an operation inside a using block of a SqlConnection. For example, doing the following can give the reported error
public Task GetData()
{
using(new SqlConnection(connString))
{
return SomeAsyncOperation();
}
}
The problem here is we're not awaiting the operation inside the using, therefore it's being disposed of before we actually execute the underling async operation. Fairly obvious, but has caught me out before.
The correct thing to do being to await inside the using.
public async Task GetData()
{
using(new SqlConnection(connString))
{
await SomeAsyncOperation();
}
}
I use the following method in a class - which I call from .cs page filling a datareader:
public static SqlDataReader getinfo(string username, string url)
{
//Initialise connection, string stored in Web.config
SqlConnection conNs = new SqlConnection(ConfigurationManager.ConnectionStrings["conNs"].ConnectionString);
SqlCommand cmdNs = new SqlCommand("usp_getinfo", conNs);
cmdNs.CommandType = CommandType.StoredProcedure;
cmdNs.Parameters.Add(new SqlParameter("#username", SqlDbType.VarChar, 50));
cmdNs.Parameters["#username"].Value = username;
cmdNs.Parameters.Add(new SqlParameter("#url", SqlDbType.VarChar, 50));
cmdNs.Parameters["#url"].Value = url;
//Execute Command
conNs.Open();
SqlDataReader dr = cmdNs.ExecuteReader(CommandBehavior.CloseConnection);
//Return data
return dr;
}
Notice 'commendbehavior.closeconnection'.
Then in a .cs page I call it like this:
SqlDataReader drInfo = dataAccess.getinfo(username, url);
//do some stuff
drInfo.Close();
That should close the connection too no?
I am having a problem with the apppool filling up with open connections. Is there a problem with my technique?
Speed is my priority here.
Thanks
I think you need to return a datatable instead of SQLDataReader and you need to close the connection right after you finish reading from the data reader.
Are you sure the connection isn't kept open in the connection pool or is the count always increasing?
it doesn't look like GetInfo returns a lot of data. You're better off not passing a DataReader around like this. Inside GetInfo, perform your Read and return an object that contains the information instead of returning the DataReader. The difference in speed will be inconsequential, and you will eliminate all the pain you're going through.