I have some legacy code I've inherited and am trying to figure out whether or not this code is causing a connection to stay open to my database. I'm not too familiar with ADO.net but from what I can see this should be causing a problem.
I have a function to get a data reader:
Public Shared Function ExecuteDataReader(ByRef ObjSqlCmd As SqlCommand, ByVal CSTRING As String) As IDataReader
Try
Dim cn As New SqlConnection(CSTRING)
cn.Open()
ObjSqlCmd.Connection = cn
Return ObjSqlCmd.ExecuteReader(CommandBehavior.CloseConnection)
Catch ex As Exception
If ObjSqlCmd.Connection.State = ConnectionState.Open Then ObjSqlCmd.Connection.Close()
Return Nothing
End Try
End Function
And then code using this to read:
Dim cmd As New SqlCommand
'set up command
Dim idr As IDataReader = ExecuteDataReader(cmd, "database")
If idr Is Nothing Then
If cmd.Connection.State = ConnectionState.Open Then cmd.Connection.Close()
Return arrResult
End If
While idr.Read
'read
End While
If cmd.Connection.State = ConnectionState.Open Then cmd.Connection.Close()
I can see it closing the connection at the end (assuming nothing goes wrong and an exception is thrown) but from MSDN they say you should always clsoe the data reader. Does that get closed when the connection is closed?
Also from what I understand, the code
ObjSqlCmd.ExecuteReader(CommandBehavior.CloseConnection)
Will not close the connection unless the data reader is closed. Can someone explain what is happening here and if everything might be closing correctly? I know the best practice is to use "using" and then try/catch/finally to close the reader, the the original developers did not seem to follow these practices.
Yes it will. Any exception that occurs while reading from the data reader will bypass the closing statement. At some point, the garbage collector will kick in and dispose and release the connection back to the connection pool.
Until that point though, the connection can't be used. Worse, any locks acquired while executing the command will remain until the connection is closed.
Using ... using isn't a best practice just because. It's actually a lot simpler and safer to manage commands and connections in this way
Related
ASP.NET WEB PROGRAMMING
How to optimally handle
Open
Close
Dispose
Exception
when retrieving data from a database.
LOOKING TO OPTIMISE
I have always used the following to make a connection, catch any errors, and then correctly dispose of the connection in either event.
VB.NET
Try
con.Open()
//...
con.Close()
Catch ex As Exception
lbl.Text = ex.Message
Finally
If con IsNot Nothing Then
con.Dispose()
End If
End Try
After reading many articles I find people practically throwing up at this code; however, I do not see any other way to accomodate the four steps required efficiently.
The alternative, and I believe more cpu friendly USING statement, seems to be the tool of choice for the correct disposal of a sql connection. But what happens when bad data is retrieved from the database and there is nothing in place to indicate to an end user what went wrong?
QUESTION
Between: Using, try catch, and other.
Which is faster, cleaner, and/or most efficient way to handle a data retrieval statement?
I am happy to hear people's opinions but I am looking for facts.
You can also use the following block of code as template. It combines the Using...End Using and Try...Catch blocks.
Using conn As New SqlConnection(My.Settings.SQLConn)
Try
Conn.open
Catch ex As SqlException
End Try
End Using
There is no need to call conn.Dispose() because using block does that automatically.
Use Entity Framework, it implements Unit Of Work Pattern for You efficiently, and perform your operations within transaction scope
Always use the "Using" process as it will automatically assign and then free up system resources. You are still able to perform the try-catch within this to present errors to the user.
I have seen some code like this (not on consecutive lines, but in the same function scope):
Dim con1 As SqlConnection
con1 = New SqlConnection
'More code here
con1 = New SqlConnection
con1 = Nothing
I believe this is just a bug, but I wanted to check that there is not a form of shadowing going on here that I am unaware of. What happens to the first con1 variable? I assume it is inaccessible as there is no reference to the object.
What's Happening Here
con1 points to two different objects during the lifetime of that function.
The first object, created by the first
con1 = New SqlConnection
is no longer referenced after the second
con1 = New SqlConnection
is executed.
Is this a memory leak?
No. The object that is no longer referenced will eventually be disposed of, then the GC decides to do so. However it is a resource leak. Every time you fail to close a SQL Connection (assuming it was opened and not just allocated), you leave a resource unavailable for reuse. The GC will trigger when memory is low, so you will certainly regain the unreferenced object's memory at the latest when the system is low on memory (you will also regain the DB connection at that point). However, low resources will not trigger GC. You could run completely out of DB connections before the GC ever decides to kick in and release the SqlConnection objects (including the DB connections they were hoarding).
Fixing the Code
Since SqlConnection must be closed to release the connection, the first object will hang around until the GC decides to dispose of it. That is a bad thing, as SQL connections are a resource that should only be held as long as necessary.
Calling Close() the first connection before assigning the new SqlConnection object would improve the situation (also, call Close() on the second instance before leaving variable scope).
However, if you get an Exception somewhere in the code, without proper exception handling, you would still be left with undisposed objects until the GC kicks in. Always, always put exception handling around anything that manages a resource such as this.
The very best way to put exception handling in place for this scenario is with the Using keyword. I have never written a line of VB.Net until now, but here's my attempt at a correct version of your code:
Dim con1 As SqlConnection
Using con1
con1 = New SqlConnection
End Using
'More code here
Using con1
con1 = New SqlConnection
End Using
' NOTE: I believe the following is unnecessary, but was necessary in VB6 and prior
' con1 = Nothing
Aim: Calling a very slow stored procedure (with parameters) asynchronously from code behind of an asp.net web page via single function call, and then forgetting about it.
Notes: I tried using SqlCommand.BeginExecuteNonQuery without calling SqlCommand.EndExecuteNonQuery (see the code below), but the stored procedure didn't run. (I used a tiny stored procedure to update single field on a table for testing but the field was not updated. It gets update when I use SqlCommand.ExecuteNonQuery).
I am not interested in and don't know when the stored procedure will end. (So I can't wait for it to finish to call SqlCommand.EndExecuteNonQuery.)
Situation:
After some research, I found out that sql jobs can be used for this purpose. And for the sp parameters; I can store them in a table and then sp can read them. However I don't know if this is the right approach to my problem (I am new to SQL world). I hope you can comment on usage of an sql job for this purpose, or suggest alternatives. Thanks.
Code:
using (SqlConnection connection = new SqlConnection(
#"Data Source=XXX\MSSQL2008R2;Initial Catalog=YYY;Integrated Security=True"
+ ";Asynchronous Processing=true"))
{
connection.Open();
SqlCommand cmd = new SqlCommand("spTestProcedure", connection);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add(new SqlParameter("#pText", DateTime.Now.ToString()));
cmd.BeginExecuteNonQuery(); //Doesn't work.
//cmd.ExecuteNonQuery(); // This works.
}
I think you should simply execute your sp in a separate thread.
http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx
Use ThreadPool for example to make a sync call on a separate thread.
It will looks something like this...
Extract method:
private void ExecuteCommandSync()
{
using (SqlConnection connection = new SqlConnection(
#"Data Source=XXX\MSSQL2008R2;Initial Catalog=YYY;Integrated Security=True"
+ ";Asynchronous Processing=true"))
{
connection.Open();
SqlCommand cmd = new SqlCommand("spTestProcedure", connection);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add(new SqlParameter("#pText", DateTime.Now.ToString()));
cmd.ExecuteNonQuery();
}
}
Change your code:
ThreadPool.QueueUserWorkItem((x) => { ExecuteCommandSync(); });
It will make a synchronous call on some thread from ThreadPool, once it is done - it will close connection and you are done.
It is not the BEST solution performance-wise, because you will have a thread sleeping while it waits for the Stored Proc, but it is good enough and will do what you want.
I was looking for a way of doing this from the code behind of asp.net, but realized there is no easy way of doing it (I didn't want to think about connection or time out problems). I ended up doing it via a web service.
From asp.net code behind, I call my webs service's function synchronously.
Within the web service's function, I call the stored procedure asynchronously using SqlCommand.BeginexecuteNonQuery(AsyncCallback, Object).
The callback is handled within the web service (for error handling).
Hence my web page keeps working the way I want: Fire the request once, then forget about it.
i've a huge problem.
Take a look to this sample code
private sub FUNCTION1()
dim conn as new mysqlconnection(myconnstring)
conn.open
---- do something
FUNCTION2()
----
conn.close
end sub
private sub FUNCTION2()
dim conn as new mysqlconnection(myconnstring)
....
conn.open
-- do something
conn.close
end sub
Despite i close regulary all my connection, they remains "open" on the mysql server.
I know this because i'm using MySQL Administration tool to check how many connection i open "line by line" of my source code.
In fact i get a lot of time "user has exceeded the 'max_user_connections' resource (current value: 5)"
My hoster permit ONLY 5 connection, but i think that if i write GOOD source code, this cannot be a problem.
So my question is: why that "damn" connections remain open ??
Thank you in advance!
Consider wrapping your MySqlConnection operations in a Using statement...
Any object that you instantiate within that Using ... new statement will be disposed of properly by the compiler. Once that End Using statement appears, the object goes out of scop. Any objects that are declared with in the Using block will need to be disposed of by the developer, as per normal.
Using conn As New MySqlConnection(myConnString)
Using cmd As New MySqlCommand("SELECT * FROM Employees", conn)
conn.Open()
Using rdr As MySqlDataReader = cmd.ExecuteReader()
While rdr.Read()
Console.WriteLine(rdr(0))
End While
End Using
End Using
End Using
In this case, you don't HAVE to encase your Command and Reader in their own Using, but it allows you to take advantage of the fact that they all implement IDisposable.
Try calling conn.Dispose() and then setting it to null.
On another note, are you opening a connection in Function1 and Function2 also as it appears that you're creating 2 connections. If that's the case try to implement a singleton pattern to manage your connections.
I just uploaded my first ASP.NET (as part of my learning of vb.net) and got into awful mess with the connection pooling (funny things happen when there are more than 1 user of your web site) all sorted now by better use of the try catch statements (well the idea was to learn) BUT I was wondering if this is the best / final method, now if the try fails, then a LOT of the detail on the page isn't placed/updated, so if you are doing some database work and the try fails, do you reload the page ... redirect to self and hope it work the next time ... or just inform the user there was a error and they should try again ?
Thanks
You should defintely use 'using' statements for all objects that implement IDisposable (such as connections and readers). A 'using' statement gets translated into a try-finally block under the covers, and ensures that Dispose() is called even if an error occurs.
Here is a code snippet example:
using (SqlConnection conn = new SqlConnection(this.connectionString))
{
using (SqlCommand cmd = conn.CreateCommand())
{
cmd.CommandText = "LoadFromRepository";
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#ID", fileID);
conn.Open();
using (SqlDataReader rdr =
cmd.ExecuteReader(CommandBehavior.CloseConnection))
{
while (rdr.Read())
{
// do something with read data
}
}
}
}
If an unexpected error occurs, redirect the user to an error page as it probably will happen the next time around too.
Have you looked into "Using" statements for DB connections and readers?
I would never automatically redirect and hope that it will work the next time (you might get in a infinite loop).
Inform the user and optionally a link to try it again.
You even might want to analyze your exception to see if another try will help. Some exceptions are really bugs, and another try will not help.