简体   繁体   中英

Nesting sql queries in vb.net

The following code give the following error when I tried to nest sql queries.

There is already an open DataReader associated with this Command which must be closed first

reader4 = cmd4.ExecuteReader()

Stack trace:

[InvalidOperationException: There is already an open DataReader associated with this Command which must be closed first.]
   System.Data.SqlClient.SqlInternalConnectionTds.ValidateConnectionForExecute(SqlCommand command) +5333807
   System.Data.SqlClient.SqlConnection.ValidateConnectionForExecute(String method, SqlCommand command) +51
   System.Data.SqlClient.SqlCommand.ValidateCommand(String method, Boolean async) +155
   System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method, TaskCompletionSource`1 completion, Int32 timeout, Task& task, Boolean asyncWrite) +82
   System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method) +53
   System.Data.SqlClient.SqlCommand.ExecuteReader(CommandBehavior behavior, String method) +137
   System.Data.SqlClient.SqlCommand.ExecuteReader() +99
   CW_Recruiter_postjob.btnPostJob_Click(Object sender, EventArgs e) in C:\Users\Khav\Documents\Visual Studio 2013\WebSites\BSE14BFT_140928\CW\Recruiter\postjob.aspx.vb:42
   System.EventHandler.Invoke(Object sender, EventArgs e) +0
   System.Web.UI.WebControls.Button.OnClick(EventArgs e) +9628722
   System.Web.UI.WebControls.Button.RaisePostBackEvent(String eventArgument) +103
   System.Web.UI.WebControls.Button.System.Web.UI.IPostBackEventHandler.RaisePostBackEvent(String eventArgument) +10
   System.Web.UI.Page.RaisePostBackEvent(IPostBackEventHandler sourceControl, String eventArgument) +13
   System.Web.UI.Page.RaisePostBackEvent(NameValueCollection postData) +35
   System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +1724

Code :

 Protected Sub btnPostJob_Click(sender As Object, e As EventArgs) Handles btnPostJob.Click
        Dim con As New SqlConnection(ConfigurationManager.ConnectionStrings("JPortalCS").ConnectionString)
        con.Open()
        Dim cmd As New SqlCommand("INSERT INTO [JobDetails].[Job](Job_Title,Job_Desc,Start_Date,End_Date,Status,Category_ID,Recruiter_ID) VALUES (@Title,@Desc,@Start,@End,@Status,@Category_ID,@Recruiter_ID)", con)
        cmd.Parameters.AddWithValue("@Title", job_title.Text)
        cmd.Parameters.AddWithValue("@Desc", job_desc.Text)
        cmd.Parameters.AddWithValue("@Start", DateTime.Now.ToString("d"))
        cmd.Parameters.AddWithValue("@End", DateTime.Now.ToString("d"))
        cmd.Parameters.AddWithValue("@Status", "Available")
        'getting category_id 
        Dim reader As SqlDataReader
        Dim cmd2 As New SqlCommand("SELECT * FROM [JobDetails].[Category] where Category_Desc=@cat", con)
        cmd2.Parameters.AddWithValue("@cat", ddlCategory.SelectedValue)
        reader = cmd2.ExecuteReader()
        Dim catid As Integer
        If reader.HasRows Then
            reader.Read()
            catid = reader.Item("Category_ID")
        End If
        'end
        'getting user_id
        Dim reader4 As SqlDataReader
        Dim cmd4 As New SqlCommand("SELECT * FROM [UserDetails].[User] where User_Name=@username", con)
        cmd4.Parameters.AddWithValue("@username", Session("login_user"))
        reader4 = cmd4.ExecuteReader()
        Dim userid As Integer
        If reader4.HasRows Then
            reader4.Read()
            userid = reader4.Item("User_ID")
        End If
        'getting recruiter_id 
        Dim reader3 As SqlDataReader
        Dim cmd3 As New SqlCommand("SELECT * FROM [UserDetails].[Recruiter] where User_ID=@user", con)
        cmd3.Parameters.AddWithValue("@user", userid)
        reader3 = cmd3.ExecuteReader()
        Dim recruterid As String = ""
        If reader3.HasRows Then
            reader3.Read()
            recruterid = reader3.Item("Recruiter_ID")
        End If
        'end
        cmd.Parameters.AddWithValue("@Category_ID", catid)
        cmd.Parameters.AddWithValue("@Recruiter_ID", recruterid)
        cmd.CommandType = CommandType.Text
        cmd.ExecuteNonQuery()
        con.Close()
    End Sub

the error message is self explanatory: only one data reader can be active on a single connection (see remarks in the documentation ).
you could hunt for the culprit trying to understand which of the many object that are not disposed is to blame or rewrite your code in a more clean (and readable, and elegant) way.

here follows an example of what your code should look like:

Dim catid As Integer
Dim userid As Integer
'getting category_id 
Using cmd As New SqlCommand("SELECT * FROM [JobDetails].[Category] where Category_Desc=@cat", con)
    cmd.Parameters.AddWithValue("@cat", ddlCategory.SelectedValue)
    Using reader As SqlDataReader = cmd.ExecuteReader()
        If reader.HasRows Then
            reader.Read()
            catid = reader.Item("Category_ID")
        End If
    End Using
End Using
'end
'getting user_id
Using cmd As New SqlCommand("SELECT * FROM [UserDetails].[User] where User_Name=@username", con)
    cmd.Parameters.AddWithValue("@username", Session("login_user"))
    Using reader As SqlDataReader = cmd.ExecuteReader()
        If reader.HasRows Then
            reader.Read()
            userid = reader4.Item("User_ID")
        End If
    End Using
End Using

this way you avoid the issue because the SqlDataReader object is disposed immediately after use and is not around to crash the next call.

another change that could improve the overall readability and stability is the use of explicit naming of fields in sql query:

SqlCommand("SELECT * FROM [JobDetails].[Category] where Category_Desc=@cat", con)

can be written as:

SqlCommand("SELECT Category_ID FROM [JobDetails].[Category] where Category_Desc=@cat", con)

there is no need to retrieve the whole row if you use just a single value.
using the above solution you could even rewrite the call to retrieve the Category_ID field as:

'getting category_id 
Using cmd As New SqlCommand("SELECT Category_ID FROM [JobDetails].[Category] where Category_Desc=@cat", con)
    cmd.Parameters.AddWithValue("@cat", ddlCategory.SelectedValue)
    catid = cmd.ExecuteScalar()
End Using
'end

have a look at the documentation for the behaviour and limits of ExecuteScalar

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM