0

是否有任何可能的方法来执行此操作而不会出现此错误“已经有一个打开的 DataReader 与此连接关联,必须先关闭。” 我已经尝试使用“ dr.close()”,但我收到另一个错误,显示“阅读器关闭时读取尝试无效”。你能帮我吗?

这是我的代码:

Private Sub Button4_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button4.Click
    Label2.Text = AllPicker1.Text
    Label3.Text = AllPicker2.Text
    If AllPicker1.Value >= AllPicker2.Value Then
        MsgBox("End Date Must be Greater!")
    Else
        Dim SQLstatement As String = "SELECT * FROM tblStudInfo,tbl_studentLog WHERE tblStudInfo.StudID = tbl_studentLog.StudentNumber AND tbl_studentLog.LoginDate BETWEEN '" & AllPicker1.Text & "' AND '" & AllPicker2.Text & "'"
        OpenData(SQLstatement)
    End If
End Sub

Public Sub OpenData(ByRef SQLstatement As String)
    Dim cmd As MySqlCommand = New MySqlCommand

    With cmd
        .CommandText = SQLstatement
        .CommandType = CommandType.Text
        .Connection = SqlConnection
        dr = .ExecuteReader()
    End With
    While dr.Read
        Dim SQLstatementSave As String = "INSERT INTO tbl_report (RepStudNo,RepName,RepCourse,RepDept,RepLogTime,RepLogdate) VALUES ('" & dr("StudID") & "','" & dr("Name") & "','" & dr("Course") & "','" & dr("Dept") & "','" & dr("LoginTime") & "','" & dr("LoginDate") & "') "
        dr.Close()
        Save(SQLstatementSave)
    End While
    SqlConnection.Close()
    SqlConnection.Dispose()
    SqlConnection.Open()
End Sub

Public Sub Save(ByRef SQLstatementSave As String)
    Dim cmd As MySqlCommand = New MySqlCommand

    With cmd
        .CommandText = SQLstatementSave
        .CommandType = CommandType.Text
        .Connection = SqlConnection
        .ExecuteNonQuery()
    End With

    SqlConnection.Close()
    SqlConnection.Dispose()
    SqlConnection.Open()
End Sub
End Class
4

3 回答 3

3

看来您只使用了一个 SqlConnection。对于大多数数据库系统,您在读取连接时不能重用连接。您可以将所有数据读入内存/DataTable 并在之后处理行,或者为您的插入使用不同的 SqlConnection。

在使用 SqlConnections、Readers 和 Commands 时,我发现Using 语句对于可视化对象的使用和创建非常有帮助。

于 2013-10-04T14:07:02.710 回答
0

我们可以将其简化为单个查询:

INSERT INTO tbl_report 
      (RepStudNo,RepName,RepCourse,RepDept,RepLogTime,RepLogdate)
   SELECT StudID, Name, Course, Dept, LoginTime, LoginDate
   FROM tblStudInfo
   INNER JOIN tbl_studentLog ON tblStudInfo.StudID = tbl_studentLog.StudentNumber
   WHERE tbl_studentLog.LoginDate BETWEEN @StartDate AND @EndDate

注意使用完整的 INNER JOIN 语法。应避免使用旧的 TableA、TableB 连接语法。还要注意日期占位符的使用。这很重要

现在我需要注意我看到的几个函数:OpenData() 和 Save()。

这两个功能从根本上被破坏了,因为它们迫使您以一种容易受到 sql 注入黑客攻击的方式构建查询。很快有一天,有人会将这样的值放入包含在查询中的文本框中:

';DROP 表 tbl_studentLog;--

仔细想想如果有人将它输入到您的AllPicker1.Text. 对于日期选择器很难做到这一点,但我敢打赌你有其他纯文本字段可以做到这一点。我建议的输入中的第一个字符(单引号)将关闭查询中的字符串文字。第二个字符(分号)将结束单个语句,但 sql server不会停止执行 code。下一组字符组成了一个附加语句,该语句将删除您的表。最后两个字符注释掉后面的任何内容,以避免 sql server 由于语法错误而拒绝或不提交命令。是的,如果您在文本框中输入的内容,Sql Server将运行该附加语句。

所以,你写的方法被破坏了,因为只接受完整的 sql 字符串作为输入。任何调用数据库的函数都必须包含接受查询参数的机制。您最终希望运行的代码更像这样:

Public Sub CreateReport(ByVal StartDate As DateTime, ByVal EndDate As DateTime)
    Dim sql As String = _
         "INSERT INTO tbl_report " & _
            " (RepStudNo,RepName,RepCourse,RepDept,RepLogTime,RepLogdate) " & _
            " SELECT StudID, Name, Course, Dept, LoginTime, LoginDate " & _
            " FROM tblStudInfo " & _
            " INNER JOIN tbl_studentLog ON tblStudInfo.StudID = tbl_studentLog.StudentNumber " & _
            " WHERE tbl_studentLog.LoginDate BETWEEN @StartDate AND @EndDate"

    '.Net is designed such in most cases that you really do want a new SqlConnection for each query
    'I know it's counter-intuitive, but it is the right way to do this
    Using cn As New SqlConnection("Connection string"), _
          cmd As New SqlCommand(sql, cn)

        'Putting your data into the query using parameters like this is safe from injection attacks
        cmd.Parameters.Add("@StartDate", SqlDbType.DateTime).Value = StartDate
        cmd.Parameters.Add("@EndDate", SqlDbType.DateTime).Value = EndDate

        cn.Open()
        cmd.ExecuteNonQuery()
    End Using
End Sub

这里要指出的一点是,乍一看我并没有关闭连接。但是,该Using块将确保立即关闭连接......即使抛出异常。如果出现异常,您现有的代码将使连接挂起。

另请注意,这巧妙地避开了在打开阅读器时需要执行单独查询的整个问题......但如果你真的需要这样做(很少见),答案很简单:使用单独的连接.

于 2013-10-04T14:47:20.680 回答
0

代替:

    Dim SQLstatementSave As String = "INSERT INTO tbl_report
    (RepStudNo,RepName,RepCourse,RepDept,RepLogTime,RepLogdate) 
    VALUES ('" & dr("StudID") & "','" & etc.

尝试在您的 DR() 引用上使用 .ToString。

    Dim SQLstatementSave As String = "INSERT INTO tbl_report
    (RepStudNo,RepName,RepCourse,RepDept,RepLogTime,RepLogdate) 
    VALUES ('" & dr("StudID").ToString & "','" & etc.
于 2013-10-04T15:17:49.083 回答