SqlDataReader Item property loses connection - vb.net

I have this large SQL Server query (legacy) over several tables, that I read with a SqlDataReader. It usually has a limited number of rows to handle, but recently I had to use it over a larger number.
After a number of iterations, the SqlDataReader object, on reading an Item() property, throws a SqlException with the code 0x80131904 and the message "Une erreur de niveau transport s'est produite lors de la réception des résultats du serveur. (provider: TCP Provider, error: 0 - Une connexion existante a dû être fermée par l’hôte distant.)" (More details below.)
The message is in my native French and I'm not entirely sure what the official english version is. My translation would be, after some Web searching, "A transport-level error happened on receiving results from the server. (provider: TCP Provider, error: 0 - An existing connection was forcibly closed by the remote host.)"
I tried it again: the exception was thrown at the exact same row, reading the exact same field. (Subsequent tries showed the exception might be thrown in different places, but that did not vary wildly : out of eight tries, the exception was thrown in only three different places.
The SqlDataReader object happily reads that same field on the previous Read iterations, and checking the query's results in the database shows that the row where the exception is thrown doesn't have a particular value.
I tried tweaking the query's Where clause to get to the exception faster: it actually read more rows than before, then threw the same exception on a different row. (But again, that was more or less on the same row each time with the tweaked query.)
Looking closer, I put a conditional breakpoint on the instruction that throws the exception. It turns out that:
Reading the Item() property with the QuickWatch throws the same exception.
Reading the Item() property for another field that hasn't been read yet in the current iteration throws the same exception.
Conversely, reading the Item() property for other fields already read in the current iteration throws no exception.
I looked up the SqlDataReader's properties before and after the exception. State changes, going from Open to Closed (which is consistent with the exception). ClientConnectionId changes as well, its new value being an unsurprising series of 0s.
What is happening, and how can I prevent that?
APPENDIX 1
The code that opens the SqlDataReader is as follows:
Dim z_dreLecteur As IDataReader = Nothing
Dim z_dcdCommande As IDbCommand = Nothing
[...]
If TypeOf p_dcxConnexion Is SqlConnection Then
z_dcdCommande = New SqlCommand(p_strQuery, DirectCast(p_dcxConnexion, SqlConnection))
Exit Try
End If
[...]
z_dcdCommande.CommandTimeout = 600
[...]
z_dreLecteur = z_dcdCommande.ExecuteReader()
[...]
z_dcdCommande.Dispose()
p_dcxConnexion is a parameter, already set before the above code runs.
APPENDIX 2
More details on the SqlException.
It has an InnerException:
type System.ComponentModel.Win32Exception,
ErrorCode -2147467259,
Message "Une connexion existante a dû être fermée par l’hôte distant" ("An existing connexion was forcibly closed by the remote host"),
NativeErrorCode 10054.
The SqlException has:
ErrorCode -2146232060,
Number 10054,
Message "Une erreur de niveau transport s'est produite lors de la réception des résultats du serveur. (provider: TCP Provider, error: 0 - Une connexion existante a dû être fermée par l’hôte distant.)", which probably translates to "A transport-level error happened on receiving results from the server. (provider: TCP Provider, error: 0 - An existing connection was forcibly closed by the remote host.)"
I can add the StackTrace if needed, but it's really bulky so I won't for now.

Related

Excel power query, error handling for fallback connection/OleDb data source

I have a PowerQuery connected to DB2 Data Source, but because of some kind of load balancing the DB Server changes periodically (ip also changes), There is no way for me to know beforehand which data source (IP), I should use until I try it and see that it errors out, then I have to use the other one, I googled for error handling in PowerQuery and found some examples of error handling but this examples did not apply to my case, most of them handled errors AFTER the connection was made or were preventing errors on missing columns or files not found, I tried to adjust the examples to my case but was not able to.
What I want is just try one IP and if it fails then use the other one.
let
//fParametros("ParamQuery",1) is the "standard" Server/Ip address (provider=IBMDADB2.IBMDBCL1;data source=CP3;location=pn8us7ldbcp3.us.mycompany.com:5912)
dbSource = fParametros("ParamQuery",1),
//fParametros("ParamQuery",5) is the "Alternate" Server/Ip address (provider=IBMDADB2.IBMDBCL1;data source=CP3;location=pn8us7ldbcp3h.us.mycompany.com:5912)
AltdbSource = fParametros("ParamQuery",5),
pOrden = Text.From(fParametros("ParamQuery",2)),
//Create the query
dbQuery = "SELECT SAPCP3.vbak.VBELN SO , SAPCP3.vbap.posnr PoLine , SAPCP3.vbep.ETENR Sch_Line , SAPCP3.vbap.matnr Part_Number,SAPCP3.makt.maktx Description,SAPCP3.vbap.kwmeng Qty ,SAPCP3.vbep.BMENG Conf_qty ,SAPCP3.vbap.vrkme UOM ,SAPCP3.vbap.netpr SalesPrice ,SAPCP3.vbap.kpein LotSize FROM SAPCP3.vbak JOIN SAPCP3.vbap ON SAPCP3.VBAp.VBELN = SAPCP3.VBAK.VBELN JOIN SAPCP3.vbep ON SAPCP3.vbep.vbeln = SAPCP3.vbak.vbeln AND SAPCP3.vbap.posnr = SAPCP3.vbep.posnr JOIN sapcp3.makt ON sapcp3.vbap.matnr=sapcp3.makt.matnr WHERE SAPCP3.VBAK.VKORG = '4000' AND (SAPCP3.vbep.edatu >= '20190701') AND SAPCP3.vbak.VBELN ="& pOrden & " ORDER BY SAPCP3.vbak.VBELN",
//Get the data
Source = OleDb.DataSource(dbSource, [Query=dbQuery]),
//Failed Attempt to handle the error:
TestForError= try Source,
//next line does not work, I get error saying Source is already defined/declared
Source = if TestForError[HasError] then OleDb.DataSource(AltdbSource, [Query=dbQuery]) else OleDb.DataSource(dbSource, [Query=dbQuery])
in
Source
I also Tried the following:
.
.
.
//Get the data
Source = OleDb.DataSource(dbSource, [Query=dbQuery]),
//Failed Attempt to handle the error:
TestForError= try Source,
Output = if TestForError[HasError] then OleDb.DataSource(AltdbSource, [Query=dbQuery]) else OleDb.DataSource(dbSource, [Query=dbQuery])
in
Output
//This last part works if the dbSource is correct, but if it is not it doesnt catch the error and gives me the Connection error shown below:
DataSource.Error: OLE DB: SQL30081N A communication error has been detected. Communication protocol being used: "TCP/IP". Communication API being used: "SOCKETS". Location where the error was detected: "172.16.0.1". Communication function detecting the error: "connect". Protocol specific error code(s): "10061", "", "". SQLSTATE=08001
Details:
DataSourceKind=OleDb
DataSourcePath=data source=CP3;location=pn8us7ldbcp3.us.mycompany.com:5912;provider=IBMDADB2.IBMDBCL1
Message= SQL30081N A communication error has been detected. Communication protocol being used: "TCP/IP". Communication API being used: "SOCKETS". Location where the error was detected: "172.16.0.1". Communication function detecting the error: "connect". Protocol specific error code(s): "10061", "", "". SQLSTATE=08001
ErrorCode=-2147467259
I want to connect to dbSource (172.16.0.1) and if it is not possible then connect to AltdbSource (172.16.0.2)
Currently I have 2 identical excel sheeets and user needs to open one or the other if he gets the connection error, I want it to change automatically.
I'm guessing that TestForError = try OleDb.DataSource(BadSource) isn't working because OleDb.DataSource(...) returns a table value than only produces an error when trying to enumerate rows.
What if you change TestForError to drill into the first row if it exists:
TestForError= try Source{0}?,
If that still doesn't work, next try indexing into a table cell in the first row.

Why is "using" seemingly rejected in one function, but okay in another?

With this code:
Protected Function GetArgValsForCompanyName(coName As String) As String()
Dim args(2) As String
Using con As New SqlConnection("SERVER=PLATYPUS42;DATABASE=duckbilldata;UID=durante;PWD=pondscum"),
cmd As New SqlCommand("select Unit, MemberNo, CustNo from Customers WHERE CompanyName = #CoName", con)
con.Open()
cmd.CommandType = CommandType.Text
cmd.Parameters.Add("#CoName", SqlDbType.VarChar, 50).Value = coName
Using reader As SqlDataReader = cmd.ExecuteReader
While reader.Read
args(0) = reader.Item(0).ToString()
args(1) = reader.Item(1).ToString()
args(2) = reader.Item(2).ToString()
End While
End Using
End Using
Return args
End Function
...I get:
Server Error in '/EMS/customerreportingnet' Application.
--------------------------------------------------------------------------------
Compilation Error
Description: An error occurred during the compilation of a resource required to service this request. Please review the following specific error details and modify your source code appropriately.
Compiler Error Message: BC30203: Identifier expected.
Source Error:
Line 94: Dim args(2) As String
Line 95:
Line 96: Using con As New SqlConnection("SERVER=PLATYPUS42;DATABASE=duckbilldata;UID=durante;PWD=pondscum"),
Line 97: cmd As New SqlCommand("select Unit, MemberNo, CustNo from Customers WHERE CompanyName = #CoName", con)
Line 98:
Source File: C:\EnhancedMonthlySalesReporting\customerreportingnet\customerreportingnet\pages\custmaint_categoryadmin.aspx.vb Line: 96
--------------------------------------------------------------------------------
Version Information: Microsoft .NET Framework Version:2.0.50727.5485; ASP.NET Version:2.0.50727.5491
So, the "using con" line is implicated as the problem. I commented out most of the code to just return some random values, and it works fine.
However, there is another "Using" being used: the Button1_Click event handler uses a using. Does it not complain only because the button has not been clicked?
Actually, when I click the button, I do get an error, but it's not about the using; it's because I apparently have too many controls on the form that the button deals with (thousands or label/checkbox pairs, in fact):
Server Error in '/EMS/customerreportingnet' Application.
--------------------------------------------------------------------------------
Operation is not valid due to the current state of the object.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.InvalidOperationException: Operation is not valid due to the current state of the object.
Source Error:
An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.
Stack Trace:
[InvalidOperationException: Operation is not valid due to the current state of the object.]
System.Web.HttpValueCollection.ThrowIfMaxHttpCollectionKeysExceeded() +4198079
System.Web.HttpValueCollection.FillFromEncodedBytes(Byte[] bytes, Encoding encoding) +60
System.Web.HttpRequest.FillInFormCollection() +189
[HttpException (0x80004005): The URL-encoded form data is not valid.]
System.Web.HttpRequest.FillInFormCollection() +11196408
System.Web.HttpRequest.get_Form() +119
System.Web.TraceContext.InitRequest() +1188
System.Web.TraceContext.VerifyStart() +133
System.Web.HttpApplication.System.Web.IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb, Object extraData) +11307449
System.Web.HttpRuntime.ProcessRequestInternal(HttpWorkerRequest wr) +452
--------------------------------------------------------------------------------
Version Information: Microsoft .NET Framework Version:2.0.50727.5485; ASP.NET Version:2.0.50727.5491
So is it really the "using" that's the problem in the first block of code? Is there a way to still use it, or do I need to revert to the olde-[fangl,fashion]ed way of creating objects?
In VB.NET, the line continuation character allows to treat two lines as one from the point of view of the compiler. This character is the underscore and should be the last one on the line.
This requirement has been largely removed by latest version of VB.NET Compiler that has enough intelligence to understand where the statements end.
For example in LinqPAD 5.10 there is no need to use this character between the two using statement.
Instead, It seems that your current compiler cannot understand this and adding the continuation character should solve the problem
Using con As New SqlConnection("SERVER=PLATYPUS42;DATABASE=duckbilldata;UID=durante;PWD=pondscum"), _
cmd As New SqlCommand("select Unit, MemberNo, CustNo from Customers WHERE CompanyName = #CoName", con)

