Can you use 2 'using' statements for both sqlconnection and sqldatareader? - asp.net

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);
}
}

Related

SqDataReader closed issue in .net

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

Invalid attempt to call Read when reader is closed

I'm kind of struggling trying to get this datagrid to bind. Everytime I run my code, I get an error message stating, "Invalid attempt to call Read when reader is closed". I don't see where I am closing my reader. Can you please help me? My code for loading the datagrid is below:
protected void LoadGrid()
{
using (SqlConnection conn = new SqlConnection())
{
conn.ConnectionString = ConfigurationManager.ConnectionStrings["VTC"].ConnectionString;
conn.Open();
string sql = "select * from roi_tracking";
using (SqlCommand cmd = new SqlCommand(sql, conn))
{
using (SqlDataReader sqlReader = cmd.ExecuteReader())
{
gridROI.DataSource = sqlReader;
gridROI.DataBind();
sqlReader.Dispose();
cmd.Dispose();
}
}
}
}
You can't use a SqlDataReader as a DataSource for a DataGrid.
From MSDN:
A data source must be a collection that implements either the
System.Collections.IEnumerable interface (such as
System.Data.DataView, System.Collections.ArrayList, or
System.Collections.Generic.List(Of T)) or the IListSource interface to
bind to a control derived from the BaseDataList class.
Datasource property:
http://msdn.microsoft.com/en-us/library/system.web.ui.webcontrols.basedatalist.datasource.aspx
SqlDataReader:
http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqldatareader.aspx
A common methodology to bind the query results to your datagrid would be to use a SqlDataAdapter to fill a DataTable or DataSet and then bind that to your DataGrid.DataSource:
protected void LoadGrid()
{
using (SqlConnection conn = new SqlConnection())
{
conn.ConnectionString = ConfigurationManager.ConnectionStrings["VTC"].ConnectionString;
conn.Open();
string sql = "select * from roi_tracking";
using (SqlCommand cmd = new SqlCommand(sql, conn))
{
SqlDataAdapter adapter = new SqlDataAdapter();
adapter.SelectCommand = cmd;
adapter.Fill((DataTable)results);
gridROI.DataSource = results;
}
}
}
In addition to what pseudocoder said, you wouldn't want to bind to a SqlDataReader anyway: the connection to the database will remain open so long as the reader instance exists.
You definitely want to deserialize the data into some other disconnected data structure so that you release the connection back into the pool as quickly as possible.

Reusable code to retrieve data

i have found an very good method for retrieving any result set from the database just by specifying the stored procedure name.i think the code is very much reusable.code is as follows
using System.Data;
using System.Data.SqlClient;
private DataSet GetFreshData(string sprocName)
{
using ( SqlConnection conn = new SqlConnection() )
{
using ( SqlDataAdapter da = new SqlDataAdapter() )
{
da.SelectCommand = new SqlCommand();
da.SelectCommand.CommandText = sprocName;
da.SelectCommand.CommandType = CommandType.StoredProcedure;
da.SelectCommand.Connection = conn;
DataSet ds = new DataSet();
try
{
da.SelectCommand.Connection.Open();
da.Fill(ds);
da.SelectCommand.Connection.Close();
}
catch
{
return null;
}
finally
{
// do other things...calling Close() or Dispose()
// for SqlConnection or SqlDataAdapter objects not necessary
// as its taken care of in the nested "using" statements
}
return ds;
}
}
}
my question is can someone suggest a modification to this method when the stored procedure need to specify several parameters
Easy! :) take a SqlParameter[] as the second argument to the function.
Then make sure da.SelectCommand.Parameters is filled with the list of SqlParameter objects in the SqlParameter[]

how to close a sqlconnection in asp.net

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(); }
}
}

Asp.Net select in Sql

