"Procedure or function expects parameter which was not supplied" - vb.net

I've been trying to figure out this bug for a while now, some help would be appreciated. Thanks.
Here is my error message:
Procedure or function 'getAvailableSMSNumbers' expects parameter '#Election_ID', which was not supplied.
Here is my sql code:
CREATE PROCEDURE {databaseOwner}{objectQualifier}getAvailableSMSNumbers
#Election_ID nvarchar(20)
AS
SELECT *
FROM {databaseOwner}{objectQualifier}icc_sms_phones
LEFT JOIN {databaseOwner}{objectQualifier}icc_sms_elections ON sms_elections_sms_number = phones_number
WHERE sms_elections_sms_number IS NULL
OR sms_elections_id = #Election_ID
GO
Function:
Public Overrides Function getAvailableSMSNumbers(eventid As String) As IDataReader
Dim dtable As New DataTable
Using sqlconn As New SqlConnection(Me.ConnectionString)
Using sqlcomm As New SqlCommand
Using sqlda As New SqlDataAdapter
sqlcomm.Connection = sqlconn
sqlcomm.CommandType = CommandType.StoredProcedure sqlcomm.CommandText=GetFullyQualifiedName("getAvailableSMSNumbers")
sqlcomm.Parameters.AddWithValue("#Election_ID", eventid)
sqlda.SelectCommand = sqlcomm
sqlconn.Open()
sqlda.Fill(dtable)
sqlconn.Close()
Return dtable.CreateDataReader
End Using
End Using
End Using
End Function
Where the function is used:
Public Function getAvailableSMSNumbers(eventid As String) As List(Of phoneModel)
Dim numbers As New List(Of phoneModel)
Dim number As phoneModel
numbers = CBO.FillCollection(Of phoneModel)(dal.getAvailableSMSNumbers(eventid))
For Each number In numbers 'dal.getAvailableSMSNumbers(eventid).Rows
number = New phoneModel
With number
.val = ("PHONES_NUMBER").ToString
.text = String.Format("{0:# (###) ###-####}", Long.Parse(.val))
End With
numbers.Add(number)
Next
Return numbers
End Function
If you need anymore information, let me know, and I will add it.

This typically occurs if the object supplied as the value of your SQL parameter is NULL, but the stored procedure does not allow null values (which yours does not). You can set a conditional breakpoint on this line sqlcomm.Parameters.AddWithValue("#Election_ID", eventid) to make sure the eventid parameter is not null.
It might also be a good idea to use defensive coding, and in your getAvailableSMSNumbers function, check to make sure eventid is not null, and if it is, throw an exception or provide some type of feedback for the user.

As an option you can try to re-compile your stored procedure to allow NULL parameter :
CREATE PROCEDURE {databaseOwner}{objectQualifier}getAvailableSMSNumbers
#Election_ID nvarchar(20) = NULL
AS
That means that the default value of your Parameter will be null in case there is no value on input. This solution will be nice in case you want to return empty datatable without error. In any other case you have to debug your VB code and understand where the issue starts.

Think about how you are calling you procedure. When you call you need to supply the value of the procedure: For example,
Call get_particular_girl_from_girlsTable("Jane")
where get_particular_girl_from_girlsTable is the procedure and "Jane" is value for parameter GirlName.

Did you verify if
cmd.CommandType = CommandType.**StoredProcedure**
By default, the value is Text, expecting a SELECT, INSERT or other command text.

Related

How can i resolve ExecucuteNonQuery throwing exception Incorrect syntex near '?'