SQL Server Query won't run

I am connected to a remote SQL Server instance
These queries works fine:
SELECT COUNT(*) FROM Provider
SELECT TOP 1 * FROM Provider
but these don't
SELECT * FROM Provider
SELECT TOP 2 * FROM Provider
and return this error after a long delay:
Msg 64, Level 20, State 0, Line 0
A transport-level error has occurred when receiving results from the
server. (provider: TCP Provider, error: 0 - The specified network name
is no longer available.)
What configuration should I be looking at that would allow a single row result but not a multiple row result?
I found the following
Hey mate, another thing to try i found the following and have included the URL for you. Considering the first bit of trouble shooting didn't work for you
Symptoms
The stack trace of the error resembles the following:
A transport-level error has occurred when receiving results from the server. (provider: TCP Provider, error: 0 - The specified network name is no longer available.)
at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection)
at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection)
at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj)
at System.Data.SqlClient.TdsParserStateObject.ReadSniError(TdsParserStateObject stateObj, UInt32 error)
at System.Data.SqlClient.TdsParserStateObject.ReadSni(DbAsyncResult asyncResult, TdsParserStateObject stateObj)
at System.Data.SqlClient.TdsParserStateObject.ReadPacket(Int32 bytesExpected)
at System.Data.SqlClient.TdsParserStateObject.ReadBuffer()
at System.Data.SqlClient.TdsParserStateObject.ReadByte()
Cause
The error is being caused by a timeout exception for long running queries. In previous versions of Visual Studio .NET, the exception was properly represented as a exception with a timeout description.
Resolution
Set the commandtimeout property of the command object to an appropriate value. Use a value of zero to wait without an exception being thrown.
Taken from https://support.microsoft.com/en-us/kb/555938

