System.Data.OleDb.OleDbException: 'Parameter @Status has no default value.' 在 VB

System.Data.OleDb.OleDbException: 'Parameter @Status has no default value.' in VB

抱歉,如果我的 post 没有遵循编码论坛的常见 practice/rules,因为这是我的第二个问题。

我有一个按钮,单击该按钮后,它会根据组合框中选定的值在 MS Access 数据库中搜索项目,并将这些项目添加到数据网格中。它为每个值创建一个参数,并将相关的组合框项添加到该参数。

CreateSQLStr() 函数根据具有选定值的组合框形成 SQL 命令文本(即,如果组合框没有选定值,则它会不要在 table 中为该字段指定条件,但如果指定,它将添加 AND (TableField = @Parameter) 到 SQL 命令文本中。

我已经尝试将“ACTIVE”更改为预定义的字符串并出现相同的错误,但我想不出其他任何东西。

我的错误是`System.Data.OleDb.OleDbException: 'Parameter @Status has no default value.'

详情:

System.Data.OleDb.OleDbException
  HResult=0x80040E10
  Message=Parameter @Status has no default value.
  Source=System.Data
  StackTrace:
   at System.Data.OleDb.OleDbCommand.ExecuteCommandTextErrorHandling(OleDbHResult hr)
   at System.Data.OleDb.OleDbCommand.ExecuteCommandTextForSingleResult(tagDBPARAMS dbParams, Object& executeResult)
   at System.Data.OleDb.OleDbCommand.ExecuteCommandText(Object& executeResult)
   at System.Data.OleDb.OleDbCommand.ExecuteCommand(CommandBehavior behavior, Object& executeResult)
   at System.Data.OleDb.OleDbCommand.ExecuteReaderInternal(CommandBehavior behavior, String method)
   at System.Data.OleDb.OleDbCommand.ExecuteReader(CommandBehavior behavior)
   at System.Data.OleDb.OleDbCommand.ExecuteReader()
   at WindowsApplication1.frmEmployee.vehiclesearch_Click(Object sender, EventArgs e) in C:\Users\aawad\OneDrive\Documents\QHP\QHP\QHP\frmEmployee.vb:line 79
   at System.EventHandler.Invoke(Object sender, EventArgs e)
   at System.Windows.Forms.Control.OnClick(EventArgs e)
   at System.Windows.Forms.Button.OnClick(EventArgs e)
   at System.Windows.Forms.Button.OnMouseUp(MouseEventArgs mevent)
   at System.Windows.Forms.Control.WmMouseUp(Message& m, MouseButtons button, Int32 clicks)
   at System.Windows.Forms.Control.WndProc(Message& m)
   at System.Windows.Forms.ButtonBase.WndProc(Message& m)
   at System.Windows.Forms.Button.WndProc(Message& m)
   at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
   at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
   at System.Windows.Forms.NativeWindow.DebuggableCallback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
   at System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG& msg)
   at System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(IntPtr dwComponentID, Int32 reason, Int32 pvLoopData)
   at System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32 reason, ApplicationContext context)
   at System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32 reason, ApplicationContext context)
   at Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase.OnRun()
   at Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase.DoApplicationModel()
   at Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase.Run(String[] commandLine)
   at WindowsApplication1.My.MyApplication.Main(String[] Args) in :line 81

  This exception was originally thrown at this call stack:
    [External Code]
    WindowsApplication1.frmEmployee.vehiclesearch_Click(Object, System.EventArgs) in frmEmployee.vb
    [External Code]

`

Private Sub vehiclesearch_Click(sender As Object, e As EventArgs) Handles VehicleSearch.Click
    If DbConnect() Then 'This fucntions returns boolean for whether or not database is connecyed
        Dim SQLcmd As New OleDbCommand
        With SQLcmd
            .Connection = cn
            .CommandText = CreateSQLStr() 'Creates command text based on selected combo box items, see below
            .Parameters.AddWithValue("@Make", MakeCBox.SelectedItem)
            .Parameters.AddWithValue("@Model", ModelCBox.SelectedItem)
            .Parameters.AddWithValue("@ClassV", ClassCBox.SelectedItem)
            .Parameters.AddWithValue("@VSeats", SeatsCBox.SelectedItem)
            .Parameters.AddWithValue("@GrBox", GrboxCBox.SelectedItem)
            .Parameters.AddWithValue("@Branch", BranchCBox.SelectedItem)
            .Parameters.AddWithValue("@Status", "ACTIVE")
            Dim rs As OleDbDataReader = .ExecuteReader 'Error occurs at this point
            While rs.Read 'Adding data to data grid
                Dim make, model, year, fuel, vclass, body As String
                make = rs("VMake")
                model = rs("VModel")
                year = rs("VRegYear")
                fuel = rs("VFuel")
                vclass = rs("VClass")
                body = rs("VBody")
                VehDGrid.Rows.Add(make, model, year, fuel, vclass, body)
            End While
            rs.Close()
            cn.Close()
        End With
    Else 'Error message when no vehicle found
        MessageBox.Show("Could not find any vehicles with the selected details. ", "Find vehicle", MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
    End If
End Sub

Function CreateSQLStr() As String
    Dim SQLStr As String
    SQLStr = "Select * FROM Vehicles WHERE (VStatus = @Status)"
    If MakeCBox.SelectedItem IsNot Nothing Then
        SQLStr = SQLStr + " AND (VMake = @Make)"
    End If
    If ModelCBox.SelectedItem IsNot Nothing Then
        SQLStr = SQLStr & " AND (VModel = @Model)"
    End If
    If ClassCBox.SelectedItem IsNot Nothing Then
        SQLStr = SQLStr & " AND (VClass = @ClassV)"
    End If
    If SeatsCBox.SelectedItem IsNot Nothing Then
        SQLStr = SQLStr & " AND (VSeats = @Seats)"
    End If
    If GrboxCBox.SelectedItem IsNot Nothing Then
        SQLStr = SQLStr & " AND (VGearbox = @GrBox)"
    End If
    If BranchCBox.SelectedItem IsNot Nothing Then
        SQLStr = SQLStr & " AND (BranchID = @Branch)"
    End If
    Return SQLStr
End Function
 

编辑:回复评论

 Dim rs As OleDbDataReader = SQLcmd.ExecuteReader

                While rs.Read
                    VehDGrid.Rows.Add(rs("VehicleID"), rs("VMake"), rs("VModel"), rs("VRegYear"),
                                      rs("VFuel"), rs("VClass"), rs("VBody"), rs("VSeats"),
                                      rs("VGearbox"), rs("VReg"), rs("PPDay"), rs("VColour"))
                End While

对于 MS Access,参数的顺序比名称更重要。使用参数时,我在 SQL 命令中使用 ? 占位符。我还指定了数据类型,因此请考虑使用 OleDbParameter Constructor (String, OleDbType) 构造函数。在我的示例中,我使用了 VarChar,您可能希望对其进行相应更改。

我也会考虑实施 Using:

Managed resources are disposed of by the .NET Framework garbage collector (GC) without any extra coding on your part. You do not need a Using block for managed resources. However, you can still use a Using block to force the disposal of a managed resource instead of waiting for the garbage collector.

最后,如果您想根据所选值有条件地更新 WHERE,您应该对参数执行相同的操作,以确保在不需要时也不会添加它们:

If MakeCBox.SelectedItem IsNot Nothing Then
    SQLcmd.Parameters.AddWithValue("@Make", MakeCBox.SelectedItem)
End If

这应该有助于确保您的 SQL 命令符合您的要求。

礼貌地考虑花时间命名您的变量、方法和其他任何更具描述性的东西,例如 CreateSQLStr 变成 GetSelectVehicleCommandString .

我还尝试抽象出方法背后的重复代码,以便您可以调用它并传入参数。它使维护更容易一些,如果您愿意,您可以为这些方法编写单元测试,以确保它们完全按照您的预期进行。这些只是我的想法。我已经举了一个例子来帮助你。希望这一切对您有用:

Private Sub vehiclesearch_Click(sender As Object, e As EventArgs) Handles VehicleSearch.Click
    Dim selectVehicleCommandString = "SELECT * FROM Vehicles WHERE (VStatus = @Status)"

    Using connection As New OleDbConnection(connectionString),
          command As New OleDbCommand(selectVehicleCommandString, connection) 

        command.Parameters.Add(CreateParameter("@Status", OleDbType.VarChar, "ACTIVE"))

        If MakeCBox.SelectedItem IsNot Nothing Then
            selectVehicleCommandString = AppendToWhereClause(selectVehicleCommandString, "VMake")
            command.Parameters.Add(CreateParameter("@Make", OleDbType.VarChar,MakeCBox.SelectedItem))
        End If

        If ModelCBox.SelectedItem IsNot Nothing Then
            selectVehicleCommandString = AppendToWhereClause(selectVehicleCommandString, "VModel")
            command.Parameters.Add(CreateParameter("@Model", OleDbType.VarChar,ModelCBox.SelectedItem))
        End If

        If ClassCBox.SelectedItem IsNot Nothing Then
            selectVehicleCommandString = AppendToWhereClause(selectVehicleCommandString, "VClass")
            command.Parameters.Add(CreateParameter("@ClassV", OleDbType.VarChar,ClassCBox.SelectedItem))
        End If

        If SeatsCBox.SelectedItem IsNot Nothing Then
            selectVehicleCommandString = AppendToWhereClause(selectVehicleCommandString, "VSeats")
            command.Parameters.Add(CreateParameter("@VSeats", OleDbType.VarChar,SeatsCBox.SelectedItem))
        End If

        If GrboxCBox.SelectedItem IsNot Nothing Then
            selectVehicleCommandString = AppendToWhereClause(selectVehicleCommandString, "VGearbox")
            command.Parameters.Add(CreateParameter("@GrBox", OleDbType.VarChar,GrboxCBox.SelectedItem))
        End If

        If BranchCBox.SelectedItem IsNot Nothing Then
            selectVehicleCommandString = AppendToWhereClause(selectVehicleCommandString, "BranchID")
            command.Parameters.Add(CreateParameter("@Branch", OleDbType.VarChar,BranchCBox.SelectedItem))
        End If

        command.CommandText = selectVehicleCommandString

        connection.Open()

        Dim results As New DataTable
        results.Load(command.ExecuteReader())

        VehDGrid.DataSource = results
    End Using
End Sub

Private Function AppendToWhereClause(commandString As String, columnName As String) As String
    Return commandString + $" AND ({columnName} = ?)"
End Function

Private Function CreateParameter(parameterName As String, dataType As OleDbType, value As String)
    Dim parameter As New OleDbParameter(parameterName, dataType) With {
        .Value = value
    }
    Return parameter
End Function

Please note that this code is completely untested as I haven't got the capabilities of running this against an MS Access database. if I've missed something, let me know and I'll look to correct accordingly

我在这里的尝试是尝试给你一些关于如何处理代码的想法:

  1. 有机会就“使用”
  2. 如果有条件地附加到 WHERE 子句,如果值存在,则对参数本身执行相同的操作
  3. 为您的变量名、方法、表单对象提供更具描述性的名称
  4. 移除不必要的变量并简化代码,例如,在向 VehDGrid 添加行时

您不需要循环来归档数据网格。

而且无论连接是打开还是关闭,我都不会弄乱。所有编写的代码都将关闭连接,因此代码基于该假设。

虽然 oleDB 的“@”名称参数无关紧要(对于 Access 数据引擎),但是,您不妨使用它们,因为该代码将来可用于 sql 服务器等.

我也不会单独添加 where 子句,然后单独添加参数。始终同时添加它们。

那样的话,您可以更改代码的顺序,甚至可以剪切 + 粘贴更多的部分,它总是有效的。由于 Access 对参数的顺序很敏感,因此这个建议更有价值(在代码中添加 sql 和参数作为一个组 - 而不是在两个单独的地方。那样的话,你真的不能搞砸了顺序,因为您在代码中配对了这个过程。

如前所述,您不需要循环即可将行添加到数据网格 - 它是一种数据感知代码,因此您只需向其发送数据 table.

例如:

    Using conn As New OleDbConnection(My.Settings.AccessDB)

        conn.Open()

        Using cmdSQL As New OleDbCommand("", conn)

            pAdd(cmdSQL, "VMake", MakeCBox)
            pAdd(cmdSQL, "Vmodel", ModelCBox)
            pAdd(cmdSQL, "VClass", ClassCBox)
            pAdd(cmdSQL, "VSeats", SeatsCBox)
            pAdd(cmdSQL, "VGearBox", GrboxCBox)
            pAdd(cmdSQL, "BranchID", BranchCBox)

            cmdSQL.CommandText &= " AND (VStatus = @VStatus)"
            cmdSQL.Parameters.AddWithValue("@VStatus", "ACTIVE")

            cmdSQL.CommandText = "SELECT * FROM Vehicles WHERE " & cmdSQL.CommandText
            Console.WriteLine(cmdSQL.CommandText)

            Dim rstData As New DataTable
            rstData.Load(cmdSQL.ExecuteReader)

            If rstData.Rows.Count = 0 Then
                MsgBox("no data")
            Else
                VehDGrid.DataSource = rstData
            End If

        End Using
    End Using

End Sub

Sub pAdd(cmd As OleDbCommand, sColumn As String,  c As ComboBox)

    If c.SelectedItem IsNot Nothing AndAlso c.Text <> "" Then

        Dim sP As String = "@" & sColumn
        If cmd.CommandText <> "" Then cmd.CommandText &= " AND "
        cmd.CommandText &= "(" & sColumn & " = " & sP & ")"
        cmd.Parameters.AddWithValue(sP, c.Text)

    End If

End Sub

编辑:关于 My.Settings.AccessDB

的问题

好吧,是桌面版还是网络版?我刚用过这个:

项目->项目属性,然后是:

因此,在上面,您可以单击“...”按钮,这将启动连接生成器。一旦你在上面保存,就可以使用 My.Settings."my setting name here".

中的设置

所以,对于公司名称、头衔或其他什么?您可以构建和创建自己的设置。完成后,这些方便的设置就可以在代码中使用了。