Dim StrSql = "update student set id=?"
Updated (StrSql,15)
Public Function Updated (ByVal strSql As String, ByVal ParamArray Parameters As String ())
For Each x In Parameters
cmd.Parameters.AddWithValue("?",x)
Next
cmd.ExecuteNonQuery()
End Function
You didn't leave us much to go on; as jmcilhinney points out, you need to add more detail to future questions. For example in this one you have code there that doesn't compile at all, doesnt mention the types of any variable, you don't give the name of the database...
...I'm fairly sure that "Incorrect syntax near" is a SQL Server thing, in which case you need to remember that it (re)uses named parameters, unlike e.g. Access which uses positional ones:
SQL Server:
strSql = "SELECT * FROM person WHERE firstname = #name OR lastname = #name"
...Parameters.AddWithValue("#name", "Lee")
Access:
strSql = "SELECT * FROM person WHERE firstname = ? OR lastname = ?"
...Parameters.AddWithValue("anythingdoesntmatterwillbeignored", "Lee")
...Parameters.AddWithValue("anythingdoesntmatterwillbeignoredalso", "Lee")
This does mean your function will need to get a bit more intelligent; perhaps pass a ParamArray of KeyValuePair(Of String, Object)
Or perhaps you should stop doing this way right now, and switch to using Dapper. Dapper takes your query, applies your parameters and returns you objects if you ask for them:
Using connection as New SqlConnection(...)
Dim p as List(Of Person) = Await connection.QueryAsync(Of Person)( _
"SELECT * FROM person WHERE name = #name", _
New With { .name = "John" } _
)
' use your list of Person objects
End Using
Yep, all that adding parameters BS, and executing the reader, and converting the results to a Person.. Dapper does it all. Nonquery are done like connection.ExecuteAsync("UPDATE person SET name=#n, age=#a WHERE id=#id", New With{ .n="john", .a=27, .id=123 })
http://dapper-tutorial.net
Please turn on Option Strict. This is a 2 part process. First for the current project - In Solution Explorer double click My Project. Choose Compile on the left. In the Option Strict drop-down select ON. Second for future projects - Go to the Tools Menu -> Options -> Projects and Solutions -> VB Defaults. In the Option Strict drop-down select ON. This will save you from bugs at runtime.
Updated(StrSql, 15)
Your Updated Function calls for a String array. 15 is not a string array.
Functions need a datatype for the return.
cmd.Parameters.AddWithValue("?", X)
cmd is not declared.
You can't possible get the error you mention with the above code. It will not even compile, let alone run and produce an error.
It is not very helpful to write a Function that is trying to be generic but is actually very limited.
Let us start with your Update statement.
Dim StrSql = "update student set id=?"
The statement you provided will update every id in the student table to 15. Is that what you intended to do? ID fields are rarely changed. They are meant to uniquely identify a record. Often, they are auto-number fields. An Update command would use an ID field to identify which record to update.
Don't use .AddWithValue. See http://www.dbdelta.com/addwithvalue-is-evil/
and
https://blogs.msmvps.com/jcoehoorn/blog/2014/05/12/can-we-stop-using-addwithvalue-already/
and another one:
https://dba.stackexchange.com/questions/195937/addwithvalue-performance-and-plan-cache-implications
Here is another
https://andrevdm.blogspot.com/2010/12/parameterised-queriesdont-use.html
Since you didn't tell us what database you are using I guessed it was Access because of the question mark. If it is another database change the connection, command and dbType types.
Using...End Using block ensures you connection and command are closed and disposed even if there is an error.
Private ConStr As String = "Your Connection String"
Public Function Updated(StudentNickname As String, StudentID As Integer) As Integer
Dim RetVal As Integer
Using cn As New OleDbConnection(ConStr),
cmd As New OleDbCommand("Update student set NickName = #NickName Where StudentID = #ID", cn)
cmd.Parameters.Add("#NickName", OleDbType.VarChar, 100).Value = StudentNickname
cmd.Parameters.Add("#ID", OleDbType.Integer).Value = StudentID
cn.Open()
RetVal = cmd.ExecuteNonQuery
End Using
Return RetVal
End Function
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
Dim RowsUpdated = Updated("Jim", 15)
Dim message As String
If RowsUpdated = 1 Then
message = "Success"
Else
message = "Failure"
End If
MessageBox.Show(message)
End Sub
This code keeps your database code separated from user interface code.

