How to get a return value from a stored procedure in VB.NET - sql

I have a stored procedure in SQL Server for generating transaction numbers.
Can anyone help me with how to call the Stored Procedure from VB.NET and how will i get the value that is returned from the procedure into the front end.
Regards,
George

I think you want something like this:
Public Sub Foo()
Using sql As New SqlClient.SqlConnection("YourConnection")
sql.Open()
Using cmd As New SqlClient.SqlCommand("YourSPName", sql)
cmd.CommandType = CommandType.StoredProcedure
Dim myReturnValue As String = cmd.ExecuteScalar
End Using
End Using
End Sub
Where myReturnValue will be what ever your output param in SQL is.

What kind of value is it you're returning? Will that value in turn result in another database action?
It might be best to return data instead of a single value.
For example if you were verifying the username and password for a potential login, instead of returning a simple true or false you would return the users information. No information returned means failed login.
This method has the advantage of minimising database requests, something which will have a serious effect if it is a common action.
Personally I've never needed to return a single value.

Related

Specified cast is not valid with datareader

Hi friends of stackoverflow,
I'm writing a question here because i'm having problems to detect why sometimes a read to a field of a datareader returns a invalid cast exception.
I will give all information possible to understand my situation. I'm working with ASP.NET 3.5
I have a Module that have a function that returns a IDataReader and receives a sql query. something like this
function get_dr(query as String) as IDataReader
dim connection = new SqlClient.SqlConnection("connection string")
connection.open()
dim command = connection.createCommand
command.commandText = query
dim reader = command.executeReader(CommandBehavior.CloseConnection)
return reader
end function
I have a Class with a Shared function that recovers a new dataReader and returns a date. something like this:
public shared function getDate() as Date
using dr = get_dr("SELECT dbo.getDate()")
if dr.read() and dr(0) isnot dbnull.value then
return dr.GetDateTime(0)
end if
end using
end function
when in another code i call the getDate() function, it gives me a call stack like this.
System.InvalidCastException: Specified cast is not valid.
at System.Data.SqlClient.SqlBuffer.get_DateTime()
at System.Data.SqlClient.SqlDataReader.GetDateTime(Int32 i)
Why sometimes i'm getting this error? i was thinking this is because that a lot of users is calling this function in conjunction with another functions of my application (those functions eventually uses get_dr too), mixing the data of the dataReader on another executions, but i need to know if im doing something wrong or maybe to do something better.
Notes:
dbo.getDate is a sql function that ALWAYS returns a date.
don't worry about bad writing code, those are only examples but they have the necessary to understand the scenario.
sorry for my bad english
Really thanks in advance.
One possible reason - you declare connection inside of the function that returns DataReader. When you're out of the function that connection goes out of scope. That means that at some unpredictable point (depends on memory usage etc.) Garbage Collector will collect it. If you try to use the DataReader at that point - all bets are off.
One way to solve it is to declare connection outside of function get_dr and pass it there as a parameter. But also seeing that you're returning a single value and if you don't plan to use the reader for multiple values I suggest using ExecuteScalar instead - it will save you a lot of headaches.

Data is Null when trying to run basic stored procedure

