This is causing me such grief! Any help would be MUCH appreciated.
Basic summary of the problem:
I have an Intranet site I have developed that uses a SQL Server backend. It uses SqlConnection and SqlDataReader objects to get data form the database.
When more than one person uses the site at the same time, conflicts occur. It seems that the users' processes are attempting to share the SqlDataReaders, which results in problems because one user tries to open the SqlDataReader but it is already open by the other user, and similarly when they try to close a DataReader.
Full details are as follows:
SQL Server 7 backend database
webserver running Windows Windows 2003 server 5.2 SP1
IIS 6.0
.Net version 1.1 1927
Aparent collisions are occuring when more than one person works on the website at the same time.
I am using SQLConnection and SQLDataReader objects for data access from the database, a typical example being:
in Global.asax.vb:
Application("SQLString") = "workstation id={WebserverName};packet size=4096;user id=sa;password={password};data source={SQLServermachine};persist security info=False;initial catalog={DatabaseName}"
in a local class module:
Dim SQLConnection_docstatustable =New SQLConnection(HttpContext.Current.Application("SQLString"))
PublicFunction GetAll()As DocStatuses
'Returns a collection of all the Document Statuses from the database
Dim TheseDocStatusesAsNew DocStatuses
Dim commandAsNew SqlCommand("select * from lkDocStatus where Deleted = 0", SQLConnection_docstatustable)
Dim drAs SqlDataReader
Try
If SQLConnection_docstatustable.State = ConnectionState.ClosedThen SQLConnection_docstatustable.Open()
dr = command.ExecuteReader(CommandBehavior.SingleResult)
While dr.Read()
TheseDocStatuses.Add(PopulateItem(dr))
EndWhile
Finally
IfNot drIsNothingAndAlsoNot dr.IsClosedThen
dr.Close()
EndIf
SQLConnection_docstatustable.Close()
command.Dispose()
EndTry
Return TheseDocStatuses
EndFunction
The above function uses the following function to read each record as it loops through the datareader:
Function PopulateItem(ByVal drAs IDataRecord)As DocStatus
Dim thisDocStatusAsNew DocStatus
thisDocStatus.DocStatusID = Convert.ToInt32(dr("DocStatID"))
IfNot dr("DocStatus")Is DBNull.ValueThen
thisDocStatus.DocStatusString = Convert.ToString(dr("DocStatus"))
EndIf
Return thisDocStatus
EndFunction
This example is a very small one, but all my database queries are done in pretty much the same way. The main difference is that most include a lot more fields.
I have consistently used try/finally blocks, and closed both the SQL Connection and the DataReader in there, so that everything is being tidied up for each specific user.
Depending on precisely what point the two users are at when the problem happens, I get various different .NET error messages, but they all seem to boil down to collisions between the different users, with particular regard to DataReaders.
Some error messages I have received:
Object reference not set to an instance of an object - pointing to adr = command.ExecuteReader(CommandBehavior.SingleResult) statement (I take it to mean the other user's process has just closed this DataReader).
There is already an open DataReader associated with this Connection which must be closed first - pointing to the same place (I take it to mean the other user's process has just opened this DataReader).
ExecuteReader requires an open and available Connection. The connection's current state is closed - pointing to the same place again (I assume the other users's process has just closed the Connection).
Also:
Internal Connection Fatal Error pointing - toSQLConnection_productstable.Close()(I take this also to mean that the other user's process has just closed this Connection).
Timeout expired. The timeout period elapsed prior to completion of the operation or the server is not responding (I am not sure about this one)
Finally, I get one or two errors that sound like queries may not have not returned all the expected records from the database, perhaps because of a DataReader closing too early.
I understand that .NET 1.1 allows only one datareader per connection, but when I tried turning connection pooling off it made no difference. Nor did allowing a high Min Pool size, although when I tried this I could see the many connections in the Windows Perfomance Manager.
I don't really see why I need to check whether the connection object is closed before opening it, or why I need to check that the datareader is not nothing or closed before closing it, but I found I got worse results still if I didn't do these.
I have tried to replicate this problem on my own computer, but it doesn't happen. This is a standalone PC with SQL Server and webserver all on the same machine. The software is the same, except that I have Windows XP Pro 5.1 SP2, and IIS 5.1 The fact this works OK on mine makes me wonder if there might be a .NET setting different on the two, or perhaps the network might cause problems - their webserver is separate from their SQL one. But the errors I am getting do sound like .NET issues that I could fix by changing the program, if only I could work out what to change!
Regards
David Vaughan
Apparently, neither was the case. It's still too early in our testing to tell, but it looks like our actual cuplrits were a handful of procedures that used DataReader objects directly. In each case, the DataReader was either not issued a Close followed by a Dispose, or was not nested inside a try/catch block to do the same on an exception.
So, if I were you, I'd comb through the code once more to see if you have any "bad apples" like this spoiling the bunch. In our case, about 1-2 dozen such procedure calls were to blame, hidden within about 19000 total database calls from managed code over the course of roughly a half hour.|||
misfit815:
Apparently, neither was the case. It's still too early in our testing to tell, but it looks like our actual cuplrits were a handful of procedures that used DataReader objects directly. In each case, the DataReader was either not issued a Close followed by a Dispose, or was not nested inside a try/catch block to do the same on an exception.
I've had the same experience as misfit815. All of my DataReaders are encapsulated using blocks (It's like a Try/Finally block which autodisposes), and I make sure that I don't call other functions that might hit the database inside one of those blocks.
No comments:
Post a Comment