i would like to know if there's something wrong in this asp.net code:
mydatareader = mycmd.executeReader()
if myDataReader.HasRow then
// Do something
end if
myConnection.Close()
If i avoid to call a "MyDataReader.Close()" does the connection close anyway ?
I ask this because i'm assuming that if call a "MyConn.Close" it automatically close the associated datareader... or am i wrong ?
Thanks
You have to close your reader instead of closing the connection. Have a look here:
http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqldatareader.close.aspx
The best practice to perform such operations is as follows:
using(SqlConnection connection = new SqlConnection(connStr)) {
connection.Open();
SqlCommand command = new SqlCommand(connection, "SELECT...");
SqlDataReader reader = command.ExecuteReader();
// Fill your container objects with data
}
The using statement:
Defines a scope, outside of which an object or objects will be disposed.
So you can be assured that your connection, command and reader variables will be closed and disposed accordingly when exiting the using block.
If you neither close the data reader nor the connection, garbage collector will do it for you. On the other side, this will probably affect the performance of your application.
I'm not absolutely sure but I'm always using try-catch-finally block for db actions:
using (SqlConnection cnn = new SqlConnection(ConnectionString))
{
using (SqlCommand cmmnd = new SqlCommand("SELECT Date();", cnn))
{
try
{
cnn.Open();
using (SqlDataReader rdr = cmmnd.ExecuteReader())
{
if (rdr.Read()) { result = rdr[0].ToString(); }
}
}
catch (Exception ex) { LogException(ex); }
finally { cnn.Close(); }
}
}
Related
I have a method inside a class, that contains SqlDataReader(it connects to a database table and retrieves a number of rows).
later i want to assign each record that SqlDataReader has, to asp labels . but i don't know what should be the return type of a method , so i can extract the values from it, and how to do that in code behind file.
here is the code;
public (???) displayCustoemrhShipOrder()
{
string htmlStr = string.Empty;
string sConnectionString = ConfigurationManager.ConnectionStrings["LGDB"].ToString();
SqlConnection SqlCOn = new SqlConnection(sConnectionString);
SqlCommand SqlCmd = new SqlCommand();
SqlCmd.Connection = SqlCOn;
SqlCmd.CommandText = "displayCustomerShipOrder";
SqlCmd.CommandType = CommandType.StoredProcedure;
SqlCOn.Open();
SqlCmd.Parameters.AddWithValue("ShipOrderID",shipOrderID);
SqlDataReader reader = SqlCmd.ExecuteReader();
while (reader.Read())
{
htmlStr = reader.GetInt32(0).ToString();
}
reader.Close();
SqlCOn.Close();
return(???)
}
In general, you should never return SqlDataReader to a layer outside of your database-access code. The reason behind this general principle is that SqlDataReader is a very "expensive" object that keeps an open connection to the database! So if you keep a SqlDataReader around, it's not so bad, but if you keep 10 lying around at any given time? what about 100 of them? 1000? It's a recipe for disaster.
So how do you close the connection to the database from a SqlDataReader? .NET has a very handy methods in the IDisposable interface called Dispose() that will take care of the cleanup for you. So your code might looks like
function getData()
{
// instantiate SqlDataReader from SqlCommand, call it "rdr"
rdr.Dispose();
}
But actually there's an issue with that code. What if your code throws an exception before it gets to Dispose()? It should really look like
function getData()
{
try{
// instantiate SqlDataReader from SqlCommand, call it "rdr"
}
catch(Exception){}
Finally{
rdr.Dispose();
}
}
That's very verbose! What if you don't want to type all that? Don't worry, .NET handled this for you as well. Do this:
public (???) displayCustoemrhShipOrder()
{
string htmlStr = string.Empty;
string sConnectionString = ConfigurationManager.ConnectionStrings["LGDB"].ToString();
using(SqlConnection SqlCOn = new SqlConnection(sConnectionString))
{
using(SqlCommand SqlCmd = new SqlCommand())
{
SqlCmd.Connection = SqlCOn;
SqlCmd.CommandText = "displayCustomerShipOrder";
SqlCmd.CommandType = CommandType.StoredProcedure;
SqlCOn.Open();
SqlCmd.Parameters.AddWithValue("ShipOrderID",shipOrderID);
using(SqlDataReader reader = SqlCmd.ExecuteReader())
while (reader.Read())
{
htmlStr = reader.GetInt32(0).ToString();
}
// reader.Close(); // this line becomes optional, Dispose() will call Close()
}
// SqlCOn.Close(); // this line becomes optional, Dispose() will call Close()
}
}
return(???)
}
This is all a roundabout way of saying that if you know you need to Dispose() and Close() the SqlDataReader, and that you cannot get data out of it once you do that, then you obviously should not return it to code what needs to work with the data (but not necessarily the database connection that it was taking up). I suggest using a DataTable class, you can read about it here:
.NET DataTable class
Also the method to convert to a DataTable is called DataTable.Load(SqlDatareader...
You can add each individual value to List<string> return it and use outside of the method.
I have an ASP.NET web page. It makes use of 4 BackgroundWorkers. Each bw retrieves some data from a database.
The code for connecting to the database is:
if (dbConnection.State == ConnectionState.Closed)
{
dbConnection.Open();
}
DataTable dt = new DataTable();
OdbcCommand cmd = new OdbcCommand(sqlQuery, dbConnection);
cmd.CommandTimeout = 0;
IDataReader dataReader = cmd.ExecuteReader();
dt.Load(dataReader);
dataReader.Close();
dataReader.Dispose();
In the constructor, this.dbConnection = new OdbcConnection(networkdetails);
Each bw makes use of the above code snippet to query the database and retrieve the values. The code works perfectly fine sometimes. Other times it throws the exception given above.
Any help as to what I may be doing wrong?
Try to handle the exception and then close the connection.
For this, write your code in 'Try' block, catch the exception in 'Catch' block and close the connection in 'Finally' block.
try{
// Your code
}
catch
{
// Catch exception
}
Finally
{
// Close the connection
dbConnection.Close();
}
i have created a function which executes query and returns SqlDataReader, now i am using that in another function work with the returned data, but i gets the error saying reader is already closed. here is the functions:
public static SqlDataReader ExecuteReader(string procedure, SqlParameter[] parameters, CommandType commandType)
{
SqlDataReader reader = null;
using (SqlConnection connection = new SqlConnection(connectionString))
{
using (SqlCommand command = new SqlCommand(procedure, connection))
{
connection.Open();
if(parameters != null)
{
if (commandType == CommandType.StoredProcedure)
command.Parameters.AddRange(parameters);
}
reader = command.ExecuteReader();
}
}
return reader;
}
here is the code where i am calling the SqlDataReader
using (SqlDataReader reader = SqlHelper.ExecuteReader("select top 10 username from users", null, System.Data.CommandType.Text))
{
Response.Write(reader.IsClosed); //This returns True
}
EDIT
ExecuteReader with CommanBehavior ( automatically close connection after reading data)
To over come connection closing proble just make use of CommandBheviour
- CommandBehavior.CloseConnection
When you pass above values as argument to ExecuteReader
1. there is no need to close connection explicitly connection get close when you close your reader.
code will be like this no need to close connection explicitly
public void CreateMySqlDataReader(string mySelectQuery,string myConnectionString)
{
SqlConnection myConnection = new SqlConnection(myConnectionString);
SqlCommand myCommand = new SqlCommand(mySelectQuery, myConnection);
myConnection.Open();
SqlDataReader myReader = myCommand.ExecuteReader(CommandBehavior.CloseConnection);
while(myReader.Read())
{
Console.WriteLine(myReader.GetString(0));
}
myReader.Close();
//Implicitly closes the connection because CommandBehavior.CloseConnection was specified.
}
its causing problem because you are closing connection
SqlReader always make use of open connection i.e live connection which is open when you use this
using (SqlConnection connection = new SqlConnection(connectionString))
{
}
it dispose connection object which is used by reader object that why its returing IsColosed as true
If you wanto return value the objec than make use of DataTable which is disconnected data object and doens makse use of connection
Modified code
public static DataTable ExecuteReader(string procedure, SqlParameter[] parameters, CommandType commandType)
{
DataTable dt = null;
using (SqlConnection connection = new SqlConnection(connectionString))
{
using (SqlCommand command = new SqlCommand(procedure, connection))
{
connection.Open();
if(parameters != null)
{
if (commandType == CommandType.StoredProcedure)
command.Parameters.AddRange(parameters);
}
SqlDataAdapter da = new SqlDataAdapter(command);
da.Fill(dt);
}
}
return dt;
}
DataReader needs an Open Connection. What you can do it either return a DataTable or Have custom class to represent the results of your SQL query and return an instance of that.
Create a Class to represent your Entity
public class Customer
{
public int ID { set;get;}
public string Name { set;get;}
}
And inside your method;
public List<Customer> GetCustomer()
{
List<Customer> custList=new List<Customer>();
using (SqlConnection connection = new SqlConnection(connectionString))
{
using (SqlCommand command = new SqlCommand("yourParameterizedSQLQuery",
connection))
{
//Add parameters if needed
connection.Open();
using (var reader = cmd.ExecuteReader())
{
if (reader.HasRows)
{
cust=new Customer();
while(reader.Read())
{
var cust=new Customer();
// TO DO :Do db null checking before reading
cust.ID=reader.GetInt32(reader.GetOrdinal("ID"));
cust.Name=reader.GetString(reader.GetOrdinal("Name"));
custList.Add(cust);
}
}
}
}
}
return custList;
}
The problem is that you have using SqlConnection which closes the connection to your database when leaving the scope.
SqlDataReader needs a "still open" connection. Returning it to the parent does not keep the connection open.
Your choice are basically to return a DataSet, which is an "unconnected" data source or change the way you manage your connection to open it, use the SqlDataReader, close the connection.
You may have to leave the connection open and let the calling code close the connection associated with the reader.
I had this challenge so I change my return type to DataTable
reader = command.ExecuteReader();
DataTable dt = new DataTable();
dt.Load(reader);
return dt;
That way I don't have to worry about closing the connection outside that method
Can you use 2 'using' statements like:
using (SqlConnection ..)
{
using(SqlDataReader reader = new SqlDataReader())
{
}
}
I'm trying to do this put getting an error on the constructor of the SqlDataReader
SqlDataReader has no constructor. You are returned a datareader by calling the ExecuteReader method of a SqlCommand object.
e.g.
using (SqlConnection ..)
{
SqlCommand cmd = new SqlCommand(...);
using(SqlDataReader reader = cmd.ExecuteReader()))
{
}
}
You can and you can also format them without the extra brackets like so:
using (SqlConnection ..)
using(SqlDataReader reader = new SqlDataReader())
{
}
Which I do all the time to limit the amount of scope nesting.
You can't instantiate a SqlDataReader like that as mentioned above. Generally I see 2 levels of using blocks, but the inner one would be the command object, something like this:
using (var conn = new SqlConnection(...))
{
conn.Open();
using (var cmd = new SqlCommand(...))
{
var rdr = cmd.ExecuteReader(CommandBehavior.CloseConnection);
}
}
I have a generic method to call a stored Procedure in ASP.NET:
public SqlDataReader ExecuteStoredProc(string sprocName, SqlParameter[] SqlP)
{
SqlDataReader iReader;
SqlCommand sql = new SqlCommand();
sql.CommandText = sprocName;
sql.CommandType = CommandType.StoredProcedure;
sql.Connection = ConnStr;
if (SqlP != null)
{
foreach (SqlParameter p in SqlP)
{
sql.Parameters.Add(p);
}
}
sql.Connection.Open();
iReader = sql.ExecuteReader(CommandBehavior.CloseConnection);
sql.Dispose();
return iReader;
}
Even though I am calling CommandBehavior.CloseConnection the connection is not closing. I can get the data fine the first time I request a page. On reload I get the following error:
The connection was not closed. The
connection's current state is open.
Description: An unhandled exception
occurred during the execution of the
current web request. Please review the
stack trace for more information about
the error and where it originated in
the code.
Exception Details:
System.InvalidOperationException: The
connection was not closed. The
connection's current state is open.
Source Error:
Line 35: Line 36: } Line
37: sql.Connection.Open();
Line 38: iReader =
sql.ExecuteReader(CommandBehavior.CloseConnection);
Line 39: sql.Dispose();
Finally if I put sql.Connection.Close(); before sql.Dispose(); I get an error that iReader is not readable because it's been closed already.
Obviously I am closing my connection incorrectly, can someone point me in the right direction?
When you return a DataReader, the underlying connection must remain open. It's the consumer's responsibility to properly clean up resources.
public SqlDataReader ExecuteStoredProc(string sprocName, SqlParameter[] SqlP)
{
SqlCommand sql = new SqlCommand();
sql.CommandText = sprocName;
sql.CommandType = CommandType.StoredProcedure;
sql.Connection = ConnStr;
if (SqlP != null)
{
foreach (SqlParameter p in SqlP)
{
sql.Parameters.Add(p);
}
}
sql.Connection.Open();
return sql.ExecuteReader(CommandBehavior.CloseConnection);
}
public void ConsumingMethod()
{
using(SqlDataReader reader = ExecuteStoredProc("MyProc", params))
{
while(reader.Read())
{
//work with your reader
}
}
}
I would suggest wrap the sql connection with a "using" statement, and that will take care of most sql connection issue.
using (var conn = new SqlConnection("..."))
{
conn.Open();
using (var cmd = conn.CreateCommand())
{
cmd.CommandText = "...";
using (var reader = cmd.ExecuteReader())
{
while (reader.Read())
{
// ...
}
}
}
}
The idea is to do a Connection.Close(); after you've finished with the SqlReader, so basically instead of placing the close() statement before the SqlReader.Dispose() command, you should place it below.
This is my preferred way to process IDataReader. Let the caller creates SqlConnection instance and passes to methods.
It's expensive to create SqlConnection instance. And you’ll end up code call the same ExecuteStoredProc method multiple times in different situation.
Thus, I refactor ExecuteStoredProc method by adding SqlConnection instance as part of the parameter.
using (SqlConnection conn = new SqlConnection())
{
conn.ConnectionString = // Connection String;
conn.Open();
using (IDataReader reader = foo.ExecuteStoredProc(conn, sprocName, SqlP))
{
// Process IDataReader
}
}