I created this simple test for reading data from a stored procedure. I must be overlooking something obvious, because I can't figure out why I'm continuing to get no data.
myReader.HasRows is always true.
I noticed that it is returning the correct number of rows, but I am having trouble getting at any data that might be in the rows.
Stored Procedure
ALTER PROCEDURE [dbo].[testProc]
-- Add the parameters for the stored procedure here
#carID uniqueidentifier
AS
BEGIN
SELECT * FROM carTable WHERE carID=#carID
END
VB.Net
Dim cmd As New SqlClient.SqlCommand("testProc", _conn)
cmd.CommandType = CommandType.StoredProcedure
cmd.Parameters.AddWithValue("carID", carID)
_conn.Open()
Dim myReader As SqlDataReader
myReader = cmd.ExecuteReader()
If myReader.HasRows = True Then
While (myReader.Read())
If Not IsDBNull(myReader.GetString(0)) Then
' do stuff here
End If
End While
End If
From MSDN about SqlDataReader.GetString Method method:
No conversions are performed; therefore, the data retrieved must
already be a string.
Call IsDBNull to check for null values before
calling this method.
My guess is IsDBNull.myReader.GetString(0) returns False because CarId is not a String column (it's uniqueidentifier).
Instead, to test is the value is null, try to use the SqlDataReader.IsDBNull Method, which apply to any datatype.
If Not myReader.IsDBNull(0) Then
First, try running the query in your management studio with the supplied car ID to see what results it is passing back. Secondly I would try to change the if statement as follows and see if you still have issues:
If Not myReader.IsDBNull(0) Then
Additionally, all subsequent fields that allow NULL values must be checked for IsDBNull before they are used. This may also cause some issues.

UPDATE statement in Oracle

We are building a client program where parameters for storage in a web server with Oracle backend are set in the .Net client program and uploaded as a dataset via webservice.
In the webservice code, data is read from the dataset and added to UPDATE statements on the web server (Oracle backend).
Because the server will run on the customer's LAN behind a firewall and because of the dynamic nature of the parameters involved, no sprocs are being used - SQL strings are built in the logic.
Here is an example string:
UPDATE WorkOrders
SET TravelTimeHours = :TravelTimeHours,
TravelTimeMinutes = :TravelTimeMinutes,
WorkTimeHours = :WorkTimeHours,
WorkTimeMinutes = :WorkTimeMinutes,
CompletedPersonID = :CompletedPersonID,
CompletedPersonName = :CompletedPersonName,
CompleteDate = :CompleteDate
WHERE WorkOrderNumber = :WorkOrderNumber
When debugging code in VS 2010 and stepping into the server code, we receive the following error:
ORA-01036: illegal variable name/number
when executing the SQL command on destination oracle machine, we were prompted to enter the bind
variables for the above statement, and as long as we used the correct date format, the UPDATE statement
worked correctly.
QUESTIONS:
1) is it possible that oracle threw the ORA-01036 error when the month format was wrong?
2) why don't we have to convert the date format from the ASP.net website running on the Oracle machine?
does Oracle have a default conversion routine that excludes the bind variable entry screen?
3) if the date format was not the problem, what precisely does ORA-1036 mean and how do I discover
WHICH variable had an illegal name/number?
This is a snippet of a function that takes the type of the dataset (WOName) and returns the appropriate SQL string.
Many Cases exist but have been removed for readability.
Private Function GetMainSQLString(ByVal WOName As String) As String
Dim Result As String = ""
Select Case WOName
Case "Monthly Site Inspection"
Dim sb As New StringBuilder
sb.Append("UPDATE WorkOrders SET ")
sb.Append("CompletedPersonID = :CompletedPersonID, CompletedPersonName = :CompletedPersonName, CompleteDate = :CompleteDate, ")
sb.Append("SupervisorID = :SupervisorID, SupervisorName = :SupervisorName ")
sb.Append("WHERE WorkOrderNumber = :WorkOrderNumber")
Result = sb.ToString
End Select
Return Result
End Function
This is a snippet of a function that takes the Oracle command object byRef and adds the required parameters to it,
depending upon which of the possible 15 types of dataset(WOName) is received from the client program.
Many Cases exist but have been removed for readability.
The updated Cmd object is then returned to the main program logic, where ExecuteNonQuery() is called.
The test values of params below are as follows:
dr.Item("CompletedPersonID") 21
dr.Item("CompletedPersonName") Pers Name
dr.Item("CompleteDate") #8/16/2010#
dr.Item("SupervisorID") 24
dr.Item("SupervisorName") Sup Name
dr.Item("WorkOrderNumber") 100816101830
Private Function addMainCmdParams(ByVal WOName As String, ByRef cmd As OracleCommand, ByVal dr As DataRow) As OracleCommand
Select Case WOName
Case "Monthly Site Inspection"
cmd.Parameters.Add(":CompletedPersonID", Oracle.DataAccess.Client.OracleDbType.Int32).Value = dr.Item("CompletedPersonID")
cmd.Parameters.Add(":CompletedPersonName", Oracle.DataAccess.Client.OracleDbType.Varchar2).Value = dr.Item("CompletedPersonName")
cmd.Parameters.Add(":CompleteDate", Oracle.DataAccess.Client.OracleDbType.Date).Value = dr.Item("CompleteDate")
cmd.Parameters.Add(":SupervisorID", Oracle.DataAccess.Client.OracleDbType.Int32).Value = dr.Item("SupervisorID")
cmd.Parameters.Add(":SupervisorName", Oracle.DataAccess.Client.OracleDbType.Varchar2).Value = dr.Item("SupervisorName")
cmd.Parameters.Add(":WorkOrderNumber", Oracle.DataAccess.Client.OracleDbType.Varchar2).Value = dr.Item("WorkOrderNumber")
End Select
Return cmd
End Function
While running this today, this precise code WAS successful; but another similar case was not. I still distrust any implicit typecasting performed by Oracle (if any) - and I'm especially suspicious of how Oracle handles any of these parameters that are passed with a dbNull.value - and I know it's going to happen. so if that's the problem I'll have to work around it. There are too many optional parameters and columns that don't always get values passed in for this system to break on nulls.
One Oracle "gotcha" that can cause this error is the fact that, by default, Oracle maps parameters to parameter symbols in the query by sequence, not by name. If the number/type of parameters does not match, you get an error like this one.
The solution is to tell Oracle to bind by name:
cmd.BindByName = true
Without diving into the details of your code, this may or may not be the answer to your specific problem, but this setting should be the default, and should be part of any command setup that uses parameters. It's rather amazing to watch this one statement fix some obscure problems.
EDIT: This assumes that you're using Oracle's data access provider. In .NET, you should be using this, not Microsoft's Oracle provider.
The error has nothing to do with date formats, it means that a variable in the statement was not bound.
Could be as simple as a spelling mistake (would be nice if Oracle included the variable name in the error message).
Can you update your question with the surrounding code that creates, binds, and executes the statement?
This is a snippet of a function that takes the type of the dataset (WOName) and returns the appropriate SQL string.
Many Cases exist but have been removed for readability.
Private Function GetMainSQLString(ByVal WOName As String) As String
Dim Result As String = ""
Select Case WOName
Case "Monthly Site Inspection"
Dim sb As New StringBuilder
sb.Append("UPDATE WorkOrders SET ")
sb.Append("CompletedPersonID = :CompletedPersonID, CompletedPersonName = :CompletedPersonName, CompleteDate = :CompleteDate, ")
sb.Append("SupervisorID = :SupervisorID, SupervisorName = :SupervisorName ")
sb.Append("WHERE WorkOrderNumber = :WorkOrderNumber")
Result = sb.ToString
End Select
Return Result
End Function
This is a snippet of a function that takes the Oracle command object byRef and adds the required parameters to it,
depending upon which of the possible 15 types of dataset(WOName) is received from the client program.
Many Cases exist but have been removed for readability.
The updated Cmd object is then returned to the main program logic, where ExecuteNonQuery() is called.
The test values of params below are as follows:
dr.Item("CompletedPersonID") 21
dr.Item("CompletedPersonName") Pers Name
dr.Item("CompleteDate") #8/16/2010#
dr.Item("SupervisorID") 24
dr.Item("SupervisorName") Sup Name
dr.Item("WorkOrderNumber") 100816101830
Private Function addMainCmdParams(ByVal WOName As String, ByRef cmd As OracleCommand, ByVal dr As DataRow) As OracleCommand
Select Case WOName
Case "Monthly Site Inspection"
cmd.Parameters.Add(":CompletedPersonID", Oracle.DataAccess.Client.OracleDbType.Int32).Value = dr.Item("CompletedPersonID")
cmd.Parameters.Add(":CompletedPersonName", Oracle.DataAccess.Client.OracleDbType.Varchar2).Value = dr.Item("CompletedPersonName")
cmd.Parameters.Add(":CompleteDate", Oracle.DataAccess.Client.OracleDbType.Date).Value = dr.Item("CompleteDate")
cmd.Parameters.Add(":SupervisorID", Oracle.DataAccess.Client.OracleDbType.Int32).Value = dr.Item("SupervisorID")
cmd.Parameters.Add(":SupervisorName", Oracle.DataAccess.Client.OracleDbType.Varchar2).Value = dr.Item("SupervisorName")
cmd.Parameters.Add(":WorkOrderNumber", Oracle.DataAccess.Client.OracleDbType.Varchar2).Value = dr.Item("WorkOrderNumber")
End Select
Return cmd
End Function
While running this today, this precise code WAS successful; but another similar case was not. I still distrust any implicit typecasting performed by Oracle (if any) - and I'm especially suspicious of how Oracle handles any of these parameters that are passed with a dbNull.value - and I know it's going to happen. so if that's the problem I'll have to work around it. There are too many optional parameters and columns that don't always get values passed in for this system to break on nulls.

