How to pass the parameter to stored procedure - vb.net

Using Crystal Report 9 and VB.Net
Report is running through stored procedure, I want to pass the data to stored procedure.
How to do it?
In VB6, i did like this.
Report.ReportFileName = REPPATH & "\Detail.rpt"
Report.StoredProcParam(0) = Did
Report.StoredProcParam(1) = Deed
Report.Action = 1
How to do it in vb.net?

I think something like the following should work:
SetDataSource have like 4 overrides that all take an IEnumerable like object as parameter. But this has been done with CR 13 for VS 2010... I hope you will find something like with CR 9.
Dim report As New CrystalReport1
Dim sqla = New SqlDataAdapter()
sqla.SelectCommand.Connection = New SqlConnection(sConnectionString)
sqla.SelectCommand = New SqlCommand("EXEC storedProcName #a, #b, #c")
sqla.SelectCommand.Parameters.Add("#a", paramA)
sqla.SelectCommand.Parameters.Add("#a", paramB)
sqla.SelectCommand.Parameters.Add("#a", paramC)
report.SetDataSource(sqla)
//'If you do not have parameters, you may use :
report.SetDataSource(New SqlDataAdapter("EXEC storedProcName ", sConnectionString))
report.Refresh()
EDIT : sqla.SelectCommand.Parameters.Add("#a", paramA) is depreciated in the CR13 version. sqla.SelectCommand.Parameters.AddWithValue("#a", paramA) is used instead.

Related

Why are my database Commands always calling sp_describe_first_result_set?

Background
I'm maintaining a VB.Net (formerly VB6) utility that exports records to a flat file. One of our customers reported that the new version was taking a long time to run, and digging into the trace it was easy to see why: (I've obfuscated this slightly)
exec [sys].sp_describe_first_result_set
N'Update [MAIN].[dbo].[Inventory] Set ExportId = #P1 Where Comp = #P2 and Osite = #P3 and Key = #P4',
N'#P1 numeric(10),#P2 varchar(6),#P3 varchar(6),#P4 int',1
This and statements like it were taking half a second each. The utility has to update the main inventory table with some information about the export, and the table is heavily triggered, so sp_describe_first_result_set has to simulate all the triggers in order to determine the result - I can verify that later down in the trace.
Problem
I can't figure out why exactly my code is calling sp_describe_first_result_set for an update statement, a thing that doesn't even have a result set. The command setup doesn't look like it's doing anything weird:
Dim connection = New ADODB.Connection
connection.ConnectionString = config.AdoConnectionString
connection.CursorLocation = CursorLocationEnum.adUseClient
connection.Open()
cmd = New ADODB.Command
With cmd
.ActiveConnection = connection
.CommandType = CommandTypeEnum.adCmdText
.CommandText = "Update [MAIN].[dbo].[Inventory] " &
"Set ExportId = ? " &
"Where Comp = ? and Osite = ? and Key = ?"
.Parameters.Append(NumericParameter(cmd.CreateParameter("ExportId", DataTypeEnum.adNumeric, ParameterDirectionEnum.adParamInput), 10, 0))
.Parameters.Append(cmd.CreateParameter("Comp", DataTypeEnum.adVarChar, ParameterDirectionEnum.adParamInput, 6))
.Parameters.Append(cmd.CreateParameter("Osite", DataTypeEnum.adVarChar, ParameterDirectionEnum.adParamInput, 6))
.Parameters.Append(cmd.CreateParameter("Key", DataTypeEnum.adInteger, ParameterDirectionEnum.adParamInput))
End With
cmd.Parameters("ExportID").Value = lExportId
cmd.Parameters("Comp").Value = sComp
cmd.Parameters("Osite").Value = sOsite
cmd.Parameters("Key").Value = iKey
cmd.Execute()
Is there some setting I'm just missing that's making it run sp_describe_first_result_set all the time? Is there a way to stop it from doing that?
You need to explicitly say you don't want a recordset.
In VB6, an ADODB Command object can tell whether or not it should return a recordset based on whether the value of the Execute() function is being assigned to anything.
In VB.NET, this is no longer done, but execution options flags can be provided as arguments to the Execute() function. Calling the command like this:
cmd.Parameters("ExportID").Value = lExportId
cmd.Parameters("Comp").Value = sComp
cmd.Parameters("Osite").Value = sOsite
cmd.Parameters("Key").Value = iKey
cmd.Execute(Options:=ExecuteOptionEnum.adExecuteNoRecords)
will not call sp_describe_first_result_set to establish the parameters of the record set, but will only execute the command directly, the same way more modern methods such as SqlCommand.ExecuteNonQuery() will.
Out of curiosity, does the modern equivalent to that code also trace as performing this op?
Dim cmd as New SqlCommand( _
"Update [MAIN].[dbo].[Inventory] Set ExportId = #e Where Comp = #c and Osite = #o and Key = #k",
"Data Source=YOUR_SERVER;Initial Catalog=YOUR_DB;User ID=YOUR_USER_EG_sa;Password=YOUR_PASSWORD" _
)
cmd.Connection.Open()
cmd.Parameters.AddWithValue("#e", lExportId)
cmd.Parameters.AddWithValue("#c", sComp)
cmd.Parameters.AddWithValue("#o", sOsite)
cmd.Parameters.AddWithValue("#k", iKey)
cmd.ExecuteNonQuery()
I use AddWithValue for testing/convenience purposes here, but you should probably avoid it in prod because it can cause performance issues with SQLS - see that link for advice on how to craft parameters properly

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.

