I have 2 methods as below :
internal static SqlDataReader SelectData(string sql)
{
using (var sqlConnection = new SqlConnection(Constant.ConnectionString))
{
sqlConnection.Open();
var sqlCommand = new SqlCommand(sql, sqlConnection);
var dataReader = sqlCommand.ExecuteReader();
return dataReader;
}
}
============
And using this method as :
var dataReader = SelectData(---some sql ---);
private void AddData(dataReader)
{
while (dataReader.Read())
{
Employee e = new Employee();
e.FirstNamei = dataReader["Name"].ToString();
}
dataReader.Close();
}
I know we can merge this two method, but I am looking at better way write this, OR this can cause some problem??
Actually you are in fact leaving yourself open a bit. You really want to write it like this:
using (SqlConnection cnn = new SqlConnection(cnnString))
using (SqlCommand cmd = new SqlCommand(sql, cnn))
{
// use parameters in your SQL statement too, so you can do this
// and protect yourself from SQL injection, so for example
// SELECT * FROM table WHERE field1 = #parm1
cmd.Parameters.AddWithValue("#parm1", val1);
cnn.Open();
using (SqlDataReader r = cmd.ExecuteReader())
{
}
}
because you need to make sure these objects get disposed. Further, by going this direction you don't need dataReader.Close(). It will get called when it gets automatically disposed by the using statement.
Now, wrap that collection of statements inside a try...catch and you're in business.
A couple of things
1) Since you're closing your connection on SelectData, dataReader should blow up on AddData as it requires an open connection
2) AddData shouldn't close dataReader as he didn't open it.
3) Maybe you're hiding some code but I don't see that you use Employee instance created on AddData
Technically, first method would be correct if you would do
sqlCommand.ExecuteReader(CommandBehavior.CloseConnection);
Then, your client would close the reader and your connection will be closed as well.
Second example would also be correct if you didn't close the reader inside of it. There is no criminal in passing the reader to a method just to iterate it. but it has to be controlled from where it was created. How you open and dispose of it - this is different question.
Related
I would like to refactor my SqlDataReader code, so it's using using..
SqlDataReader reader = null;
reader = xdCmd.ExecuteReader();
// use reader..
Can I use solution 1) where I declare the reader in the using and first later initialize it with a SqlDataReader, and still get the Dispose "features" using provides? Or do I need to do it as in solution 2), where the initialization happends right away, in the using?
I would guess that 1) is fine, but I'm not sure.
1)
using (SqlDataReader reader = null)
{
xdCmd.CommandText = $"select * from {tableName}";
reader = xdCmd.ExecuteReader();
// use reader..
}
2)
using (SqlDataReader reader = new SqlDataReader(new SqlCommand($"select * from {tableName}"), xdCon))
{
reader = xdCmd.ExecuteReader();
// use reader..
}
The C# language does not include syntax to express the concept of object "ownership" or lifetime management unlike Rust, so it's entirely up to an API's documentation to say if an object's constructor takes ownership of its arguments or not (and thus who gets to call .Dispose()). This is not something the C# compiler can determine for you. However .Dispose() implementations must be idempotent anyway so there is no harm in calling .Dispose() multiple (redundant) times.
Just follow the C# idiomatic conventions of stacked using statements:
using( SqlConnection c = new SqlConnection( connectionString ) )
using( SqlCommand cmd = c.CreateCommand() )
{
await c.OpenAsync().ConfigureAwait(false);
cmd.CommandText = "SELECT foo, bar FROM baz";
using( SqlDataReader rdr = await cmd.ExecuteReaderAsync().ConfigureAwait(false) )
{
...
}
}
for me i prefere to use the second solution it's more subtle , or you can use my example just below :
using (var sqlConnection = new SqlConnection(connection))
{
using (var command = new SqlCommand(query, sqlConnection))
{
using (var read = command.ExecuteReader())
{
// process on your read
}
}
}
This is causing me a headache. I know this question (or atleast variants of it) has been asked many times but before one flags it as a duplicate please consider the following code:
string myConnectionString = myConnectionString = ConfigurationManager.ConnectionStrings["DBCS"].ToString();
SqlConnection mySQLConnection;
SqlCommand mySQLCommand;
SqlDataReader mySQLDataReader;
using (mySQLConnection = new SqlConnection(myConnectionString))
{
mySQLCommand = new SqlCommand("SELECT TOP 1 * FROM Table ORDER BY Id DESC", mySQLConnection);
mySQLCommand.Connection = mySQLConnection;
mySQLCommand.Connection.Open();
using(mySQLDataReader = mySQLCommand.ExecuteReader())
{
if (mySQLDataReader.HasRows)
{
if (mySQLConnection.State == ConnectionState.Open)
{
while (mySQLDataReader.Read())
{
//Perform Logic : If the last record being returned meets some condition then call the below method
MethodCalled();
}
}
}
}
MessageBox.Show("Connection state: " + mySQLConnection.State);
}
I would like to find a way to either:
Close the reader after it has finished reading
Break out of the while-loop when it has finished reading and there are no more rows left
But I just keep on getting a SqlException stating the following:
invalid attempt to call read when reader is closed
Just from broad observation, I can trace that error is due to me returning data that contains one row only. The problem is that after it has read that row, the compiler goes back to While(mySQLDataReader.Read()){} and attempts to read through a table that does not contain any rows.
I attempted the following:
Wrapping the ExecuteReader() from the command object in a using block so that it automatically closes the reader and the connection respectively once it has done reading like so:
using(mySQLDataReader = mySQLCommand.ExecuteReader())
{
//Logic performed
}
Just before the closing brace of the while-loop, I tried checking if there are any more rows left/returned from the sql command and breaking out the loop once that condition is satisfied:
if(mySQLDataReader.HasRows == false) //No more rows left to read
{
break; //break out of loop
}
Both attempts were unsuccessful. How can I get around this?
It must be one of the following 3 things:
You're using Read() OUTSIDE the using block. Remember that using block will implicitly call Close and Dispose on your reader. Thus any Read() calls must be placed inside the using block.
The body of your using block is explicitly closing the reader. This seems improbable.
Apparently you have declared your mySQLDataReader at a higher level. It could be that some other (async) code is closing the reader. This also is unlikely. You shouldn't, in most cases, define a DataReader at global level.
Edit
Reading the full code block that you have posted now, I'd suggest a few changes. Can you run the following and tell us if it runs:
using (var mySQLConnection = new SqlConnection(myConnectionString))
{
mySQLCommand = new SqlCommand("SELECT TOP 1 * FROM Table ORDER BY Id DESC", mySQLConnection, mySQLConnection);
mySQLCommand.Connection.Open();
using(mySQLDataReader = mySQLCommand.ExecuteReader())
{
while (mySQLDataReader.Read())
{
//Perform Logic : If the last record being returned meets some condition then call the below method
MethodCalled();
}
}
}
If this version runs fine, we can then dig the problem better.
If there is no data to iterate, while loop will not execute at all. Do you need to check for HasRows as such? Also, you should use CommandBehavior.CloseConnection when you are creating data reader. This will make sure that underlying connection is closed once you have read through it.
Should if call SqlDataReader.HasRows if I am calling SqlReader.Read
SQLDataReader Source Code
using (SqlConnection mySQLConnection = new SqlConnection(myConnectionString))
{
using (SqlCommand mySQLCommand = new SqlCommand("SELECT TOP 1 * FROM Table ORDER BY Id DESC", mySQLConnection))
{
mySQLConnection.Open();
SqlDataReader mySQLDataReader = mySQLCommand.ExecuteReader(CommandBehavior.CloseConnection);
while (mySQLDataReader.Read())
{
//Code logic here
}
// this call to mySQLDataReader.Close(); will close the underlying connection
mySQLDataReader.Close();
}
MessageBox.Show("Connection state: " + mySQLConnection.State);
}
I am attempting to get the information of user whenever user logged in to the website, it success when I used a DataSet, but if I want to use the SqlDataReader, the error says: Invalid attempt to read when reader is closed. I have search why is it like that and I have found an article says that
SqlDataReader requires connection remains open in order to get the
data from the server, while DataSet does not need requires
connection remains open.
My question is: I want to know how can I use SqlDataReader as well? So that I don't have to depends on DataSet all the times when I want to get the data from the database.
My problem is occurs when I am trying to change the structure of reading the data function using SqlDataReader, so that it can be re-usable anytime.
Here is the code:
DatabaseManager class:
public SqlDataReader GetInformationDataReader(string procName, SqlParameter[] parameters)
{
SqlDataReader reader = null;
using (SqlConnection conn = new SqlConnection(connectionString))
{
conn.Open();
using (SqlCommand cmd = new SqlCommand(procName, conn))
{
cmd.CommandType = CommandType.StoredProcedure;
if (parameters != null)
{
foreach(SqlParameter parameter in parameters)
{
cmd.Parameters.Add(parameter);
}
}
reader = cmd.ExecuteReader();
}
}
return reader;
}
Web Manager class:
public ModelContexts.InformationContext GetInformation(string username)
{
SqlDataReader reader = null;
ModelContexts.InformationContext context = new ModelContexts.InformationContext();
SqlParameter[] parameters =
{
new SqlParameter("#Username", SqlDbType.NVarChar, 50)
};
parameters[0].Value = username;
try
{
reader = DatabaseManager.Instance.GetInformationDataReader("GetInformation", parameters);
while(reader.Read())
{
context.FirstName = reader["FirstName"].ToString();
context.LastName = reader["LastName"].ToString();
context.Email = reader["Email"].ToString();
}
}
catch(Exception ex)
{
throw new ArgumentException(ex.Message);
}
return context;
}
Controller:
public ActionResult MainMenu(ModelContexts.InformationContext context, string firstName, string lastName, string username, string email)
{
context = WebManager.Instance.GetInformation(User.Identity.Name);
firstName = context.FirstName;
lastName = context.LastName;
username = User.Identity.Name;
email = context.Email;
return View(context);
}
Model contains string return value with getter and setter (FirstName, LastName and Email).
View contains the html label and encode for FirstName, LastName and Email from the Model.
Appreciate your answer.
Thanks.
Here is an approach you can use to keep the code pretty clean that allows you to read from the SqlDataReader while the connection is still open. It takes advantage of passing delegates. Hopefully the code is understandable. You can adjust it to fit your specific needs, but hopefully it illustrates another option at your disposal.
public void GetInformationDataReader(string procName, SqlParameter[] parameters, Action<SqlDataReader> processRow)
{
SqlDataReader reader = null;
using (SqlConnection conn = new SqlConnection(connectionString))
{
conn.Open();
using (SqlCommand cmd = new SqlCommand(procName, conn))
{
cmd.CommandType = CommandType.StoredProcedure;
if (parameters != null)
{
foreach(SqlParameter parameter in parameters)
{
cmd.Parameters.Add(parameter);
}
}
using (SqlDataReader dataReader = cmd.ExecuteReader())
{
while (dataReader.Read())
{
// call delegate here.
processRow(dataReader);
}
}
}
}
return reader;
}
public ModelContexts.InformationContext GetInformation(string username)
{
SqlDataReader reader = null;
ModelContexts.InformationContext context = new ModelContexts.InformationContext();
SqlParameter[] parameters =
{
new SqlParameter("#Username", SqlDbType.NVarChar, 50)
};
parameters[0].Value = username;
try
{
// Instead of returning a reader, pass in a delegate that will perform the work
// on the data reader at the right time, and while the connection is still open.
DatabaseManager.Instance.GetInformationDataReader(
"GetInformation",
parameters,
reader => {
context.FirstName = reader["FirstName"].ToString();
context.LastName = reader["LastName"].ToString();
context.Email = reader["Email"].ToString();
});
}
catch(Exception ex)
{
throw new ArgumentException(ex.Message);
}
return context;
}
Brief explanation:
You'll notice that the overall structure of the code is very similar to what you already have. The only changes are:
Instead of returning a SqlDataReader, the GetInformationDataReader() method accepts an Action<SqlDataReader> delegate.
Within the GetInformationDataReader() method, the delegate is invoked at the correct time, while the connection is still open.
The call to GetInformationDataReader() is modified to pass in a block of code as a delegate.
This sort of pattern can be useful for exactly these cases. It makes the code reusable, it keeps it pretty clean and separate, and it doesn't prevent you from benefiting from the using construct to avoid resource/connection leaks.
You have wrapped your SqlConnection object in a using clause, therefore at the end of it SqlConnect.Dispose is called, closing the connection. Whatever caller is consuming your SqlDataReader no longer has the open connection, therefore you're getting your error.
while DataSet does not need requires connection remains open.
That is not entirely correct. DataSet is just an object that is typically filled when called by SqlDataAdapter (the Fill() method of that class). The SqlDataAdapter handles the opening and closing of the SqlConnection, which is most likely why that comment states that. But it's a different class that handles that, not the DataSet itself. Think of the DataSet as just the object that holds the result set of the SqlCommand.
To answer your comment...
So, shouldn't I use using keyword for this matter? In all of the Sql keyword?
I wouldn't take that approach either. You could have a connection leak bug quite easily with that model, and running out of pooled connections could be a not-so-fun thing to troubleshoot.
Typically it's best to consume your data and then close/dispose your connection. There's a saying, "open late, close early". That's typically how you'd want to approach this. I wouldn't try to pass a SqlDataReader object between class methods for this very issue that you're dealing with. The workaround (leaving the connection open) is very error prone.
Another though process, going back to something we mentioned, don't use the SqlDataReader. You have no benefit to cyclically loop through reading each row. Depending on your result set, just fill a DataSet (or usually more appropriate, a DataTable) and return either that Data[Set | Table] or, even better, an object that is more representative of the data it pertains to.
I'm rewriting part of an old webforms application. I want to make a central function that will do the select queries. I will feed it the SQL query and parameters and it will do the rest.
So far I have this:
MySqlDataReader DoRead(string query, params MySqlParameter[] pms)
{
MySqlCommand myCommand;
if (!myConnection)
myConnection = new MySqlConnection(sCon);
if (myConnection.State != ConnectionState.Open)
{
myConnection.Close();
myConnection.Open();
}
myCommand = myConnection.CreateCommand();
myCommand.CommandText = query;
foreach (MySqlParameter p in pms)
{
myCommand.Parameters.Add(p);
}
return myReader = myCommand.ExecuteReader();
}
My question is, can I save the results of myReader to a variable and return that same variable instead of myReader just so I could close the connection and the reader immediately in the function instead in the main code?
Of course. And that's exactly what you should be doing, for exactly the reason you state:
so I could close the connection and the reader immediately in the function
The problem you're facing is the use of the reader in the first place, which itself is coupled to the open data stream. So if consuming code is expecting a reader, it's going to have to change in order to fix this.
How does the application later use this reader? If you're trying to make a generic function, then the result needs to be pretty generic too. So if consuming code is all doing custom things with the reader then perhaps you can instead return a DataSet and consuming code can do custom things with that instead. Something like this:
var result = new DataSet();
using(var myConnection = new MySqlConnection(sCon))
{
myConnection.Open();
var myCommand = myConnection.CreateCommand();
myCommand.CommandText = query;
foreach (var p in pms)
myCommand.Parameters.Add(p);
var myAdapter = new MySqlDataAdapter(myCommand);
myAdapter.Fill(result);
}
return result;
(Note that I made another change here as well. The connection object should also be local to the scope of the method. Shared connection objects open up a world of potential problems, one of which you've undoubtedly tried to fix with that conditional to close/open the connection state. Just avoid that world of problems entirely and dispose of connections once you're done with them.)
I'm using a helper method like this:
private OdbcCommand GetCommand(string sql)
{
string conString = "blah";
var con = new OdbcConnection(conString);
var cmd = new OdbcCommand(sql, con);
return cmd;
}
Then i use it like this:
using (var cmd = GetCommand("select * from myTable")
{
cmd.connection.open();
using(var reader = cmd.ExecuteReader())
{
}
}
Here is a second example:
public static OdbcDataReader GetReader(string conString,string sql)
{
var cmd = GetCommand(conString, sql);
cmd.Connection.Open();
return cmd.ExecuteReader();
}
used like this:
using(var reader = GetReader("blah","select * from blah")
{
}
In these two cases, am i disposing of the connection and cmd objects? I think connection is not being disposed in the first, and neither connection nor cmd in the second, is that right?
Do i need to do i the long way to ensure correct disposal, or is there a shorter approach?
using (var con ...)
using (var cmd)
using (var reader)
In short, if it has Dispose, then you should call it. You can't assume that another object will dispose of an object you pass it for you.
Disposing the command object, will not dispose the connection object, because you can use the connection again after you are done with the command. Disposing of each is the best approach.
For both examples where you wrap your Reader in a using block, you will close the connection with the existing code IF you use the override that accepts a CommandBehavior and set it to 'CloseConnection'
using(var reader = cmd.ExecuteReader(CommandBehavior.CloseConnection)){}
see http://msdn.microsoft.com/en-us/library/s9bz2k02.aspx
Microsoft knows that the connection must remain open while the reader is consumed, and therefore created the option to close the connection when the Reader is closed.
You are correct that the command is not being Disposed in the second example.