Please help prevent data layer refactoring of this ODP.NET code and transactions

I am using Oracle 11g client, with ODP.NET. I am trying to add conditional Transaction handling.
Dim ds As New DataSet()
Dim txn As OracleTransaction
Dim _beginTransaction as Bolean = true
Using conn As New OracleConnection(ConnString)
Try
conn.Open()
If _beginTransaction Then
txn = conn.BeginTransaction(IsolationLevel.Serializable)
End If
Dim adapter As OracleDataAdapter = New OracleDataAdapter()
adapter.SelectCommand = New OracleCommand(sSQL, conn)
For i As Integer = 0 To UBound(parameters, 1)
adapter.SelectCommand.Parameters.Add(parameters(i))
Next
adapter.Fill(ds)
If _beginTransaction Then
txn.Commit() //txn is undefined here? why?
End If
Catch e As Exception
txn.Rollback()
End Try
End Using
How do I fix txn being nothing / null? The error is: Variable 'txn' is used before it has been assigned a value. A null reference exception could result at runtime. Links or pointers to solutions would be appreciated also.
Edit: Thanks to RichardOD for pointing out that you can not explicitly declare that a transaction cannot be opend up on stored procedures via ODP.NET. I have verified that this is an issue. BUT We still haven't figured out why the error is occuring. I understand that txn is initially given a value w/in an if statement, but being defined outside of the try/catch block should make that irrelevant.... right? Or is that bad coding?
Assuming _beginTransaction is a boolean have you set it to true before If _beginTransaction Then ?
Also have you committed the previous transaction before starting this one? Oracle can do weird stuff with connection pooling and BeingTransaction.
A long time ago I had a bug similar to this. Have you looked here?
Edit- are you trying to call a .NET stored proc? OracleConnection.BeginTransaction does not support stored procedure calls:
OracleConnection.BeginTransaction is
not allowed for .NET stored procedure
Question: Is it null immediately after being asigned? And if not, when does it become null? If it's null after immediately, it might be the connection pooling stuff. If after getting the adapter from conn or after filling it, then it's even crazier...
But I would try and find out
Oracle does not require a transaction for selecting data. Why do you try open one?
EDIT:
If your vb code is called from oracle (via .net integration) than there is no transaction support as RichardOD wrote. Please clarify the environment.
The sql statement executed is dynamic and given in sSQL. The command is prepared and given to a DataAdapter that fills a DataSet. Than you can only execute SELECT statements. Otherwise there is no result.
OR
Because the parameters are prepared too. You are calling a stored procedure (without telling the the CommandType is StoredProcedure). One of your parameters is a ref cursor parameter which will fetched into the DataSet. Right?
Oracle does not need explicit transactions as sql server does. Oracle starts an implicit transaction with the first dml statement in your session. The sideeffect is, if you did not start an transaction you cannot commit the implicit transaction. I do not know if there is access to implicit transaction via the connection object.

