【问题标题】:how can i open execuete a query in VB while there is reader opened?当打开阅读器时,如何在 VB 中打开执行查询?
【发布时间】:2013-10-04 13:27:28
【问题描述】:

是否有任何可能的方法来执行此操作而不会出现此错误“已经有一个打开的 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

【问题讨论】:

  • 像这样构建查询的字符串连接会使您容易受到 sql 注入攻击。这非常糟糕。

标签: mysql vb.net


【解决方案1】:

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

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

【讨论】:

    【解决方案2】:

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

    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 不会停止执行代码。下一组字符构成了一个附加语句,会删除您的表格。最后两个字符注释掉后面的任何内容,以避免 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 块将确保立即关闭连接...即使抛出异常。如果出现异常,您现有的代码将使连接挂起。

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

    【讨论】:

      【解决方案3】:

      代替:

          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.
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2022-01-18
        • 1970-01-01
        • 2018-03-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-06-24
        相关资源
        最近更新 更多