I have the following code and I was wondering if anyone knew the correct way to handle this.
SqlConnection objConn = new SqlConnection(ConfigurationManager.ConnectionStrings["someConnectionString"].ConnectionString);
SqlCommand objComm = new SqlCommand("usp_someStoredProcedure", objConn);
objComm.CommandType = CommandType.StoredProcedure;
objComm.Parameters.AddWithValue("#Variable1", VOne);
objComm.Parameters.AddWithValue("#Variable2", VTwo);
objConn.Open();
using (IDataReader dr = objComm.ExecuteReader(CommandBehavior.CloseConnection))
{
//do stuff
}
Now, let's say the stored procedure returns nothing, is there a method to handle this?
Normally the section you have marked with //do stuff would contain a
if (dr.Read())
{
// do stuff
}
or a
while (dr.Read())
{
// do stuff
}
The .Read() check ensures that you're only acting if it returned data.
while (dr.Read())
{
Console.WriteLine(String.Format("{0}", reader[0]));
}
you can refer from http://msdn.microsoft.com/en-us/library/y6wy5a0f.aspx
Related
I am trying to search the database and set the results in textboxes. I am
getting error, which says "invalid cast exception". I need your guide please.
private void btn_search_Click(object sender, EventArgs e)
{
con.Open();
string STR="select * from TICKETSALES where REFERENCE="+txtSearch.Text;
cmd = new SqlCommand(STR,con );
dr = cmd.ExecuteReader();
if(dr.Read())
{
txtTrans.Text = dr.GetInt32("TRANSACTIONNUMBER").ToString();
txtPax.Text = dr.GetString("PASSENGERNAME");
}
else
{
MessageBox.Show("Ticket Number not Found");
}
}
Modify your select statement to get required column details.
While assigning values to test box, use column index to get value from dr
Convert value to string assign it to respective textbox
Here is sample implementation
con.Open();
//Use of camelCasing. transactionNumber instead of TRANSACTIONNUMBER
string STR="select transactionNumber,passengerNumber from TICKETSALES where REFERENCE=#search";
cmd = new SqlCommand(STR,con );
cmd.Parameters.Add("#search", txtSearch.Text);
dr = cmd.ExecuteReader();
if(dr.Read())
{
txtTrans.Text = Convert.ToString(dr[0]);
txtPax.Text = Convert.ToString(dr[1]);
}
Few tips for best coding practices (Credits: #tsahi-asher)
Don't pass values inside query, use parameters in query and use Paramere.Add() function to replace parameter with its value
Don't put your sql statements in presentation layer. Have some dedicated layer of SQL.
Don't use select *, use specific column name.
Don't use all-caps for identifier names, use camelCase.
How about something like this:
Note the sql injection protection by paramtising the sql query.
private void btn_search_Click(object sender, EventArgs e)
{
using (SqlConnection con = new SqlConnection(connectionString))
{
string query = "select top 1 TRANSACTIONNUMBER, PASSENGERNAME from ticketsales where reference=#ref";
using (SqlDataAdapter adap = new SqlDataAdapter(query, con))
{
con.Open();
DataTable dt = new DataTable();
adap.SelectCommand.Parameters.AddWithValue("#ref", txtSearch.Text.Trim());
adap.Fill(dt);
if (dt.Rows.Count > 0)
{
txtTrans.Text = dt.Rows[0]["TRANSACTIONNUMBER"].ToString().Trim();
txtPax.Text = dt.Rows[0]["PASSENGERNAME"].ToString().Trim();
}
else
{
MessageBox.Show("Ticket Number not Found");
}
}
}
}
There are few issue already mentioned in comments and posts. I will chip in my remarks - you don't dispose of unmanaged resources, one answer covers that, but it violates your code. So here is alternative solution:
SqlConnection con;
SqlCommand cmd;
SqlDataReader dr;
//some methods, fields
private void btn_search_Click(object sender, EventArgs e)
{
con.Open();
// as it has benn already said, you have to prevent yourself from SQL injection!
cmd = (new SqlCommand("select * from TICKETSALES where REFERENCE=#ref", con)).Parameters.AddWithValue("#res", txtSearch.Text.Trim());
dr = cmd.ExecuteReader();
if (dr.Read())
{
txtTrans.Text = dr.GetInt32("TRANSACTIONNUMBER").ToString();
txtPax.Text = dr.GetString("PASSENGERNAME");
}
else
{
MessageBox.Show("Ticket Number not Found");
}
}
// it looks like you have unamanaged resources held by fields in your form,
// so to release them you have to call their Dispose() method!
// normally you should use using keyword if they were used locally in a method, as other answer states
public void Dispose()
{
base.Dispose();
if (con != null) con.Dispose();
if (cmd != null) cmd.Dispose();
if (dr != null) dr.Dispose();
}
I have this problem: Invalid attempt to call HasRows when reader is closed.
I have tried alot; removing connection close line, closing the connection in the end. but having same issue. I can't get whats wrong with my code.
try
{
con = new SqlConnection(ConfigurationManager.ConnectionStrings["TextItConnectionString"].ConnectionString);
using (con)
{
con.Open();
Library.writeErrorLog("connection build and open");
SqlCommand cmd = con.CreateCommand();
using (cmd)
{
cmd.CommandText = "Select [name] From [dbo].[Users]";
SqlDataReader reader = cmd.ExecuteReader();
using (reader)
{
user.dt.Load(reader);
if (reader.HasRows)
{
while (reader.Read())
{
Library.writeErrorLog(reader.GetString(0));
}
}
else
Library.writeErrorLog("no rows");
reader.Close();
con.Close();
}
}
}
//SqlDataAdapter adap = new SqlDataAdapter("Select [name] From [dbo].[Users]", con);
//adap.Fill(user.dt);
}
catch (Exception ex)
{
Library.writeErrorLog(ex);
}
Thanks for the help!
I assume that user.dt returns a DataTable. You know that DataTable.Load(reader) will consume all records of the resultset and advances the reader to the next set? I'm asking because you are using HasRows after you've already used DataTable.Load.
As Steve has commented
Looking at the reference source of DataTable.Load you could clearly
see that the DataReader is closed before exiting from the method.
So if there is no other resultset(f.e. SELECT * FROM T1;SELECT* from T2) the reader will be closed at the end of Load which will cause the exception if you try to use SqlDataReader.HasRows.
I'd call this a lack of documentation since it's mentioned nowhere on MSDN.
So either use
reader.Read and reader.GetString in a loop and add it to the DataTable manually,
use DataTable.Load and loop the table afterwards or
use SqlDataAdapter.Fill(table):
1) while loop and manually filling the table
using (SqlDataReader reader = cmd.ExecuteReader())
{
if(reader.HasRows)
{
while (reader.Read())
{
string name = reader.GetString(0);
user.dt.Rows.Add(name);
Library.writeErrorLog(name);
}
}
else
Library.writeErrorLog("no rows");
}
2) requires two loops, one in DataTable.Load and the foreach
using (SqlDataReader reader = cmd.ExecuteReader())
{
if(reader.HasRows)
{
user.dt.Load(reader); // all records added
foreach(DataRow row in user.dt.Rows)
{
string name = row.Field<string>(0);
Library.writeErrorLog(name);
}
}
else
Library.writeErrorLog("no rows");
}
3) another option is to use a SqlDataAdapter and it's Fill(dataTable) method:
using (var da = new SqlDataAdapter(cmd))
{
da.Fill(user.dt);
if (user.dt.Rows.Count > 0)
{
foreach (DataRow row in user.dt.Rows)
{
string name = row.Field<string>(0);
Library.writeErrorLog(name);
}
}
else
Library.writeErrorLog("no rows");
}
Side-note: you don't need to use reader.Close or con.Close if you use the using-statement.
I have a problem with the below code,There's no any coding error BUT sometimes it throws some exceptions.I just wanted to know any code organizing issue ? & how to fix it.
Sometimes it shows those exceptions
1.ExecuteReader requires an open and available Connection. The connection's current state is closed.
2.Invalid attempt to call FieldCount when reader is closed.
But Sometimes it works without any issue,as expected
My Coding Goes here
[WebMethod, ScriptMethod]
public static List<HomeImageSliders> GetHomeImageSliders()
{
List<HomeImageSliders> HomeImageList = new List<HomeImageSliders>();
try
{
SqlCommand comHomeImage = new SqlCommand("SP_GetHomeImageSliders", conDB);
comHomeImage.CommandType = CommandType.StoredProcedure;
if (conDB.State != ConnectionState.Open)
{
conDB.Open();
}
SqlDataReader rdr = comHomeImage.ExecuteReader();
DataTable dt = new DataTable();
dt.Load(rdr);
foreach (DataRow r in dt.Rows)
{
HomeImageList.Add(new HomeImageSliders
{
Id = (int)r["Id"],
ImagePath = r["ImagePath"].ToString(),
ModifiedDate = Convert.ToDateTime(r["ModifiedDate"]).Date
});
}
}
catch (Exception ee)
{
}
finally
{
conDB.Close();
}
return HomeImageList;
}
You should use the "using" construction:
(using ommand comHomeImage = new SqlCommand("SP_GetHomeImageSliders", conDB) {
(using SqlDataReader rdr = new SqlDataReader) {
//do some things
}
}
I don't know why your connection sometimes is closed when you call
ExecuteReader()
But why don't you use a using block instead like this:
using(SqlConnection conDB = new SqlConnection(connectionString))
{
...
}
this will close your connection to the DB when it loses scope.
try to prevent using the same connection with two or more threads by usinglock
lock(_conDb)
{
//// your code here
}
and also wrap your conDb in using block as below
using(SqlConnection conDB = new SqlConnection(connectionString))
{
...
}
I normally use DataSet because It is very flexible. Recently I am assigned code optimization task , To reduce hits to the database I am changing two queries in a procedure. one Query returns the count and the other returns the actual data. That is , My stored procedure returns two tables. Now, I know how to read both tables using DataSets, But I need to read both tables using DataReader. In search of that I found This.
I follow the article and wrote my code like this:
dr = cmd.ExecuteReader();
while (dr.Read())
{
}
if (dr.NextResult()) // this line throws exception
{
while (dr.Read())
{
But I am getting an exception at dt.NextResult. Exception is :
Invalid attempt to call NextResult when reader is closed.
I also googled above error , but still not able to solve the issue.
Any help will be much appreciated. I need to read multiple tables using datareader, is this possible?
Try this because this will close connection ,data reader and command once task get over , so that this will not give datareader close exception
Also do check like this if(reader.NextResult()) to check there is next result,
using (SqlConnection connection = new SqlConnection("connection string here"))
{
using (SqlCommand command = new SqlCommand
("SELECT Column1 FROM Table1; SELECT Column2 FROM Table2", connection))
{
connection.Open();
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
MessageBox.Show(reader.GetString(0), "Table1.Column1");
}
if(reader.NextResult())
{
while (reader.Read())
{
MessageBox.Show(reader.GetString(0), "Table2.Column2");
}
}
}
}
}
I have tried to reproduce this issue (also because i haven't used multiple tables in a reader before). But it works as expected, hence i assume that you've omitted the related code.
Here's my test code:
using (var con = new SqlConnection(Properties.Settings.Default.ConnectionString))
{
using (var cmd = new SqlCommand("SELECT TOP 10 * FROM tabData; SELECT TOP 10 * FROM tabDataDetail;", con))
{
int rowCount = 0;
con.Open();
using (IDataReader rdr = cmd.ExecuteReader())
{
while (rdr.Read())
{
String object1 = String.Format("Object 1 in Row {0}: '{1}'", ++rowCount, rdr[0]);
}
if (rdr.NextResult())
{
rowCount = 0;
while (rdr.Read())
{
String object1 = String.Format("Object 1 in Row {0}: '{1}'", ++rowCount, rdr[0]);
}
}
}
}
}
I built on Pranay Rana's answer because I like keeping it as small as possible.
string rslt = "";
using (SqlDataReader dr = cmd.ExecuteReader())
{
do
{
while (dr.Read())
{
rslt += $"ReqID: {dr["REQ_NR"]}, Shpr: {dr["SHPR_NR"]}, MultiLoc: {dr["MULTI_LOC"]}\r\n";
}
} while (dr.NextResult());
}
The question is old but I find the answers are not correct.
Here's how I do it:
List<DataTable> dataTables = new();
using IDataReader dataReader = command.ExecuteReader();
do
{
DataTable dataTable = new();
dataTable.Load(dataReader);
dataTables.Add(dataTable);
}
while (!dataReader.IsClosed);
I'm getting "Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use and max pool size was reached." error, but can't find where is the problem. Little help, please. :)
public static void Update(string p, int c)
{
using (SqlConnection conn = new SqlConnection("ConnectionString"))
{
SqlCommand cmd = new SqlCommand();
SqlDataReader myRdr;
cmd.Connection = conn;
cmd.CommandText = "SOME SQL QUERY #parameter";
cmd.CommandType = CommandType.Text;
cmd.Parameters.Add("#parameter", SqlDbType.NVarChar, 10).Value = p;
conn.Open();
myRdr = cmd.ExecuteReader();
if (myRdr.HasRows)
{
while (myRdr.Read())
{
//do something using myRdr data
}
myRdr.Close();
cmd.Dispose();
foreach (DataRow r in dt.Rows)
{
SqlCommand cmd1 = new SqlCommand();
cmd1.Connection = conn;
cmd1.CommandText = "SOME SQL QUERY #parameter";
cmd1.CommandType = CommandType.Text;
cmd1.Parameters.Add("#parameter", SqlDbType.NVarChar, 10).Value = r["SomeData"];
myRdr = cmd1.ExecuteReader();
myRdr.Read();
//do something with myRdr data
myRdr.Close();
cmd1.Dispose();
int a = Convert.ToInt32(r["SomeData"]) - Convert.ToInt32(r["SomeData1"]);
if (a >= 0)
{
//do something
}
else
{
//do something else and runn the Update() again with some other parameters
Update(x, y); //I think here is some problem...
//because when this condition is not met and program
//does not need to run Update() again, it goes OK and I get no error
}
}
}
else
{
myRdr.Close();
cmd.Dispose();
}
}
}
You should cosider moving your call to Update(x,y) somewhere outside the outer using() block.
Calling it from the inside using means you're establishing another connection to the same database without first freeing the outer one. If you get number of recursive calls then you'll run out of free connections VERY quickly.
In general, using recursion calls in parts of code dealing with databases is considered a very bad practice.
If you REALLY need this recursion here then still it should be done outside the outer using. I'd suggest caching your x,y in some kind of collection and issuing the calls to Update(x,y) by traversing this collection outside the using block, like this :
public static void Update(string p, int c)
{
// I'd suggest Dictionary, but don't know whether your strings are unique
var recursionParameters = new List<KeyValuePair<string, int>>();
using (SqlConnection conn = new SqlConnection("ConnectionString"))
{
...
//do something else and runn the Update() again with some other parameters
//Update(x, y); Don't do it here! Instead:
recursionParameters.Add(new KeyValuePair<string, int>(x,y));
...
}
foreach (var kvp in recursionParameters
{
Update(kvp.Key, kvp.Value)
}
}
Your Reader may not be closed when exception occurs. Surround it with try finally block, so when you meet exception your reader will be closed on finally statement.
try
{
myRdr = cmd.ExecuteReader();
// do some other stuff
}
catch(SqlException)
{
// log the exception or deal with ex.
}
finally
{
myRdr.Close(); // you can be sure it will be closed even on exception
}