Doing a lot of input validation in VB.NET

I have a form set up where users can enter their booking for a room at my college. I want to validate the user input to avoid SQL injection (my program uses a MS Access database) and also stop numbers and synbols in their name, etc.
I can do the validation fine, but there is to be a lot of validation and then methods executed only if all validation tests come back as true. I did have something like this:
If txtName.Text = "" Then
frmBookErr.SetError(txtName, "Name field cannot be left blank.")
fail = 1
Else
frmBookErr.SetError(txtName, "")
fail = 0
End If
And then check the fail variable, but it obviously gets overridden later in the form if one of the validation tests come back as true.
Can anyone provide some input into this? Thanks.
If you want to avoid SQL injection, use parameterised SQL queries or stored procedures, and do not construct SQL by concatenation.
Set your fail variable at the start of the procedure to 0 then only set it to 1 if something fails...
fail = 0
If txtName.Text = "" Then
frmBookErr.SetError(txtName, "Name field cannot be left blank.")
fail = 1
End If
If txtSomethingElse.Text = String.Empty Then fail = 1
If fail = 0 Then frmBookErr.Clear()
To avoid SQL Injections you need to use something that doesn't directly allow for changes in the SQL Query.
Now this doesn't mean you cant provide values, it means that you strongly specifies what types you want to process to the server.
Example from CodeProject
string commandText = "SELECT * FROM Customers "+
"WHERE Country=#CountryName";
SqlCommand cmd = new SqlCommand(commandText, conn);
cmd.Parameters.Add("#CountryName",countryName);
Instead of providing the countrName as concated string, you actually tell your Sql Command that you provide it as a parameter, which wont allow for any changes in the query itself.