Using the New List command with a Passed Parameter

I'm trying to Pass a Field Parameter from my form textbox to a Function to create a New List object from the Data Table parameter I'm passing.
In the following code, the first tmpReadTable shows with no syntax error, but when I try to use the Parm with the Datatable name I'm not sure what I'm missing syntax wise. I'm new to this, thanks in advance!
Updated code below:
Thank you for all the helpful replies...sorry I'm not more experienced, I'm coming from a Visual Foxpro background.
To summarize:
I want to pass in my IMPORT table parameters from my form.
The cImportTable is an empty SQL Table to use to import and validate each CSV file row.
I found this example in Murach's VB book but he leaves out how the LIST is being created from a PRODUCTS table in an earlier exercise. So I thought I could just substitute my passed cImportTable to do the same...that's where I'm stuck and maybe you all know of a better way.
Private Function ReadImportFile(ByVal cImportFile As String, ByVal cGroupID As String, ByVal cControlTable As String, ByVal cImportTable As String)
MessageBox.Show(cImportFile + " " + cGroupID + " " + cControlTable)
If Not File.Exists(cImportFile) Then
MessageBox.Show("File: " + cImportFile + " does not exist - cancelling process.")
Return False
End If
Dim curFileStream As New StreamReader(New FileStream(cImportFile, FileMode.Open, FileAccess.Read))
Dim curImportTable = "NewDataSet." + cImportTable
'Here I'm trying to create a LIST or DATASET using my Empty SQL Import Table and read in each row of the CSV file in the DO WHILE loop
'...I'm coming from Visual Foxpro background so am not sure what I'm missing or what is the standard procedure to do this simple task.
'This line gives me a syntax issue - and I'm not even sure what it's suppose to do, I'm taking it from Murach's VB book example,
'but he leaves out this vital piece of how to create this LIST from a Datatable - or if it's even the right method to use.
Dim tmpReadTable = New List(Of curImportTable)
Do While curFileStream.Peek <> -1
Dim row As String = curFileStream.ReadLine
Dim columns() As String = row.Split(",")
Dim ImportRecord As New curImportTable
ImportRecord.GroupId = columns(0)
ImportRecord.MemberId = columns(1)
Loop
'More Processing after Importing CSV file.....
curFileStream.Close()
'If lNoErrors
Return True
End Function
You are using a variable instead of TYPE on the code line #3 here
' This seems to be ok, no syntax error
Dim tmpReadTable = New List(Of NewDataSet.FO_ImportDataTable)
' The variable below implicitely will be of STRING type
Dim curImportTable = "NewDataSet." + cImportTable.ToString
' This line is not going to work
Dim tmpReadTable = New List(Of curImportTable)
' BUT THIS WILL
Dim x = New List(Of String)
Another issue is that Dim tmpReadTable happened twice in your code! can't re-declare variable. On top you declared it as NewDataSet.FO_ImportDataTable
Besides, I recommend declare all variables like Dim curImportTable as String, this way you can recognize types easier. Option Infer is good when you use anonymous types, LINQ, etc

"Procedure or function expects parameter which was not supplied"

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.

An SqlParameter with ParameterName is not contained?

I have a problem with something I have done many times but this time it just doesn't work.
This is what what I am trying to do (in Visual Studio 2003 and VB.NET)
Earlier in the code:
Private SaveCustomerInformationCommand As System.Data.SqlClient.SqlCommand
Then this in a setup process:
SaveCustomerInformationCommand = New System.Data.SqlClient.SqlCommand("spSaveCustomerInformation")
SaveCustomerInformationCommand.CommandType = Data.CommandType.StoredProcedure
Dim custIdParameter As System.Data.SqlClient.SqlParameter = SaveCustomerInformationCommand.CreateParameter()
custIdParameter.ParameterName = "#CustId"
custIdParameter.SqlDbType = Data.SqlDbType.Int
custIdParameter.Direction = Data.ParameterDirection.Input
SaveCustomerInformationCommand.Parameters.Add(custIdParameter)
And then later:
SaveCustomerInformationCommand.Parameters.Item("#CustId").Value = myValue
And I get this:
System.IndexOutOfRangeException: An SqlParameter with ParameterName '#CustId' is not contained by this SqlParameterCollection.
Any ideas/solutions?
AFAIK, the "#" is not technically part of the name of the parameter... rather it's what you put into your T-SQL to denote that a parameter name is coming afterwards. So I think you'll want to refer to it like this instead (with no "#") :
SaveCustomerInformationCommand.Parameters.Item("CustId").Value = myValue
You could also try the same thing when you initially insert the parameter name-- although since you're not getting an error there, I'd suspect the accessor call is to blame, not the inserter call.