Option Strict On and DBNull.Value

I've started converting an application of mine to use Option Strict On. I've been doing the CStr,Ctype etc, and it's been going well.
SQLCommand.Parameters.AddWithValue("#TERMINATE", If(IsNothing(txtEarningsTerminated.DateValue), DBNull.Value, txtEarningsTerminated.DateValue))
Then I hit this. The txtEarningsTerminated.DateValue is a custom masked text box. When it's value is Nothing I don't want to store anything in the database. However, it states
Cannot infer a common type, and Option Strict On does not allow 'Object' to be assumed.
When I change DBNull.Value to "" or nothing, the error goes away. However as Nothing, it fails during runtime stating
The parameterized query '(#CONTROL int,#CLIENTCODE nvarchar(10),#NoBill int,#TERMINATE nv' expects the parameter '#TERMINATE', which was not supplied.
I want to put a NULL in the database. This value can be a date and then become a NULL.
How do I translate this so as to not produce an error under Option Strict On?
The reason is because operator If(condition, executeAndReturnIfTrue, executeAndReturnIfFalse) expects that both true and false expressions will return same type.
In your case you return DbNull type if true and String(or some other type you didn't mentioned) if result is false.
If create SqlParameter more explicitly, then you can use next approach:
Dim value As Object = Nothing
If txtEarningsTerminated.DateValue Is Nothing Then
value = DbNull.Value
Else
value = xtEarningsTerminated.DateValue
End If
Dim param As SqlParameter = New SqlParameter With
{
.ParameterName = "#TERMINATE",
.SqlDbType = SqlDbType.VarChar, 'Or use your correct type
.Value = value
}
As mentioned in the comments using AddWithValue will force ADO.NET to decide the sql type automatically for your value, which can lead in different problems. For example everytime you run same query with different values your query plan will be recompiled every time you change value.
You can create extension method for string
Public Module ExtensionsModule
Public Function DbNullIfNothing(this As String) As Object
If this Is Nothing Then Return DBNull.Value
Return this
End Function
End Module
Then use it instead of your "inline If method"
Dim value As String = Nothing
Dim param As SqlParameter = New SqlParameter With
{
.ParameterName = "#TERMINATE",
.SqlDbType = SqlDbType.VarChar,
.Value = value.DbNullIfNothing()
}
So the answer was obvious and in the error. I just had to encase the DBNull.Value in a Ctype Object
CType(DBNull.Value, Object)
And then the code would compile just fine.
Here's one way to do it (assuming you're using stored procedures in your database):
In the stored procedure you're calling, set the date input parameter to equal null, making it an optional field with a default value of null (#terminate DATETIME = NULL).
That way, if you don't include that parameter in your procedure call, the procedure won't fail.
Set the default value of the date field to DateTime.MinValue so it always has some value. Then you can test to see if it is equal to a date other than DateTime.MinValue. If it is a valid date value, add the line to include the date parameter to the procedure call. If it is equal to DateTime.MinValue, don't add the parameter to the call.

ODP.NET OracleCommand optional parameter never getting default value