This is going to be very simple I know. I have seen so many different ways of using sql in asp.net with no real standard. What I want to know is how to cleanly select from an sql database in asp.net and retrieve multiple records. For example: select all userids.
String sql =
"SELECT [UserId] FROM [UserProfiles] WHERE NOT [UserId] = 'CurrentUserId'";
string strCon = System.Web
.Configuration
.WebConfigurationManager
.ConnectionStrings["SocialSiteConnectionString"]
.ConnectionString;
SqlConnection conn = new SqlConnection(strCon);
SqlCommand comm = new SqlCommand(sql, conn);
conn.Open();
/*
This is where I need to know how to retrieve the information from the
above command(comm). I am looking for something similiar to php's
mysql_result. I want to access the records kind of like an array or some
other form of retrieving all the data.
Also when the new SqlCommand is called...does that actual run the
SELECT STATEMENT or is there another step.
*/
conn.Close();
I think that this is what you are looking for.
String sql = "SELECT [UserId] FROM [UserProfiles] WHERE NOT [UserId] = 'CurrentUserId'";
string strCon = System.Web
.Configuration
.WebConfigurationManager
.ConnectionStrings["SocialSiteConnectionString"].ConnectionString;
SqlConnection conn = new SqlConnection(strCon);
SqlCommand comm = new SqlCommand(sql, conn);
conn.Open();
SqlDataReader nwReader = comm.ExecuteReader();
while (nwReader.Read())
{
int UserID = (int)nwReader["UserID"];
// Do something with UserID here...
}
nwReader.Close();
conn.Close();
I do have to say, though, that the overall approach can use a lot of tuning. First, you could at least start by simplifying access to your ConnectionString. For example, you could add the following to your Global.asax.cs file:
using System;
using System.Configuration;
public partial class Global : HttpApplication
{
public static string ConnectionString;
void Application_Start(object sender, EventArgs e)
{
ConnectionString = ConfigurationManager.ConnectionStrings["SocialSiteConnectionString"].ConnectionString;
}
...
}
Now, throughout your code, just access it using:
SqlConnection conn = new SqlConnection(Global.ConnectionString);
Better yet, create a class in which the "plumbing" is hidden. To run the same query in my code, I'd just enter:
using (BSDIQuery qry = new BSDIQuery())
{
SqlDataReader nwReader = qry.Command("SELECT...").ReturnReader();
// If I needed to add a parameter I'd add it above as well: .ParamVal("CurrentUser")
while (nwReader.Read())
{
int UserID = (int)nwReader["UserID"];
// Do something with UserID here...
}
nwReader.Close();
}
This is just an example using my DAL. However, notice that there is no connection string, no command or connection objects being created or managed, just a "BSDIQuery" (which does lots of different things in addition to that shown). Your approach would differ depending on the tasks that you do most often.
Most of the time, I use this (note that I am also using a connection pooling approach):
public DataTable ExecuteQueryTable(string query)
{
return ExecuteQueryTable(query, null);
}
public DataTable ExecuteQueryTable(string query, Dictionary<string, object> parameters)
{
using (SqlConnection conn = new SqlConnection(this.connectionString))
{
conn.Open();
using (SqlCommand cmd = conn.CreateCommand())
{
cmd.CommandText = query;
if (parameters != null)
{
foreach (string parameter in parameters.Keys)
{
cmd.Parameters.AddWithValue(parameter, parameters[parameter]);
}
}
DataTable tbl = new DataTable();
using (SqlDataAdapter da = new SqlDataAdapter(cmd))
{
da.Fill(tbl);
}
return tbl;
}
}
}
Here's an adaption of your existing code:
String sql = "SELECT [UserId] FROM [UserProfiles] WHERE [UserId] != #CurrentUserId";
string strCon = System.Web
.Configuration
.WebConfigurationManager
.ConnectionStrings["SocialSiteConnectionString"].ConnectionString;
DataTable result = new DataTable();
using (var conn = new SqlConnection(strCon))
using (var cmd = new SqlCommand(sql, conn))
{
cmd.Parameters.Add("#CurrentUserID", SqlDbType.Int).Value = CurrentUserID;
conn.Open();
result.Load(cmd.ExecuteReader());
}
Creating a SqlCommand doesn't execute it at all.
The command will be executed when you call ExecuteReader or something similar.
If you want something which will fetch all the results into memory, you should be looking at DataSet/DataTable. There's a tutorial for them here - or there are plenty of others on the net, and any decent ADO.NET book will cover them too.
If you don't want to fetch them all into memory at once, then ExecuteReader it the method for you. That will return a SqlDataReader which is like a database cursor - it reads a row at a time, and you ask for individual columns as you want them, calling Read to get to the next row each time.
Whereas in PHP you'd do something like,
while ($row = mysql_fetch_array ($result))
{
//this assumes you're doing something with foo in loop
$foo = $row["userid"];
//using $foo somehow
}
in .NET, you do something different. Believe me, originating from a PHP background, the transition from PHP to .NET is not easy. There's a lot of things that will seem bizarre. After a while though, it will make sense! Just stick it out. I personally like it better.
Ok.. assuming you have a DataSet like you say, you can do something like this,
//assuming you have a DataSet called myDataSet
for (int i = 0; i < myDataSet.Tables[0].Rows.Count; i++)
{
//likewise assuming here you're doing something with foo in loop
string foo = myDataSet.Tables[0].Rows[i]["userid"].ToString();
//similarly do something with foo in loop
}
That does the same thing as the PHP snippet.

Resources