I have an object that I created that handles SQL queries. The function that actually handles the query is:
Function Execute_Command(ByVal strSQLCommand As String)
Dim sqlReturnInfo As SqlDataReader
Dim sqlQuery As New SqlCommand
sqlQuery = sqlMainDBCon.CreateCommand()
sqlQuery.CommandText = strSQLCommand
sqlReturnInfo = sqlQuery.ExecuteReader
Return sqlReturnInfo
End Function
When I execute the code it works fine the first time, but the 2nd time I get an error saying that sqlReturnInfo is not closed.
I fixed the problem by putting sqlReturnInfo = Nothing at the top after the declaration.
My understanding though, is that once this function finishes, the sqlReturnInfo should go out of scope and be destroyed automatically, and then once the function runs again the sqlReturnInfo is recreated as a new object, therefore sqlReturnInfo = nothing should not be nescessary/
I have seen this type of issue in one or two other areas in my code as well, out of scope variables persisting...
Am I doing something wrong, or is my understanding of variable scope incorrect?
Thanks
-RW
Garbage Collection - not all objects follow that rule(COM objects, Streams, graphics objects, Fonts, Bitmaps(the worst), etc...), dispose when done no matter what method you use(Sub/Function). Using/End Using blocks are the best - it takes care of that for you. My rule of thumb, if you see a Dispose method on a class object - Use It! The reason they have them is the engineer knew it would have problems getting collected so they made a method to assist with it.
Your object has the following statement on it:
You must explicitly call the Close method when you are through using the SqlDataReader to use the associated SqlConnection for any other purpose.
You need to use the .Close() function when you are done:
sqlReturnInfo.Close()
See: http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqldatareader.close%28v=vs.110%29.aspx
Related
I need to run a number of queries as part of a transaction.
There's a method in my project, creatively named RunQuery(), that concentrates the database queries, so I'll be using it. It has four parameters:
a string indicating which database to run the query on,
a string containing the query,
a table name for when the result needs to be a data set,
an Enum that indicates what kind of query/results is wanted: Select query returning a data reader, Select query returning a data set, Insert/Update/Delete query.
(If you need more detail, I'll supply it gladly. I'll keep this short-ish for now.)
The point is that when you want to run a query, you don't have to worry about the connection or command objects, you just write your query and call RunQuery(). Fine.
Now, I've added a new ByRef parameter to RunQuery(), an IDbTransaction to track the transaction. It needs to be handled at least partially outside RunQuery(), if only to commit after the very last call, but on the first call it's set at Nothing.
I've also added an item to the Enum so RunQuery() knows it has to deal with a transaction.
My code looks like this:
Dim z_lisQuery As New List(Of String)
' [...] Filling z_lisQuery with queries
Dim z_dtrTransaction As IDbTransaction = Nothing
Dim z_blnExecutionOk As Boolean = True
For Each z_strQuery As String In z_lisQuery
z_blnExecutionOk = z_blnExecutionOk And RunQuery(p_strDbId,
z_strQuery,
"",
z_dtrTransaction,
QueryAction.ExecutionTransaction)
If Not z_blnExecutionOk Then
Exit For
End If
Next
If z_blnExecutionOk Then
z_dtrTransaction.Commit()
End If
z_dtrTransaction?.Connection.Dispose()
z_dtrTransaction?.Dispose()
(A Rollback() is executed within RunQuery() if something goes wrong.)
When I run that and no errors happen, everything goes fine until the penultimate line:
z_dtrTransaction?.Connection.Dispose() throws an exception because z_dtrTransaction.Connection is Nothing.
The obvious workaround is to use z_dtrTransaction?.Connection?.Dispose(), so that's not what I'm puzzled about.
What I'm puzzled about is that z_dtrTransaction.Connection is still a functional OleDbConnection object when execution reaches z_dtrTransaction.Commit(). But after z_dtrTransaction.Commit() is executed, z_dtrTransaction.Connection is Nothing.
I can see the point of that, but the language reference doesn't seem to indicate that it should happen that way and I haven't found a reference to that way of doing things.
Is it officially stated somewhere that this behavior is the normal one? Or is it just with OleDbConnection and not with SqlConnection, for instance?
(Even if it is, obviously I'm keeping z_dtrTransaction?.Connection?.Dispose() because I'm not entirely sure what happens in case of a rollback.)
Hi friends of stackoverflow,
I'm writing a question here because i'm having problems to detect why sometimes a read to a field of a datareader returns a invalid cast exception.
I will give all information possible to understand my situation. I'm working with ASP.NET 3.5
I have a Module that have a function that returns a IDataReader and receives a sql query. something like this
function get_dr(query as String) as IDataReader
dim connection = new SqlClient.SqlConnection("connection string")
connection.open()
dim command = connection.createCommand
command.commandText = query
dim reader = command.executeReader(CommandBehavior.CloseConnection)
return reader
end function
I have a Class with a Shared function that recovers a new dataReader and returns a date. something like this:
public shared function getDate() as Date
using dr = get_dr("SELECT dbo.getDate()")
if dr.read() and dr(0) isnot dbnull.value then
return dr.GetDateTime(0)
end if
end using
end function
when in another code i call the getDate() function, it gives me a call stack like this.
System.InvalidCastException: Specified cast is not valid.
at System.Data.SqlClient.SqlBuffer.get_DateTime()
at System.Data.SqlClient.SqlDataReader.GetDateTime(Int32 i)
Why sometimes i'm getting this error? i was thinking this is because that a lot of users is calling this function in conjunction with another functions of my application (those functions eventually uses get_dr too), mixing the data of the dataReader on another executions, but i need to know if im doing something wrong or maybe to do something better.
Notes:
dbo.getDate is a sql function that ALWAYS returns a date.
don't worry about bad writing code, those are only examples but they have the necessary to understand the scenario.
sorry for my bad english
Really thanks in advance.
One possible reason - you declare connection inside of the function that returns DataReader. When you're out of the function that connection goes out of scope. That means that at some unpredictable point (depends on memory usage etc.) Garbage Collector will collect it. If you try to use the DataReader at that point - all bets are off.
One way to solve it is to declare connection outside of function get_dr and pass it there as a parameter. But also seeing that you're returning a single value and if you don't plan to use the reader for multiple values I suggest using ExecuteScalar instead - it will save you a lot of headaches.
Please see the code below:
Public Function ExecuteDynamicQuery(ByVal strSQL As String, ByVal list As List(Of clsType), ByVal tyType As clsType) As List(Of clsType) Implements IGenie.ExecuteDynamicQuery
Dim objParameterValues As New clsParameterValues
Dim iConnectionBLL As iConnectionBLL
Dim objCon As DbConnection
Dim objDR As DbDataReader
Dim paramValues() As DbParameter
objParameterValues = New clsParameterValues
iConnectionBLL = New clsConnectionBLL()
objCon = iConnectionBLL.getDatabaseTypeByDescription("Genie2")
Using objCon
paramValues = objParameterValues.getParameterValues
objDR = clsDatabaseHelper.ExecuteReader(objCon, CommandType.Text, strSQL, paramValues)
Do While objDR.Read
Dim tyType2 As clsType = tyType
tyType.PopulateDataReader(objDR)
list.Add(tyType2)
Loop
objDR.Close()
Return list
End Using
End Function
An SQL statement is passed to the function along with clsType (the base type). A list of types is returned e.g. a list of Persons. For example, in this case strSQL = "SELECT * FROM Persons". A list of 500 persons is returned but they are all the same person (the last person added to the list). I realise this is because the list is referncing the same object for each entry. How do I change this?
This is a situation where making the method generic would be useful. For instance:
Public Function MyGenericMethod(Of T As New)() As List(Of T)
Dim results As New List(Of T)()
For i As Integer = 0 To 9
Dim item As New T()
' Populate item ...
results.Add(item)
Next
Return results
End Function
For what it's worth, though, I see people trying do this kind of thing often, and it never sits well with me. I'm always the first one in line to suggest that common code should be encapsulated and not duplicated all over the place, but, I've never been convinced that creating some sort of data access layer that encapsulates the calls to ADO, but doesn't also encapsulate the SQL, is a good idea.
Consider for a moment that ADO, is in-and-of-itself an encapsulation of that part of the data-access layer. Sure, it can take a few more lines of code than you might like to execute a simple SQL command, but that extra complexity is there for a reason. It's necessary in order to support all of the features of the data source. If you try to simplify it, inevitably, you will one day need to use some other feature of the data source, but it won't be supported by your simplified interface. In my opinion, each data access method should use all of the necessary ADO objects directly rather than trying to some how create some common methods to do that. Yes, that does mean that many of your data access methods will be very similar in structure, but I think you'll be happier in the long run.
I've reduced your original code. The following sample is functionally equivalent to what you posted. Without knowing more about your types, it will hard to give you anything more than this, but maybe the reduction will make the code clear enough for you to spot a solution:
Public Function ExecuteDynamicQuery(ByVal sql As String, ByVal list As List(Of clsType), ByVal type As clsType) As List(Of clsType) Implements IGenie.ExecuteDynamicQuery
Dim paramValues() As DbParameter = New clsParameterValues().getParameterValues()
Using conn As DbConnection = iConnectionBLL.getDatabaseTypeByDescription("Genie2"), _
rdr As DbDataReader = clsDatabaseHelper.ExecuteReader(conn, CommandType.Text, sql, paramValues)
While rdr.Read()
type.PopulateDataReader(rdr)
list.Add(type)
End While
Return list
End Using
End Function
There are a few additional bits of advice I can give you:
You must have some way to accept parameter information for your query that is separate from the query itself. The ExecuteReader() method that you call supports this, but you only ever pass it an empty array. Fix this, or you will get hacked.
A implementation that uses Generics (as posted in another answer) would be much simpler and cleaner. The Genie interface you're relying doesn't seem to be adding much value. You'll likely do better starting over with a system that understands generics.
The problem of re-using the same object over and over can be fixed by creating a new object inside the loop. As written, the only way to do that is with a New clsType (and it seems you may have Option Strict Off, such that this could blow up on you at run time), through some messy reflection code, a switch to using generics as suggested in #2, or a by accepting a Func(Of clsType) delegate that can build the new object for you.
I'm wondering if this is a good way of doing data access, in terms of all the database objects being properly closed and disposed? For example:
Using conn As New SqlConnection(MyConnectionString)
Using cmd As New SqlCommand("some SQL here", conn)
... add parameters ...
conn.Open()
Using dr As SqlDataReader = cmd.ExecuteReader()
While dr.Read()
... do stuff ...
Wend
End Using
End Using
End Using
Is nesting Using like acceptable practice? If I exit the method at some point within the Read() loop, will the use of Using like this ensure all objects are cleaned up properly regardless?
Using guarantees orderly disposal in an implicit try/finally block.
' THE FOLLOWING TRY CONSTRUCTION IS EQUIVALENT TO THE USING BLOCK
Dim resource As New resourceType
Try
' Insert code to work with resource.
Catch ex As Exception
' Insert code to process exception.
Finally
' Insert code to do additional processing before disposing of resource.
resource.Dispose()
End Try
Nested usings work in a similar fashion. If you exit a block of code, it will execute the finally block, and properly dispose your objects.
http://msdn.microsoft.com/en-US/library/htd05whh(v=VS.80).aspx
To add, the Using block will "behind the scenes" add a Try Finally statement. In the finally statement it will call IDisposable.Dispose on the object. In other words, no matter what you do or what happens, the object will get disposed.
Yes, this is ok. The Dispose method of IDisposable objects is always called.
PS: In this case, the Dispose method also contains the Close method.
You are writing VB.Net, so this only partially applies, but for the C# folks out there using StyleCop, multiple using statements like this will cause exception 2202 in StyleCop.
There is a lengthy section at the msdn link of dissenting comments as to the usefulness of this rule in stylecop.
I will not make a judgment call as to whether you should heed StyleCops warnings for C# in your VB.Net code.
Running into problems creating objects through reflection and then running them on multiple threads.
I just cannot seem to figure out what I need to here:
For Each WorkerNode As XmlNode In oRunSettings.GetWorkerValues
Dim sWorkerName = WorkerNode.Attributes(SETTING_NAME_ID).Value
Dim oWorkerType As Type = Type.GetType(String.Format("Worker.{0}", sWorkerName))
Dim oWorker As Object = Activator.CreateInstance(oWorkerType)
Dim tWorker As Thread = New Thread(AddressOf oWorker.Process)
tWorker.Start()
Next
It is causing errors at the "AddressOf" because Object does not have a method called that. Do I need to do something with an interface?
First of all I want to say that I've never wrote code in VB so I can be completely wrong here but I'll try anyway.
It seems that you hold the created instance as Object instead of it's correct type.
Object does not contain method named Process, hence the error.
try casting the object to the correct type.
I hate when people answer their own question, but while waiting for an answer, I realized that I could just cast the object as its base object, and set the reflection from there. That is working now.