When I'm calling a function from a PL/SQL package, there is a IN optional parameter (Date) with default value of SYSDATE.
When I execute my commmand, I don't have a choice but to add the parameter. Even if there is no value set, the function always receive null as the entered value, so it never affects the default value.
Dim cmd As New OracleCommand(con)
cmd.CommandType = CommandType.StoredProcedure
cmd.CommandText = "PACK.RefreshData"
Dim param1 As New OracleParameter()
param1.Direction = ParameterDirection.Input
param1.ParameterName = "p_date"
param1.DbType = DbType.Date
cmd.Parameters.Add(param1)
param1 = New OracleParameter()
param1.Direction = ParameterDirection.Output
param1.ParameterName = "po_errorCode"
param1.DbType = DbType.String
cmd.Parameters.Add(param1)
param1 = New OracleParameter()
param1.Direction = ParameterDirection.Output
param1.ParameterName = "po_errorDescription"
param1.DbType = DbType.String
cmd.Parameters.Add(param1)
con.Open()
cmd.ExecuteNonQuery()
In another question, somebody said the command property BindByName set to true works only for regular queries, not storedProcs. So what can I do to call the function without passing a value with my IN parameter?
Here is the Stored Proc header
Procedure RefreshData
(
p_date IN DATE := SYSDATE,
po_errorCode OUT varchar2,
po_errorDescription OUT varchar2
);
N.B. : I'm using Oracle.DataAccess.dll version 4.112.4.0
UPDATE : Here is the explanation taken from the oracle thread, from my answer below :
I think what it boils down to is that DervieParameters is deriving parameters for all of the proc params, even though they have default values, and that seems to me to be the correct behavior. Once you have parameters in the collection, if you don't assign a value to them, null will be passed.
What you probably need to do is
1) the best option, is not to use deriveparameters in the first place, and manually build the parameters collection, adding parameters for only the things you don't want to have default values:
DeriveParameters incurs a database round-trip and should only be used during design time. To avoid unnecessary database round-trips in a production environment, the DeriveParameters method itself should be replaced with the explicit parameter settings that were returned by the DeriveParameters method at design time.
2) If you want to continue using DeriveParameters, remove the unwanted parameters from the OracleParameters collection.
If you have default-value parameter not at the tail of the parameters chain you should perform named parameters call to avoid passing this default-value parameter. I think your data provider doesn't support this call type, so you should wrap your call in anonymous pl\sql block like this :
begin
PACK.RefreshData(po_errorCode => :po_errorCode, po_errorDescription=>:po_errorDescription);
end;
and then do what did before in VB code (ofcourse you CommandType will be Text).
Another solution is to move your default-value parameter to the end of parameters chain like this:
Procedure RefreshData
(
po_errorCode OUT varchar2,
po_errorDescription OUT varchar2,
p_date IN DATE := SYSDATE
);
Finally I have been able to find a way to call the procedure with this thread :
https://community.oracle.com/thread/2248928?tstart=0
Here is my corrected code :
con.Open()
cmd = con.CreateCommand()
cmd.CommandType = CommandType.StoredProcedure
cmd.CommandText = "Pack.RefreshData"
cmd.BindByName = True
OracleCommandBuilder.DeriveParameters(cmd)
cmd.Parameters.RemoveAt(0) 'I remove the optional parameter
cmd.ExecuteNonQuery()
Then I retreive my OUT parameters
Dim outPrm1 as String = ""
If cmd.Parameters("po_errorDescription").Value IsNot Nothing Then
outPrm1 = cmd.Parameters("po_errorDescription").Value
End If

Why doesn't my VB.Net code see the value that my SPROC is returning?

I've written some logic into a sproc that returns either 1 or 0 for true/false. Works great if I run it manually, but if I call it from VB I always get 0/false, no matter what the actual value the sproc returns. Probably missing something silly, but I'm just not seeing it. The code is partially copied from other code in our system that does work properly, but uses a dynamically constructed sproc call. I'm attempting to modify that code to use parameterized sproc calls.
The sproc returns the value in the column named "Allowed".
The VB code (copied and sanitized for variable names/content) looks like:
Public Function sample(ByVal parm1 As String, ByVal parm2 As String) As Boolean
Dim Allowed As Boolean = False
Try
Dim MyConnection As New SqlConnection(ConnString)
Dim MyDataAdapter As New SqlDataAdapter("sproc", MyConnection)
Dim DS As New DataSet
MyDataAdapter.SelectCommand.CommandType = CommandType.StoredProcedure
MyDataAdapter.SelectCommand.Parameters.Add(New SqlParameter("#parm1", SqlDbType.Char, 14))
MyDataAdapter.SelectCommand.Parameters("#parm1").Value = parm1
MyDataAdapter.SelectCommand.Parameters.Add(New SqlParameter("#parm2", SqlDbType.Char, 30))
MyDataAdapter.SelectCommand.Parameters("#parm2").Value = parm2.ToUpper()
MyDataAdapter.Fill(DS)
If DS.Tables.Count > 0 Then
If DS.Tables(0).Rows.Count > 0 Then
Allowed = Convert.ToBoolean(DS.Tables(0).Rows(0).Item("Allowed"))
End If
End If
Catch ex As Exception
' Real code has error handling here that's not getting hit
End Try
Return Allowed
End Function
if you are only returning a true/false you should use execute scalar. there is a good example here
http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlcommand.executescalar.aspx?cs-save-lang=1&cs-lang=vb#code-snippet-3
I would make sure to cast your value in the sproc as a bit something like:
select cast(1 as bit)
Select 1 and select Cast(1 as bit) will be two different things.

