using on SQLDataReader - c#

I know I asked a related question earlier. I just had another thought.
using (SqlConnection conn = new SqlConnection('blah blah'))
{
using(SqlCommand cmd = new SqlCommand(sqlStatement, conn))
{
conn.open();
// *** do I need to put this in using as well? ***
SqlDataReader dr = cmd.ExecuteReader()
{
While(dr.Read())
{
//read here
}
}
}
}
The argument is that: Since the SqlDataReader dr object is NOT A NEW OBJECT LIKE THE connection or command objects, its simply a reference pointing to the cmd.ExecuteReader() method, do I need to put the reader inside a using. (Now based on my previous post, it is my understanding that any object that uses IDisposable needs to be put in a using, and SQLDataReader inherits from IDisposable, so I need to put it. Am I correct in my judgement?) I am just confused since its not a new object, would it cause any problems in disposing an object that simply is a reference pointer to the command?
Many thanks

I think you are mistaken. The dr is a reference to the object returned by cmd.ExecuteReader, which is going to be a new object. In your example, nothing will dispose dr, so yes it needs to be in a using, or manually disposed.
Your judgement about IDisposable implementors needing to be in a using is not correct. They will function fine outside. A using statement is just syntactic sugar for a try ... finally. Things implementing IDisposable should have Dispose called, because they are signalling that they need to dispose certain state in a deterministic way.
Note that if you do not call Dispose, its not always a problem. Some objects also implement the finalizer, which will be triggered by the garbage collector. If they do not implement the finalizer, they might leave unmanaged memory unreclaimed. This will remain unreclaimed until your application closes. All managed memory is eventually reclaimed, unless it is not elligible for garbage collection.
Re-written:
using (SqlConnection conn = new SqlConnection('blah blah'))
using(SqlCommand cmd = new SqlCommand(sqlStatement, conn))
{
conn.open();
using (SqlDataReader dr = cmd.ExecuteReader())
{
while (dr.Read())
{
//read here
}
}
}

You should wrap the data reader in a using statement as the ExecuteReader method is creating a new data reader instance that should also be disposed of.

Related

Using statement variations

I found that my codebase contains various data access code where I have used using statements in two different ways. Which is the better way if any and are these two methods different? Any problems that could arise from not instantiating the SqlConnection and SqlCommand in the using statement? Ignore the obvious inconsistency problem.
Method 1:
public int SampleScalar(string query, CommandType queryType, SqlParameter[] parameters)
{
int returnValue = 0;
SqlConnection objConn = new SqlConnection(ConnString);
SqlCommand objCmd = new SqlCommand(query, objConn);
objCmd.CommandType = queryType;
if (parameters.Length > 0)
objCmd.Parameters.AddRange(parameters);
using (objConn)
{
using (objCmd)
{
objConn.Open();
try
{
returnValue = (int)objCmd.ExecuteScalar();
}
catch (SqlException e)
{
Errors.handleSqlException(e, objCmd);
throw;
}
}
}
return returnValue;
}
Method 2:
public int SampleScalar2(string query, CommandType queryType, SqlParameter[] parameters)
{
int returnValue = 0;
using (SqlConnection objConn = new SqlConnection(ConnString))
{
using (SqlCommand objCmd = new SqlCommand(query, objConn))
{
objConn.Open();
objCmd.CommandType = queryType;
if (parameters.Length > 0)
objCmd.Parameters.AddRange(parameters);
try
{
returnValue = (int)objCmd.ExecuteScalar();
}
catch (SqlException e)
{
Errors.handleSqlException(e, objCmd);
throw;
}
}
}
return returnValue;
}
In the first snippet, if there are any exceptions that occur after the IDisposable object is created and before the start of the using, then it won't be properly disposed. With the second implementation, there is no such gap that could result in unreleased resources.
Another problem that can occur with the first approach is that you could use an object after it has been disposed, which is not likely to end well.
It's possible that you are ensuring no exception could possibly occur, and maybe you're not. In general, I would never use the first method simply because I don't trust myself (or anyone else) to never ever ever make a mistake in that unprotected space. If nothing else, I'll need to spend time and effort looking very closely to be sure that nothing can ever go wrong. You don't really gain anything from using that less-safe method either.
I always go with the second method. It's easier to read and understand what objects are being disposed of at the end of a given block. It will also prevent you from using an object that has been disposed.
If you not use using you don't dispose your objets no managed, and GC no dispose
GC dispose only objects managed and sql connection is not managed so you must dispose, using use dispose in the end
Second one is better. Please read http://msdn.microsoft.com/en-us/library/yh598w02.aspx last remark.
In first object stays in scope after it's disposal. Using it then is not good practice.
Per MSDN
You can instantiate the resource object and then pass the variable to the using statement, but this is not a best practice. In this case, the object remains in scope after control leaves the using block even though it will probably no longer have access to its unmanaged resources. In other words, it will no longer be fully initialized. If you try to use the object outside the using block, you risk causing an exception to be thrown. For this reason, it is generally better to instantiate the object in the using statement and limit its scope to the using block.

