I have been playing with the demo code from this msdn article by Jeffrey Richter.
I have added a new function to his ApmToCcrAdapters to handle the SqlCommand.BeginExecuteReader. Only it is closing the reader before I can read it.
The following code is used to provide a FromIteratorHandler:
private static IEnumerator<ITask> AsyncReaderDemoHandler()
{
SqlDataReader reader = null;
SqlConnection connection = new SqlConnection(#"Data Source=.\SQLEXPRESS;Initial Catalog=BizData;Integrated Security=True;Async=True;");
string query = "SELECT * FROM Account;";
SqlCommand command = new SqlCommand(query,connection);
connection.Open();
yield return Arbiter.Choice(ApmToCcrAdapters.GetReader(command),
delegate(SqlDataReader r) { Msg("Got SQL data"); reader = r; },
delegate(Exception e) { Msg("Failed to get SQL data"); });
connection.Close();
if (reader == null) yield break;
//This is where the code fails: Reader is Closed!
while (reader.Read())
{
Console.WriteLine(reader["Account"]);
}
}
Which in turn calls the following code:
/// <summary>
/// Gets the Reader, requires connection to be managed
/// </summary>
public static PortSet<SqlDataReader, Exception> GetReader(SqlCommand sqlCommand)
{
Port<SqlDataReader> portResponse = null;
Port<Exception> portException = null;
GetReaderResponse(sqlCommand, ref portResponse, ref portException);
return new PortSet<SqlDataReader, Exception>(portResponse, portException);
}
// Wrapper for SqlCommand's GetResponse
public static void GetReaderResponse(SqlCommand sqlCom,
ref Port<SqlDataReader> portResponse, ref Port<Exception> portException)
{
EnsurePortsExist(ref portResponse, ref portException);
sqlCom.BeginExecuteReader(ApmResultToCcrResultFactory.Create(
portResponse, portException,
delegate(IAsyncResult ar) { return sqlCom.EndExecuteReader(ar); }), null);
}
The connection must remain open for the Reader to work. I believe closing the connection is your problem. Leave the connection open and call dispose on the reader when done and I think that should clean up the connection.
Related
i'm creating login and register page in MVC asp.net but problem is that for each purpose i have to do all SQl connection, command, try open etc which really makes it slow so i as wondering that if i can get rid of creating ti again and again and just create sqlconnection thing once and calling it again and again for login, registration etc
namespace LoginSys.Models
{
public class database
{
public ConnectionStatus connectDB(String name, String email, String pwd, String conStr)
{
// var con = conStr;
SqlConnection sqlCon = new SqlConnection(conStr);
SqlCommand sqlCom = new SqlCommand();
sqlCom.Connection = sqlCon;
sqlCom.CommandText = "insert into tblRegister (userName, userEmail, userPwd) values (#name, #email, #pwd)";
sqlCom.Parameters.AddWithValue("#name", name);
sqlCom.Parameters.AddWithValue("#email", email);
sqlCom.Parameters.AddWithValue("#pwd", pwd);
ConnectionStatus connectStatus = new ConnectionStatus();
int row_aff;
try
{
sqlCon.Open();
row_aff = sqlCom.ExecuteNonQuery();
connectStatus.Message = "OK";
}
catch(Exception ex)
{
connectStatus.Message = ex.Message;
}
finally
{
sqlCon.Close();
}
return connectStatus;
}
}
}
Actually you are not creating a new physical connection to the database. ADO.NET uses a connection pool. This means that even if you create a new instance of SqlConnection, you are not actually creating a new physical connection but are drawing one from the pool. Also when you call the .Close() method you are not actually closing the underlying connection. You are just returning it to the connection pool so that it can be reused.
So your code won't be slow because of that.
The only improvement I may recommend you to this code is to wrap the IDisposable objects in using statements:
public ConnectionStatus ConnectDB(string name, string email, string pwd, string conStr)
{
using (SqlConnection sqlCon = new SqlConnection(conStr))
using (SqlCommand sqlCom = sqlCon.CreateCommand())
{
sqlCon.Open();
sqlCom.CommandText = "insert into tblRegister (userName, userEmail, userPwd) values (#name, #email, #pwd)";
sqlCom.Parameters.AddWithValue("#name", name);
sqlCom.Parameters.AddWithValue("#email", email);
sqlCom.Parameters.AddWithValue("#pwd", pwd);
ConnectionStatus connectStatus = new ConnectionStatus();
try
{
sqlCom.ExecuteNonQuery();
connectStatus.Message = "OK";
}
catch(Exception ex)
{
connectStatus.Message = ex.Message;
}
return connectStatus;
}
}
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 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
I have DataReader that holds the results from a stored procedure caal. The results consist of two fields ...
UserID
UserName
Normally I bind these results to an ASP.NET dropdownlist control ...
ddlUserList.DataSource = rdr // rdr is the DataReader
ddlUserList.DataTextField = "UserName"
ddlUserList.DataValueField = "UserID"
ddlUserList.DataBind()
However I am now trying to accomplish the same thing using jQuery AJAX. What I am stuck on is how to manually convert the dataset held in the DataReader to JSON. How are multiples values separated? Does this look correct?
{{"UserID":1, "UserName":"Bob"}, {"UserID":2, "UserName":"Sally"},{"UserID":3, "UserName":"Fred"}}
I realize there are libraries out there such as JSON.NET to handle the serialization but I am in the learning stage now and want to make sure I understand everything from the bottom up.
Was wondering if you have tried using System.Web.Script.Serialization.JavaScriptSerializer library?
You can look at Rick Stahl's blog on this:
http://www.west-wind.com/weblog/posts/737584.aspx
Or you could also do something like create a method that will pull out data from the datareader and place it in a list of objects. (See code below). These list of object will be serialized using the JavaScriptSerializer library.
Hope this helps!
public class User
{
public int UserId { get; set; }
public string UserName { get; set;}
}
public class DataLayer
{
public string GetUsers(string connString)
{
string result = null;
List<User> users = null;
// get data using SqlReader
using(var conn = new SqlConnection(connString))
{
using(var cmd = new SqlCommand{ Connection = conn, CommandText = "SELECT * FROM Users", CommandType = CommandType.Text })
{
conn.Open();
var reader = cmd.ExecuteReader(CommandBehavior.CloseConnection);
if(!reader.HasRows)
return null;
//convert data reader to a list of user objects
users = (List<User>)ConvertToList<User>(ref reader);
conn.Close();
}
}
//convert list of objects in list to json objects
var jsonSerializer = new System.Web.Script.Serialization.JavaScriptSerializer();
result = jsonSerializer.Serialize(users);
return result;
}
public static IList<T> ConvertToList<T>(ref SqlDataReader reader)
{
IList<T> result = null;
if (reader.IsClosed)
return result;
result = new List<T>();
T item = default(T);
while (reader.Read())
{
//create item instance
item = (T)Activator.CreateInstance<T>();
//get class property members
var propertyItems = item.GetType().GetProperties();
//populate class property members with data from data reader
for (int ctr = 0; ctr < reader.FieldCount; ctr++)
{
if(reader.GetName(ctr) == propertyItems[ctr].Name)
propertyItems[ctr].SetValue(item, UtilsHelper.GetValue<string>(reader[ctr]), null);
}
//add item to list
result.Add(item);
}
reader.Close();
reader.Dispose();
return result;
}
}
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
}
}