How do you use ExecuteScalar to return a single value from an Oracle Database?

Been using the code below to return a single record from the database. I have read that ExecuteScalar is the right way to return a single record. I have never been able to get ExecuteScalar to work though. How would I change this to return a single value in VB.Net using ExecuteScalar?
Dim oracleConnection As New OracleConnection
oracleConnection.ConnectionString = LocalConnectionString()
Dim cmd As New OracleCommand()
Dim o racleDataAdapter As New OracleClient.OracleDataAdapter
cmd.Connection = oracleConnection
cmd.CommandText = "FALCON.CMS_DATA.GET_MAX_CMS_TH"
cmd.CommandType = CommandType.StoredProcedure
cmd.Parameters.Add(New OracleParameter("i_FACID_C", OracleType.Char)).Value = facilityShortName
cmd.Parameters.Add(New OracleParameter("RS_MAX", OracleType.Cursor)).Direction = ParameterDirection.Output
Try
Using oracleConnection
oracleConnection.Open()
Using oracleDataAdapter
oracleDataAdapter = New OracleClient.OracleDataAdapter(cmd)
Dim workingDataSet As DataSet
oracleDataAdapter.TableMappings.Add("OutputSH", "RS_MAX")
workingDataSet = New DataSet
oracleDataAdapter.Fill(workingDataSet)
For Each row As DataRow In workingDataSet.Tables(0).Rows
Return CDate(row("MAXDATE"))
Next
End Using
End Using
From Microsoft
"The ExecuteOracleScalar() method of the OracleCommand class is used to execute a SQL statement or stored procedure that returns a single value as an OracleType data type. If the command returns a result set, the method returns the value of the first column of the first row. The method returns a null reference if a REF CURSOR is returned rather than the value of the first column of the first row to which the REF CURSOR points. The ExecuteScalar() method of the OracleCommand class is similar to the ExecuteOracleScalar() method, except it returns a value as a .NET Framework data type.
Having said that, neither of these methods is useful when working with Oracle stored procedures. Oracle stored procedures cannot return a value as part of the RETURN statement, only as OUT parameters—see the Stored Procedures That Do Not Return Data section. Also, you cannot return a result set except through a REF CURSOR output parameter—this is discussed in the next section.
You can retrieve the return value for an Oracle function only by using a RETURN parameter (shown in the previous section) and not by using the one of the ExecuteScalar methods."
http://msdn.microsoft.com/en-us/library/ms971506.aspx
ExecuteScalar returns a single value (scalar) not a record.
not sure why the other answer is marked as accepted, as it doesn't appear to answer your question
How would I change this to return a single value in VB.Net using ExecuteScaler
ExecuteScalar will only return a Single Value - so keep that in mind when writing the query portion of your command.
The code to accomplish this would be as follows:
oracleConnection.Open
Dim obj as object 'Object to hold our return value
obj = cmd.ExecuteScalar()
oracleConnection.Close
If obj IsNot Nothing then
Return CDate(obj)
end if