SQL server and .NET memory constraints, allocations, and garbage collection

I am running .NET 3.5 (C#) and SQL Server 2005 (for our clients). The code that we run does some regression math and is a little complicated. I get the following error when I run multiple pages on our site:
.NET Framework execution was aborted by escalation policy because of out of memory.
System.InvalidOperationException: There is already an open DataReader associated with this Command which must be closed first.
System.InvalidOperationException:
I'm trying to figure out what is the root cause of this: is it a database issue or my C## code? or is it concurrency with locks when running queries? or somethin else?
The code is erroring here:
erver.ScriptTimeout = 300;
string returnCode = string.Empty;
using (SqlConnection connection = new SqlConnection(ConfigurationManager.ConnectionStrings["MainDll"].ToString())) {
connection.Open();
using (SqlCommand command = new SqlCommand(sql.ToString(), connection)) {
command.CommandType = CommandType.Text;
command.CommandTimeout = 300;
returnCode = (string)command.ExecuteScalar();
//Dispose();
}
//Dispose();
}
Our contractor wrote a bunch of code to help with SQL connections in an App_Code/sqlHelper.s file. Some of them are like this:
public static SqlDataReader GetDataReader(string sql, string connectionString, int connectionTime) {
lock (_lock) {
SqlConnection connection = null;
try {
connection = GetConnection(connectionString);
//connection.Open();
using (SqlCommand cmd = new SqlCommand(sql, connection)) {
cmd.CommandTimeout = connectionTime;
WriteDebugInfo("GetDataReader", sql);
return cmd.ExecuteReader(CommandBehavior.CloseConnection);
}
}
catch (Exception e) {
if (connection != null)
connection.Dispose();
throw new DataException(sql, connectionString, e);
}
}
}
Should there be some deallocation of memory somewhere?
The problem is that, for some reason, your DataReader isn't being closed. An exception? The method user didn't remember to close the DataReader?
A function that returns a DataReader to be used outside its body leaves the responsibility of closing it to outer code, so there's no guarantee that the Reader will be closed. If you don't close the reader, you cannot reuse the connection in which it was opened.
So returning a DataReader from a function is a very bad idea!
You can see a whole discussion on this subject here.
Look for the usages of this function (GetDataReader), and check if there's guarantee that the reader is getting closed. And, most importantly, that there is no possibility that this code re-enters and uses the same collection to open a new DataReader before the first is closed. (Don't be mislead by the CommandBehavior.CloseConnection. This only takes care of closing the connection when the DataReader is closed... only if you don't fail to close it)
This is because your data reader is already filled in. Its always a better way to release the data reader, command , data set , data table and close the connection in finally block.
Make use of Dispose() and Close() methods .

basic about "using" construct

If I use "using" construct, I know that the object gets automatically disposed. What happens if a statement inside an "using" construct raises an exception. Is the "using" object still disposed? If so, when?
A using block is converted - by the compiler - to this:
DisposableType yourObj = new DisposableType();
try
{
//contents of using block
}
finally
{
((IDisposable)yourObj).Dispose();
}
By putting the Dispose() call in the finally block, it ensures Dispose is always called - unless of course the exception occurs at the instantiation site, since that happens outside the try.
It is important to remember that using is not a special kind of operator or construct - it's just something the compiler replaces with something else that's slightly more obtuse.
This article explains it nicely.
Internally, this bad boy generates a try / finally around the object being allocated and calls Dispose() for you. It saves you the hassle of manually creating the try / finally block and calling Dispose().
Actually Using block is Equivalent to try - finally block, Which ensures that finally will always execute e.g.
using (SqlConnection con = new SqlConnection(ConnectionString))
{
using (SqlCommand cmd = new SqlCommand("Command", con))
{
con.Open();
cmd.ExecuteNonQuery();
}
}
Equals to
SqlConnection con = null;
SqlCommand cmd = null;
try
{
con = new SqlConnection(ConnectionString);
cmd = new SqlCommand("Command", con);
con.Open();
cmd.ExecuteNonQuery();
}
finally
{
if (null != cmd);
cmd.Dispose();
if (null != con)
con.Dispose();
}

Is there a way to use the Task Parallel Library(TPL) with SQLDataReader?

I like the simplicity of the Parallel.For and Parallel.ForEach extension methods in the TPL. I was wondering if there was a way to take advantage of something similar or even with the slightly more advanced Tasks.
Below is a typical usage for the SqlDataReader, and I was wondering if it was possible and if so how to replace the while loop below with something in the TPL. Because the reader can't provide a fixed number of iterations the For extension method is not possible which leaves dealing with Tasks I would gather. I was hoping someone may have tackled this already and worked out some do's and don''s with ADO.net.
using (SqlConnection conn = new SqlConnection("myConnString"))
using (SqlCommand comm = new SqlCommand("myQuery", conn))
{
conn.Open();
SqlDataReader reader = comm.ExecuteReader();
if (reader.HasRows)
{
while (reader.Read())
{
// Do something with Reader
}
}
}
You're going to have difficulty replacing that while loop directly. SqlDataReader is not
a thread safe class, so you cannot use it directly from multiple threads.
That being said, you could potentially process the data you read using the TPL. There are a few options, here. The easiest might be to make your own IEnumerable<T> implementation that works on the reader, and returns a class or struct containing your data. You could then use PLINQ or a Parallel.ForEach statement to process your data in parallel:
public IEnumerable<MyDataClass> ReadData()
{
using (SqlConnection conn = new SqlConnection("myConnString"))
using (SqlCommand comm = new SqlCommand("myQuery", conn))
{
conn.Open();
SqlDataReader reader = comm.ExecuteReader();
if (reader.HasRows)
{
while (reader.Read())
{
yield return new MyDataClass(... data from reader ...);
}
}
}
}
Once you have that method, you can process this directly, via PLINQ or TPL:
Parallel.ForEach(this.ReadData(), data =>
{
// Use the data here...
});
Or:
this.ReadData().AsParallel().ForAll(data =>
{
// Use the data here...
});
You're almost there. Wrap the code you posted in a function with this signature:
IEnumerable<IDataRecord> MyQuery()
and then replace your // Do something with Reader code with this:
yield return reader;
Now you have something that works in a single thread. Unfortunately, as you read through the query results it's return a reference to the same object each time, and the object just mutates itself for each iteration. This means that if you try to run it in parallel you'll get some really odd results as parallel reads mutate the object used in different threads. You need code to take a copy of the record to send to your parallel loop.
At this point, though, what I like to do is skip the extra copy of the record and go straight to a strongly-typed class. More than that, I like to use a generic method to do it:
IEnumerable<T> GetData<T>(Func<IDataRecord, T> factory, string sql, Action<SqlParameterCollection> addParameters)
{
using (var cn = new SqlConnection("My connection string"))
using (var cmd = new SqlCommand(sql, cn))
{
addParameters(cmd.Parameters);
cn.Open();
using (var rdr = cmd.ExecuteReader())
{
while (rdr.Read())
{
yield return factory(rdr);
}
}
}
}
Assuming your factory methods create a copy as expected, this code should be safe to use in a Parallel.ForEach loop. Calling the method would look something like this (assuming a an Employee class with a static factory method named "Create"):
var UnderPaid = GetData<Employee>(Employee.Create,
"SELECT * FROM Employee WHERE AnnualSalary <= #MinSalary",
p => {
p.Add("#MinSalary", SqlDbType.Int).Value = 50000;
});
Parallel.ForEach(UnderPaid, e => e.GiveRaise());
Important Update:
I'm not as confident in this code as I once was. A separate thread could still mutate the reader while another thread is in the process of making it's copy. I could put a lock around that, but I'm also concerned that another thread could call update the reader after the original has itself called Read() but before it begins to make the copy. Therefore, the critical section here consists of the entire while loop... and at this point, you're back to single-threaded again. I expect there is a way to modify this code to work as expected for multi-threaded scenarios, but it will need more study.

Keeping the SqlDataReader open across an n-tiered layer

I have a Database class that abstracts the ExecuteNonQuery() and ExecuteReader() of SqlCommand. Due to wrapping the Sqlconnection and SqlCommand around using blocks, the SqlDataReader gets closed after the CustomExecuteReader() is called, therefore I can't read the SqlReaderResultSet at the business level layer. Code below. Thanks guys for the feedback.
public static SqlDataReader SqlReaderResultSet { get; set; }
public static SqlDataReader CustomExecuteReader(string storedProc)
{
using (var conn = new SqlConnection(ConnectionString))
{
var cmd = new SqlCommand(storedProc, conn) {CommandType = CommandType.StoredProcedure};
try
{
conn.Open();
SqlReaderResultSet = cmd.ExecuteReader();
}
catch (InvalidOperationException)
{
if (conn.State.Equals(ConnectionState.Closed))
conn.Open();
}
finally
{
conn.Close();
}
}
return SqlReaderResultSet;
}
"I can't read the SqlReaderResultSet at the business level layer" - and you shouldn't. Data should be passed using data transfer objects, never through a low level data access structure.
I recommend changing your approach so that the method you describe above iterates the records in the datareader, and creates a list of objects. That list of objects is what should be returned and worked on.
Iterator Blocks can be a way around this. It is legal and generally safe to do the following:
IEnumerable<MyFancyData> ResultSet {
get {
using(DbConnection conn = ...)
using(DbCommand cmd = ...) {
conn.Open();
using(DbDataReader reader = cmd.ExecuteReader()) {
while(reader.Read()) {
yield return new MyFancyData(reader[0], reader[42] ...);
}
}
}
}
}
Each time you enumerate the ResultSet property, the connection will be constructed again - and Disposed of afterwards (foreach and other IEnumerator<> consumers will appropriately call the Dispose() method of the generator, allowing the using block to do its thing).
This approach retains the lazy as-you-need it evaluation of the items from the data reader (which can be relevant when your data set becomes large), which still cleaning abstracting away sql-level details from the public API.

Categories

Resources