My new application page is getting a timeout error every hour or so after getting some traffic on the page, and by traffic I mean users submitting 5-10 applications. How do I find the cause of the connections getting tied up?
This has been an issue in the past so whenever I use a sql data reader object I make sure to implement the "Using" statement. I've also made sure that the thread isn't aborted before the data reader is disposed of. I doubt that my use of data readers is the issue, so maybe it's my non-query code that's causing the issue, but I can't see why.
I also use a few sqldatasource objects for my dropdownlist controls, and as far as I know it wouldn't be the source of my issue.
See code example for how I use my sql objects.
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs)
Using drApp As SqlDataReader = LookupAppByID()
'some code
drApp.Close()
End Using
End Sub
Public Function LookupAppByID() As SqlDataReader
Dim Command As New SqlClient.SqlCommand
Command.Connection = GetDBConnection()
Command.CommandType = CommandType.Text
Command.CommandText = "select statement"
Return Command.ExecuteReader(CommandBehavior.CloseConnection)
End Function
Public Function UpdateAppStatus() As Integer
UpdateAppStatus = 0
Using Command As New SqlClient.SqlCommand("update statement", GetDBConnection())
UpdateAppStatus = Command.ExecuteNonQuery()
Command.Connection.Close()
Command.Connection.Dispose()
Command.Dispose()
End Using
End Function
Public Function GetDBConnection() As SqlClient.SqlConnection
Dim connection As New SqlClient.SqlConnection
connection.ConnectionString = "connection string"
connection.Open()
Return connection
End Function
Obviously I expect it to chug along without a hitch but when users start hitting the page it gets this error: Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use and max pool size was reached.
Is there a problem with my code?
How can I narrow down what is causing this issue?
I would keep the database objects local so I can ensure that they are closed and disposed. Create and dispose within one method. I created a class so you can easily pass all its properties in a single variable.
'User Interface Code
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs)
Dim id As Integer 'No sure where the value comes from
Using dtApp As DataTable = DataAccess.LookupAppByID(id)
'some code
End Using
End Sub
Protected Sub Update_Click(sender As Object, e As EventArgs) Handles Update.Click
Dim Applic As New ApplicationData()
Applic.ApplicantsName = txtName.Text
Applic.ID = CInt(txtID.Tex)
Applic.ApplicationStatus = "Approved"
Dim retVal = DataAccess.UpdateAppStatus(Applic)
If retVal = 1 Then
'Alert user of success
Else
'Alert user of failure
End If
End Sub
Public Class ApplicationData
Public Property ID As Integer
Public Property AppDate As Date
Public Property ApplicantsName As String
Public Property ApplicationStatus As String
End Class
Public Class DataAccess
Private Shared ConString As String = "Your connection string"
Public Shared Function LookupAppByID(AppID As Integer) As DataTable
Dim dt As New DataTable
Using cn As New SqlConnection(ConString)
Using Command As New SqlCommand("select * From YourTable Where ID = #ID", cn)
Command.Parameters.Add("#ID", SqlDbType.Int).Value = AppID
cn.Open()
dt.Load(Command.ExecuteReader)
End Using
End Using
Return dt
End Function
Public Shared Function UpdateAppStatus(App As ApplicationData) As Integer
Dim AppStatus = 0
Using cn As New SqlConnection(ConString)
Using Command As New SqlClient.SqlCommand("Update YourTable Set Status = #Status Where ID = #ID;", cn)
Command.Parameters.Add("#Status", SqlDbType.VarChar, 50).Value = App.ApplicationStatus
Command.Parameters.Add("#ID", SqlDbType.Int).Value = App.ID
cn.Open()
AppStatus = Command.ExecuteNonQuery()
End Using
End Using
Return AppStatus
End Function
End Class
I need to retrieve data from the database first and then update the table with the new entry, following is my code but I am having an error:
"Invalid attempt to call Read when reader is closed."
I know I need to open the datareader by commenting dr1.close, but as soon as I did that I face an another exception:
"there is already an open datareader associated with this command which must be closed first. vb.net"
Imports System.IO
Imports System.Data.Sql
Imports System.Data.SqlClient
Imports System.Data
Partial Class Officer_Module_GST_id_password
Inherits System.Web.UI.Page
Dim sscript As String
Dim sms As New smsgw
Dim mail As New MailSender
Dim cmd As New SqlCommand
Dim ds As New DataSet
Dim dr As SqlDataReader
Dim objconn As New connectioncls
Dim name As String
Dim pid As String
Dim pwd As String
Dim email_sent As Integer
Dim email_status As String
Dim mobile As String
Dim message As String
Dim subject As String
Dim email As String
Dim mtext As String
Protected Sub validatedeal_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles validatedeal.Click
containertwo.Visible = True
txt_subject.Text = "Communication of the Provisional Identification Number and Password"
txt_mail.Text = "Instance mail"
End Sub
Protected Sub btnsendmail_Click(ByVal sender As Object, ByVal e As System.EventArgs)
objconn.openconn()
cmd.Connection = objconn.conn
cmd.CommandText = "Select Trade_name,provissional_id,access_token,reg_mobile_no,reg_email_id,email_status,isnull(no_of_email_sent,0) from Provisional_details"
Dim dr1 As SqlDataReader = cmd.ExecuteReader()
While (dr1.Read())
name = dr1(0).ToString()
pid = dr1(1).ToString()
pwd = dr1(2).ToString()
mobile = dr1(3).ToString()
email = dr1(4).ToString()
email_status = dr1(5).ToString()
email_sent = dr1(6).ToString()
subject = "subject to instance"
mtext = "new instance email"
message = "new instance message"
Try
MailSender.SendEmail("riteshbhatt93" + "#gmail.com", "rock_on", email, subject, mtext, System.Web.Mail.MailFormat.Text, "")
Try
Call sms.SendSMSUsingNICGW(mobile, message)
Catch
sscript = "<script language=javascript>alert('Message not sent!!')</script>"
Page.ClientScript.RegisterStartupScript(Me.GetType(), "Empty", sscript)
sscript = Nothing
Exit Try
Finally
End Try
Try
Call sms.SendSMSUsingMGOVGW(mobile, message)
Catch
sscript = "<script language=javascript>alert('Message not sent!!')</script>"
Page.ClientScript.RegisterStartupScript(Me.GetType(), "Empty", sscript)
sscript = Nothing
Exit Try
Finally
End Try
Catch
Dim cmd1 As New SqlCommand
cmd1.Connection = objconn.conn
cmd1.Parameters.AddWithValue("#mobile", mobile)
cmd1.Parameters.AddWithValue("#Email_status", "NO")
cmd1.CommandText = "Update Provisional_details set Email_sent = #Email_status where reg_mob_no = #mobile"
cmd1.ExecuteNonQuery()
cmd1.Parameters.Clear()
Exit Sub
Finally
End Try
dr1.Close()
Dim cmd2 As New SqlCommand
cmd2.Connection = objconn.conn
cmd2.Parameters.AddWithValue("#mobile", mobile)
cmd2.Parameters.AddWithValue("#Email_status", "YES")
cmd2.Parameters.AddWithValue("#emailsent", email_sent + 1)
cmd2.CommandText = "Update Provisional_details set email_status = #Email_status,no_of_email_sent = #emailsent where reg_mobile_no = #mobile"
cmd2.ExecuteNonQuery()
cmd2.Parameters.Clear()
End While
sscript = "<script language=javascript>alert('Your Mail has been sent to all applied dealers!!')</script>"
Page.ClientScript.RegisterStartupScript(Me.GetType(), "Empty", sscript)
sscript = Nothing
End Sub
End Class
I've pared this down to just the method that matters (like you should have done when posting the question). Updates are in the method, using comments to annotate what's going on.
Protected Sub btnsendmail_Click(ByVal sender As Object, ByVal e As System.EventArgs)
'Best practice in .Net is use a brand new connection instance for most DB calls. Really.
'Don't try to be clever and re-use one connection. Just use the same string.
Dim connString As String = "Connection String Here"
'Using block will guarantee connection closes properly, even if an exception is thrown
Using cn As New SqlConnection(connString), _
cmd As New SqlCommand("Select Trade_name,provissional_id,access_token,reg_mobile_no,reg_email_id,email_status,isnull(no_of_email_sent,0) from Provisional_details", cn), _
cn2 As New SqlConnection(connString), _
cmd2 As New SqlCommand("Update Provisional_details set email_status = #Email_status,no_of_email_sent = #emailsent where reg_mobile_no = #mobile", cn2)
'Define your parameters as early as possible, and be explicit about parameter types and lengths
' This will avoid potentially crippling performance gotchas
cmd2.Parameters.Add("#mobile", SqlDbType.NVarChar, 14)
cmd2.Parameters.Add("#Email_status", SqlDbType.VarChar, 5)
cmd2.Parameters.Add("#emailsent", SqlDbType.Int)
'Separate SQL statements in a tight loop like this is one of the few places to re-use a connection object...
' Even here, it should be a BIG RED FLAG that there's a better way to handle this whole process that avoids multiple calls to the DB.
' For example, it might be better to assume success, Update the DB accordingly in the original statement, and then only write failures back when needed
cn2.Open()
cn.Open()
dr1 As SqlDataReader = cmd.ExecuteReader()
While (dr1.Read())
'Best practice in .Net are to declare these variables in the method where you use them
Dim name As String = dr1(0).ToString()
Dim pid As String = dr1(1).ToString()
Dim pwd As String = dr1(2).ToString() 'You're not really storing passwords in plain-text are you? Please say, "No".
Dim mobile As String = dr1(3).ToString()
Dim email As String = dr1(4).ToString()
Dim email_status As String = dr1(5).ToString()
Dim email_sent As Integer = dr1.GetInt32(6) 'It's a number. You do math on it later. Get the INTEGER value
Dim subject As String = "subject to instance"
Dim mtext As String = "new instance email"
Dim message As String = "new instance message"
cmd2.Parameters("#mobile").Value = mobile
Try
MailSender.SendEmail("riteshbhatt93" + "#gmail.com", "rock_on", email, subject, mtext, System.Web.Mail.MailFormat.Text, "")
Try
' Also... the "Call" keyword is a vb6-era relic that has no purpose any more. Don't use it
sms.SendSMSUsingMGOVGW(mobile, message)
Catch
Page.ClientScript.RegisterStartupScript(Me.GetType(), "Empty", "<script language=javascript>alert('Message not sent!!')</script>")
'Don't set values to "Nothing" to free them in VB.Net.
'It doesn't help the way it used to in vb6/vbscript, and can actually be HARMFUL in rare cases in .Net
End Try
' Do you really mean to try both gateways, even if the first succeeds?
' Because that's what the original code is doing.
Try
sms.SendSMSUsingNICGW(mobile, message)
Catch
Page.ClientScript.RegisterStartupScript(Me.GetType(), "Empty", "<script language=javascript>alert('Message not sent!!')</script>")
'No need to call Exit Try here,
' and no need for an empty Finally section
End Try
Catch
cmd2.Parameters("#emailsent") = email_sent
cmd2.Parameters("#Email_status") = "NO"
cmd2.ExecuteNonQuery()
End Try
cmd2.Parameters("#Email_status").Value = "YES"
cmd2.Parameters("#emailsent").Value = email_sent + 1
cmd2.ExecuteNonQuery()
End While
End Using
End Sub
I'm having a problem when I'm reading data from a SQL Server database. The main thing is that I want to read the data from the database and display the data in a Label control. But the concern is that it can't read data to it. I will show you the code snippet and any comments/suggestions are gladly considered.
Option Explicit On
Imports System.Data
Imports System.Data.OleDb
Partial Class ViewDetail
Inherits System.Web.UI.Page
Dim con As OleDbConnection
Dim cmd As OleDbCommand
Dim dr As OleDbDataReader
Dim InstructorID As Integer
Protected Sub Page_Load(sender As Object, e As EventArgs) Handles Me.Load
InstructorID = Request.QueryString("Instructor_ID")
Integer.TryParse(lblID.Text, InstructorID)
con = New OleDbConnection("Provider=SQLNCLI11;Data Source=ARIES-PC\SQLEXPRESS;Integrated Security=SSPI;Initial Catalog=SchoolDB")
con.Open()
cmd = New OleDbCommand("SelectData", con)
cmd.CommandType = CommandType.StoredProcedure
cmd.Parameters.AddWithValue("#id", InstructorID)
dr = cmd.ExecuteReader
If dr.HasRows Then
While dr.Read
lblID.Text = dr("Instructor_ID").ToString
lblFirstname.Text = dr("FirstName").ToString
lblLastname.Text = dr("LastName").ToString
lblAddress.Text = dr("Address").ToString
lblContact.Text = dr("Contact_Number").ToString
End While
End If
dr.Close()
cmd.Dispose()
con.Close()
End Sub
End Class
This line seems to be totally wrong
Integer.TryParse(lblID.Text, InstructorID)
This lines takes the current value in the lblID.Text at the Page_Load event and tries to set the value of InstructorID. But your code seems to want this value from the QueryString passed that contains the real value.
If you are certain the the QueryString contains a valid integer then remove that line and add
InstructorID = Convert.ToInt32(Request.QueryString("Instructor_ID"))
Please have a look at the code below, which I have written as a test. It does not make the ASP.NET process grow too much:
Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
Try
Dim Test As Integer
For Test = 0 To 1000000
Test1()
Next
Dim Test2 As String = "Test"
Catch ex As Exception
End Try
End Sub
Public Sub Test1()
Try
Dim objCommand As New SqlCommand
Dim strConString As String = "Data Source=IANSCOMPUTER;Initial Catalog=Test;Integrated Security=True"
Dim objCon As New SqlConnection
objCon.ConnectionString = strConString
objCon.Open()
objCommand.Connection = objCon
objCommand.CommandText = "select * from person "
Dim objDR As SqlDataReader = objCommand.ExecuteReader
If objDR.HasRows Then
Using objCon
End Using
End If
Catch ex As Exception
'I don't swallow exceptions.
End Try
End Sub
End Class
I am trying to detect a memory leak. I have found code similar to the above in the affected project. I notice that objCommand.dispose is not called. The project in question connects to an Oracle database and an SQL database. When connecting to an SQL database the above code is used. When connecting to an Oracle database Oracle.dataaccess.dll is used and the code looks different to reflect this.
My specific question is: if I avoid calling sqlcommand.dispose then will this cause a memory leak over time?
If a class has a Dispose method, that isn't getting called, then it is likely to be the source of a memory leak. Use Using, and it will call the Dispose method for you.
Your second example should look like the below, where all disposable object are wrapped in using blocks.
Dim strConString As String = "Data Source=IANSCOMPUTER;" +
"Initial Catalog=Test;Integrated Security=True"
Dim sqlStr as String = "select * from person "
Using objCon As New SqlConnection(strConString)
Using objCommand As New SqlCommand(sqlStr, objCon)
objCon.Open()
Using objDR As SqlDataReader = objCommand.ExecuteReader
If objDR.HasRows Then
End If
End Using
End Using
End Using
My instructor gave us example code that is very similiar to the code below. I haven't heard back from him yet and wanted to find out why my code won't work properly. Could someone give me some advice on what I'm doing wrong or an easier method to display images. Thanks for your time.
<%# WebHandler Language="VB" Class="images" %>
Imports System
Imports System.Web
Imports System.Data.SqlClient
Public Class images : Implements IHttpHandler
Public Sub ProcessRequest(ByVal context As HttpContext) Implements IHttpHandler.ProcessRequest
Dim id As String = context.Request.QueryString("ImageId")
Dim userId As Integer = 4
Dim conn As New System.Data.SqlClient.SqlConnection
Dim cmd As New System.Data.SqlClient.SqlCommand
conn.ConnectionString = System.Configuration.ConfigurationManager.ConnectionStrings("ConnectionString").ConnectionString
cmd.Connection = conn
cmd.CommandText = "SELECT Image FROM mrg_Image WHERE UserId=#userId"
cmd.Parameters.AddWithValue("#userId", userId)
conn.Open()
Dim file_bytes As Byte() = cmd.ExecuteScalar()
Dim file_bytes_stream As New System.IO.MemoryStream(file_bytes)
'During the build - The next line of code is highlighted green with the error message of, "Parameter is invalid"
Dim the_image As New System.Drawing.Bitmap(file_bytes_stream)
context.Response.ContentType = "image/jpg"
the_image.Save(context.Response.OutputStream, System.Drawing.Imaging.ImageFormat.Jpeg)
conn.Close()
End Sub
Public ReadOnly Property IsReusable() As Boolean Implements IHttpHandler.IsReusable
Get
Return False
End Get
End Property
End Class
EDIT: Answer (such as it is) in comments. The questioners code is fine...
Looks fine to me. Are you comitting an implict type conversion between the first and last lines below?
Dim file_bytes_stream As New System.IO.MemoryStream(file_bytes)
'During the build - The next line of code is highlighted green with the error message of, "Parameter is invalid"
Dim the_image As New System.Drawing.Bitmap(file_bytes_stream)
If you have option explicit set you might need to do...
Dim file_bytes_memory_stream As New System.IO.MemoryStream(file_bytes)
Dim file_bytes_stream as System.IO.Stream = DirectCast(file_bytes_memory_stream, System.IO.Stream)
Dim the_image As New System.Drawing.Bitmap(file_bytes_stream)