System.Data.SqlClient.SqlException (0x80131904)

I have a process that runs every morning looping through thousands of records and establishing whether they can be deleted or not. I sometimes see this exception in the log file:
System.Data.SqlClient.SqlException (0x80131904): A transport-level error has occurred when receiving results from the server. (provider: Session Provider, error: 19 - Physical connection is not usable)
I have read a potential solution here: http://wishmesh.com/2013/10/solution-for-a-transport-level-error-has-occurred-when-receiving-results-from-the-server/
I know that the SQLDataReader class implements IDisposable as documented here: http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqldatareader(v=vs.110).aspx. Therefore you should either wrap it in USING statements or call .close once you have finished with it.
What happens if you do not call .close? I do not understand why it could cause this exception as I thought a data reader was simply a collection of objects already returned from the database and stored in memory.

Get more info from sqlclient error message

I connect my windows mobile app directly to ms-sql server using the sqlclient dll from microsoft.
Some times I get this error when trying to get some data from the server:
System.Data.SqlClient.SqlException: SqlException
at System.Data.SqlClient.SqlConnection.OnError()
at System.Data.SqlClient.SqlInternalConnection.OnError()
at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning()
at System.Data.SqlClient.TdsParser.Run()
at System.Data.SqlClient.SqlDataReader.ConsumeMetaData()
at System.Data.SqlClient.SqlDataReader.get_MetaData()
at System.Data.SqlClient.SqlCommand.ExecuteReader()
at System.Data.SqlClient.SqlCommand.ExecuteReader()
Every time I have a error like this I need to try a few other things, some times the connection string is no good, or the network is not configured right or any other random problem.
Is there a way I could get a bit more info from the program so I shouldn't have to waste a few hours figuring out what might went rung?
That's basically just the stack trace. You should be able to get the Message property of the SqlException which contains the details. In some cases you will have to look at the InnerException to get to the interesting details.
Ok I found the answer... I changed: Catch ex As Exception To: Catch ex As SqlException
Now I get the specific error nicely displayed in the message.