Exception from Stored Procedure not caught in .NET using SqlDataAdapter.Fill(DataTable) - vb.net

I have a Stored Procedure that I am executing from VB.NET. The SP should insert records into a table and return a set to the calling app. The set returned are the records that were inserted.
If the INSERT fails, the exception is caught and re-thrown in the SP, but I never see the exception in my application. The severity level is 14, so I should see it.
Here is the stored procedure:
BEGIN TRY
BEGIN TRANSACTION
-- Declare local variables
DECLARE #DefaultCategoryID AS BIGINT = 1 -- 1 = 'Default Category' (which means no category)
DECLARE #DefaultWeight AS DECIMAL(18,6) = 0
DECLARE #InsertionDate AS DATETIME2(7) = GETDATE()
DECLARE #SendToWebsite AS BIT = 0 -- 0 = 'NO'
DECLARE #MagentoPartTypeID AS BIGINT = 1 -- For now, this is the only part type we are importing from COPICS ('simple' part type)
DECLARE #NotUploaded_PartStatusID AS TINYINT = 0 -- 0 = 'Not Uploaded'
DECLARE #Enabled_PartStatusID AS TINYINT = 1 -- 1 = 'Enabled'
DECLARE #Disabled_PartStatusID AS TINYINT = 2 -- 2 = 'Disabled'
-- Get the part numbers that will be inserted (this set will be returned to calling procedure).
SELECT c.PartNumber
FROM
COPICSPartFile c
LEFT JOIN Part p on c.PartNumber = p.PartNumber
WHERE
p.PartNumber IS NULL
-- Insert new records from COPICSPartFile (records that don't exist - by PartNumber - in Part table)
INSERT INTO Part
([PartNumber]
,[ReplacementPartNumber]
,[ShortDescription]
,[ListPrice]
,[PartStatusTypeID]
,[Weight]
,[CategoryID]
,[DateInserted]
,[SendToWebsite]
,[FileName]
,[MagentoPartTypeID]
,[PrintNumber])
SELECT
c.PartNumber
,c.ReplacementPartNumber
,c.ShortDescription
,c.ListPrice
,CASE WHEN c.PartStatusTypeID = #Enabled_PartStatusID THEN #NotUploaded_PartStatusID ELSE #Disabled_PartStatusID END
,#DefaultWeight
,#DefaultCategoryID
,#InsertionDate
,#SendToWebsite
,#FileName
,#MagentoPartTypeID
,c.PrintNumber
FROM
COPICSPartFile c
LEFT JOIN Part p on c.PartNumber = p.PartNumber
WHERE
p.PartNumber IS NULL
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
IF ##TRANCOUNT > 0
ROLLBACK TRANSACTION;
THROW;
END CATCH
And here is the .net code:
Try
'Create command
Dim command As New SqlCommand
command.CommandType = CommandType.StoredProcedure
conn = New SqlConnection(m_ConnectionString)
command.Connection = conn
command.CommandText = "trxInsertPartFromCOPICSPartFile"
With command.Parameters
.AddWithValue("#FileName", fileName)
End With
Dim da As New SqlDataAdapter(command)
Dim dt As New DataTable
da.Fill(dt)
If dt.Rows.Count > 0 Then
Return dt
Else
Return Nothing
End If
Catch ex As SqlException
Dim myMessage As String = ex.Message
Finally
If conn.State <> ConnectionState.Closed Then
conn.Close()
End If
End Try
As I was trying to figure out why the exception (duplicate key) wasn't being caught in my application, I tried commenting out the SELECT statement in the SP just before the INSERT and voila. The exception from the INSERT is caught in the application.
Can someone explain to me why the SELECT statement causes this? I know I can break out the SELECT into another SP, but I'd like to keep it all one atomic transaction if possible. Is this expected behavior? Is there a way around it?
Thanks.

The exception is being swallowed by the Fill method. Instead of using that method, create a SqlDataReader, do a command.ExecuteReader(), and then use the reader to populate the DataTable via Load(). This way the error should occur in the ExecuteReader() method and should be catchable. And then you shouldn't need the SqlDataAdapter.
Try
'Create command
Dim command As New SqlCommand
command.CommandType = CommandType.StoredProcedure
conn = New SqlConnection(m_ConnectionString)
command.Connection = conn
command.CommandText = "trxInsertPartFromCOPICSPartFile"
With command.Parameters
.AddWithValue("#FileName", fileName)
End With
Dim dt As New DataTable
conn.Open()
Dim reader As SqlDataReader = command.ExecuteReader()
dt.Load(reader)
If dt.Rows.Count > 0 Then
Return dt
Else
Return Nothing
End If
Catch ex As SqlException
Dim myMessage As String = ex.Message
Finally
If conn.State <> ConnectionState.Closed Then
conn.Close()
End If
End Try
Also, you might be better off on several levels if you combine the SELECT and the INSERT into a single statement. You can do this via the OUTPUT clause, as follows:
INSERT INTO Part
([PartNumber]
,[ReplacementPartNumber]
,[ShortDescription]
,[ListPrice]
,[PartStatusTypeID]
,[Weight]
,[CategoryID]
,[DateInserted]
,[SendToWebsite]
,[FileName]
,[MagentoPartTypeID]
,[PrintNumber])
OUTPUT INSERTED.[PartNumber] -- return the inserted values to the app code
SELECT
c.PartNumber
,c.ReplacementPartNumber
,c.ShortDescription
,c.ListPrice
,CASE WHEN c.PartStatusTypeID = #Enabled_PartStatusID
THEN #NotUploaded_PartStatusID
ELSE #Disabled_PartStatusID END
,#DefaultWeight
,#DefaultCategoryID
,#InsertionDate
,#SendToWebsite
,#FileName
,#MagentoPartTypeID
,c.PrintNumber
FROM
COPICSPartFile c
LEFT JOIN Part p on c.PartNumber = p.PartNumber
WHERE
p.PartNumber IS NULL

Related

Returning Unique IDs from Oracle Update statement in VB.NET

I am trying to update a selection of rows in an Oracle table we are using to handle messages. Because this table is busy, it would be best if the update could return the UniqueIDs of the rows it updated in an atomic transaction. I modified a code sample I found on StackOverflow to look like the following, but when I examine the parameter "p", I don't find any information coming back from the update statement, as I expected.
Any suggestions for modifying either the .NET code that is setting up the Oracle call, or modifying the Oracle SQL statement itself?
Dim connectString As String = data source=ORA1;user id=MESSAGEBOX;password=MESSAGEBOX
Dim conn As New OracleConnection(connectString)
If conn.State <> ConnectionState.Open Then
conn.Open()
End If
Dim transaction As OracleTransaction = conn.BeginTransaction()
Dim cmd As New OracleCommand()
cmd.Connection = conn
cmd.CommandText = "BEGIN UPDATE MESSAGE_TABLE SET C_WAS_PROCESSED = 2 WHERE C_ID IN (SELECT * FROM(SELECT C_ID FROM MESSAGE_TABLE WHERE C_WAS_PROCESSED = 0 AND C_CREATED_DATE_TIME < CAST(SYSTIMESTAMP AT TIME ZONE 'UTC' AS DATE) ORDER BY C_MESSAGE_PRIORITY, C_ID) WHERE ROWNUM < 16) RETURNING C_ID BULK COLLECT INTO :C_ID; END;"
cmd.CommandType = CommandType.Text
cmd.BindByName = True
cmd.ArrayBindCount = 15
Dim p As New OracleParameter()
p.ParameterName = "C_ID"
p.Direction = ParameterDirection.Output
p.OracleDbType = OracleDbType.Int64
p.Size = 15
p.ArrayBindSize = New Integer() {10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10}
p.CollectionType = OracleCollectionType.PLSQLAssociativeArray
cmd.Parameters.Add(p)
Dim nRowsAffected As Integer = cmd.ExecuteNonQuery()
transaction.Commit()
conn.Close()
conn.Dispose()
I believe that problem here is that SQL variable being RETURNED BULK COLLECT INTO needs to be of a TABLE type, not a simple number type.
Alter your SQL to include a declaration block like follows:
cmd.CommandText = "DECLARE TYPE IDS IS TABLE OF MESSAGE_TABLE.C_ID%TYPE; C_ID IDS;
BEGIN UPDATE MESSAGE_TABLE SET C_WAS_PROCESSED = 2 WHERE C_ID IN (SELECT * FROM(SELECT C_ID FROM MESSAGE_TABLE WHERE C_WAS_PROCESSED = 0 AND C_CREATED_DATE_TIME < CAST(SYSTIMESTAMP AT TIME ZONE 'UTC' AS DATE) ORDER BY C_MESSAGE_PRIORITY, C_ID) WHERE ROWNUM < 16) RETURNING C_ID BULK COLLECT INTO :C_ID; END;"
I got the table declaration syntax from this oracle-base.com link.

Merge query from datagridview to database not executing sql, vb.net

I'm having a problem executing a merge query from to update or insert values from a DataGridView table into a sql server database table. Here is my code below, it doesn't give me any errors or stoppages, however I recently noticed that it has been creating completely rows in my database table dbo.schedule which contain all NULL values even that key location, could someone please help me? I'm not very familiar with merge queries in sql so please point out issues with my syntax:
Dim query As String = String.Empty
query &= "DECLARE #TaskID nvarchar(8), #Task nvarchar(50), #Start_date datetime, #Due_date datetime, #Complete bit, #Task_Manager nvarchar(8), #JRID nvarchar(10), #Entered_By char(50), #Time_Entered datetime;"
query &= "MERGE INTO schedule USING (VALUES (#TaskID, #Task, #start_date, #Due_Date, #Complete, #Task_Manager, #JRID, #Entered_By, #Time_Entered)) AS t(TaskID, Task, start_date, Due_Date, Complete, Task_Manager, JRID, Entered_By, Time_Entered) "
query &= "ON schedule.TaskID = #TaskID WHEN MATCHED THEN"
query &= " UPDATE SET schedule.TaskID = t.TaskID, schedule.Task=t.Task, schedule.start_date=t.start_date, schedule.due_date=t.due_date, schedule.complete=t.complete, schedule.task_manager=t.task_manager, "
query &= "schedule.JRID=t.JRID, schedule.Entered_by=t.Entered_by, schedule.Time_Entered=t.Time_Entered"
query &= " WHEN NOT MATCHED THEN INSERT (TaskID, Task, start_date, Due_Date, Complete, Task_Manager, JRID, Entered_By, Time_Entered)"
query &= " VALUES (#TaskID, #Task, #start_date, #Due_Date, #Complete, #Task_Manager, #JRID, #Entered_By, #Time_Entered);"
Using conn As New SqlConnection(dbLocations(0, 1))
Using comm As New SqlCommand()
With comm
For Each row As DataGridViewRow In MainSchedule.DataGridView1.Rows
If Not (row.Cells(0).Value = Nothing) Then
.Parameters.Clear()
.Connection = conn
.CommandType = CommandType.Text
.CommandText = query
insertcommand.Parameters.AddWithValue("#TaskID", TNn)
insertcommand.Parameters.AddWithValue("#Complete", "False")
insertcommand.Parameters.AddWithValue("#Task", row.Cells(0).Value)
insertcommand.Parameters.AddWithValue("#Start_date", row.Cells(1).Value)
insertcommand.Parameters.AddWithValue("#Due_Date", row.Cells(2).Value)
insertcommand.Parameters.AddWithValue("#JRID", txtJRID.Text)
insertcommand.Parameters.AddWithValue("#Task_Manager", row.Cells(3).Value)
insertcommand.Parameters.AddWithValue("#Entered_By", GetUserName())
insertcommand.Parameters.AddWithValue("#Time_Entered", Now)
NextTask()
End If
Next
End With
conn.Open()
comm.ExecuteNonQuery()
End Using
End Using
I figured it out in case anyone is wondering, here is my new code:
Connexion.Open()
Dim query As String = String.Empty
Dim keypos = 0
query &= "UPDATE schedule SET Task = #Task, Complete = #Complete, Start_date = #Start_date, "
query &= "Due_date = #Due_date, JRID = #JRID, Task_Manager = #Task_Manager, Entered_By = #Entered_By, Time_Entered = #Time_Entered "
query &= "WHERE TaskID = #TaskID "
query &= "IF ##ROWCOUNT = 0 INSERT INTO schedule ( TaskID, Task, start_date, Due_Date, Complete, Task_Manager, JRID, Entered_By, Time_Entered)"
query &= " VALUES ( #TaskID, #Task, #start_date, #Due_Date, #Complete, #Task_Manager, #JRID, #Entered_By, #Time_Entered);"
For Each row As DataGridViewRow In MainSchedule.DataGridView1.Rows
If Not (row.Cells(0).Value = Nothing) Then
insertcommand.Parameters.Clear()
insertcommand.CommandText = query
insertcommand.Parameters.AddWithValue("#TaskID", row.Cells(0).Value)
insertcommand.Parameters.AddWithValue("#Complete", "False")
insertcommand.Parameters.AddWithValue("#Task", row.Cells(1).Value)
insertcommand.Parameters.AddWithValue("#Start_date", row.Cells(2).Value)
insertcommand.Parameters.AddWithValue("#Due_Date", row.Cells(3).Value)
insertcommand.Parameters.AddWithValue("#JRID", txtJRID.Text)
insertcommand.Parameters.AddWithValue("#Task_Manager", row.Cells(4).Value)
insertcommand.Parameters.AddWithValue("#Entered_By", GetUserName())
insertcommand.Parameters.AddWithValue("#Time_Entered", Now)
insertcommand.ExecuteNonQuery()
End If
keypos = keypos + 1
Next
Connexion.Close()

How to determine if sql INSERTED or UPDATED?

Say I have a function like this:
Private Function addNicheMerge(ByVal tyNicheMergePOLE As typeNicheMergePOLE) As Integer Implements IGenie.addNicheMerge
Dim objParameterValues As New clsParameterValues
Dim objCon As DbConnection
Dim paramValues() As DbParameter
Dim iConnectionBLL As DataAccessLayer.Connection.iConnectionBLL
iConnectionBLL = New clsConnectionBLL()
Dim intCount As Integer
Try
tyAcornCollision = New typeAcornCollision
objParameterValues = New clsParameterValues
objCon = iConnectionBLL.getDatabaseTypeByDescription("GENIE2")
Using objCon
Dim strSQL As String
strSQL = "If NOT Exists (SELECT * FROM dbNicheMergeLog WHERE MasterID=#MasterID AND ChildID = #ChildID) "
strSQL = strSQL & "INSERT INTO dbNicheMergeLog (MasterID, ChildID, DateAdded, GenieUpdated, done, CheckedByUser, MergeTypeID, Usercode, LastUpdated) VALUES (#MasterID, #ChildID, getDate(), 0, 0, #CheckedByUser, #MergeTypeID, '', " & _ "GetDate()) Else Update dbNicheMergeLog SET LastUpdated = getdate() WHERE MasterID=#MasterID AND ChildID = #ChildID"
objParameterValues.AssignParameterValues("#MasterID", tyNicheMergePOLE.MasterID, 1)
objParameterValues.AssignParameterValues("#ChildID", tyNicheMergePOLE.ChildID, 1)
objParameterValues.AssignParameterValues("#MergeTypeID", tyNicheMergePOLE.MergeTypeID, 1)
objParameterValues.AssignParameterValues("#CheckedByUser", 0, 1)
paramValues = objParameterValues.getParameterValues
intCount = clsDatabaseHelper.ExecuteNonQuery(objCon, CommandType.Text, strSQL, paramValues)
Return intCount
End Using
Catch ex As Exception
Return -2
Finally
' If objCon.State = ConnectionState.Open Then
' objCon.Close()
' End If
objCon = Nothing
End Try
End Function
Is there a way to determine whether an insertion or update took place?
if you are using sql server, you could use an output clause to determine if an update happened
Update dbNicheMergeLog SET LastUpdated =
getdate() WHERE MasterID=#MasterID AND
ChildID = #ChildID
OUTPUT count(*)
Then in vb use
dim updateCount as int = Convert.ToInt32(clsDatabaseHelper.ExecuteScalar())
Try this SQL:
WITH new (
MasterID
,ChildID
,DateAdded
,GenieUpdated
,done
,CheckedByUser
,MergeTypeID
,Usercode
,LastUpdated
)
AS(
SELECT
#MasterID
,#ChildID
,getDate()
,0
,0
,#CheckedByUser
,#MergeTypeID
,''
,getdate()
)
MERGE INTO dbNicheMergeLog t
USING new
ON t.MasterID = new.MasterID
AND t.ChildID = new.ChildID
WHEN NOT MATCHED BY TARGET THEN
INSERT (
MasterID
,ChildID
,DateAdded
,GenieUpdated
,done
,CheckedByUser
,MergeTypeID
,Usercode
,LastUpdated
)
VALUES (
new.MasterID
,new.ChildID
,new.DateAdded
,new.GenieUpdated
,new.done
,new.CheckedByUser
,new.MergeTypeID
,new.Usercode
,new.LastUpdated
)
OUTPUT $action as Action
;
You can use
Output deleted.*
in your update query to get the results that were lost on update, something like this:
Create Table #abctest(
Id int identity,Name varchar(200))
Insert into #abctest(Name)
Values('abcd'),('bcde'),('cdef'),('defg'),('efgh'),('fghi')
Update #abctest Set Name='xyz' OUTPUT Deleted.* Where Id=2
The above query will give you an output
Id Name
2 bcde

Fetching second row value from stored procedure in vb.net

ALTER procedure [dbo].[carcallvalidation]
#carid nvarchar(100)=null
as
begin
select t.TBarcode, t.Paid,t.Status,t.DelDate from Transaction_tbl t where TBarcode=#carid
declare #transid integer=null
select #transid= t1.transactID from Transaction_tbl t1 where TBarcode=#carid
select count(k.transactid) transid from KHanger_tbl k where k.transactid=#transid
end
while executing stored procedure i am getting out put like this:
Tbarcode paid status
1111 0 2
Transid
2
How I can fetch Transid value in vb.net?
I Fetched first row value like this:
Dim cmdcarvalidation As New SqlCommand("carcallvalidation", con.connect)
cmdcarvalidation.CommandType = CommandType.StoredProcedure
cmdcarvalidation.Parameters.Add("#carid", SqlDbType.VarChar, 50, ParameterDirection.Input).Value = txtTicket.Text
dr = cmdcarvalidation.ExecuteReader
While dr.Read
tbarcode=dr("Tbarcode")
payd= dr("paid")
status= dr("status")
end while
SqlDataReader.NextResult Method
NextResult is used to move to the next result set when you have multiple result sets - by default the reader is positioned at the first result set.
So:
While dr.Read()
tbarcode = dr("Tbarcode")
payd = dr("paid")
status = dr("status")
End While
dr.NextResult()
While dr.Read()
transid = dr(0)
End While
dr.NextResult()
While dr.Read()
transid_count = dr(transid)
End While
If you simply want the 1st and 3rd results from your stored proc (skipping the SELECT #transid result set), you'd omit the second dr.Read():
While dr.Read()
' process first result set
End While
dr.NextResult()
dr.NextResult()
While dr.Read()
' process the third result set
End While

SQLException was unhandled, Dataset -Table Adapter

I have the following stored procedure and I'm calling it from my Windows Forms application DataSet like this:
Me.TransactionTableAdapter.spPaymentApply(130, iAmount, Now)
Although I provide the CustomerID, (and stepping in the code to see if it's actually there - and it is) I get the following error during execution:
Procedure or function 'PaymentApply' expects parameter '#CustomerID', which was not supplied.
Here is my SP:
USE [dbPB]
GO
/****** Object: StoredProcedure [dbo].[PaymentApply] Script Date: 05/30/2013 18:34:01 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[PaymentApply]
#CustomerID int,
#Amount int,
#AsOfDate datetime
AS
WITH Totals AS (
SELECT
T.*,
RunningTotal =
Coalesce (
(SELECT Sum(S.Buyin - Coalesce(S.CreditPaid, 0))
FROM [Transaction] S
WHERE
T.CustomerID = S.CustomerID
AND S.Type = 'Credit'
AND S.Buyin > Coalesce(T.CreditPaid, 0)
AND (
T.Starttime > S.Starttime
OR (
T.Starttime = S.Starttime
AND T.TransactionID > S.TransactionID
)
)
),
0)
FROM
[Transaction] T
WHERE
CustomerID = #CustomerID
AND T.Type = 'Credit'
AND T.Buyin > Coalesce(T.CreditPaid, 0)
)
UPDATE T
SET
T.EndTime = P.EndTime,
T.CreditPaid = Coalesce(T.CreditPaid, 0) + P.CreditPaid
FROM
Totals T
CROSS APPLY (
SELECT TOP 1
V.*
FROM
(VALUES
(T.Buyin - Coalesce(T.CreditPaid, 0), #AsOfDate),
(#Amount - RunningTotal, NULL)
) V (CreditPaid, EndTime)
ORDER BY
V.CreditPaid,
V.EndTime DESC
) P
WHERE
T.RunningTotal <= #Amount
AND #Amount > 0;
;
VB Code:
Private Sub btnTransProcess_Click(sender As Object, e As EventArgs) Handles btnTransProcess.Click
Dim iAmount As Integer
Dim drv As DataRowView = CType(Me.cboCustomerName.SelectedItem, DataRowView)
Dim SelCustId As Integer
SelCustId = drv.Item("CustomerID")
Try
iAmount = CType(txtCreditPayment.Text, Integer)
Catch ex As Exception
MessageBox.Show("Enter a valid Credit Payment Amount!", "Invalid Amount", MessageBoxButtons.OK, MessageBoxIcon.Warning)
End Try
MsgBox(SelCustId)
Me.TransactionTableAdapter.spPaymentApply(130, iAmount, Now)
'
'Dim command As New SqlCommand(queryString, connection)
'command.Parameters.AddWithValue("#CustomerID", 123)
End Sub
What happened ?
You didn't provide the parameter.
But.. I double-checked...
Maybe you're not calling it the right way ?
Have you tried something like this :
Dim queryString As String = "exec PaymentApply"
Using connection As New SqlConnection(ConnStrg)
connection.Open()
Dim command As New SqlCommand(queryString, connection)
command.Parameters.AddWithValue("#CustomerID", 123)
// ... same for other non-nullable parameters
Dim reader As SqlDataReader = command.ExecuteReader()
End Using
End Try
Why should I do this ?
When you call your Stored Procedure from you code, you don't provide the #CustomerID int parameter.
Since the parameter is not nullable it expects a value and will not go any further until you give it one.
If you want it to be nullable you can set a default value to it. This way he will take this value if you don't provide a new one. Exemple :
From your code, you pass a parameter like this :
someCommand.Parameters.AddWithValue("#CustomerID", 123)
If you want your parameter to be nullable, do something like this in SQL
#CustomerID int = 123
It sounds like you're using a Table Adapter instead of a Command Object.
see: Table Adapter
and TableAdapter Query Configuration Wizard
Your call to Me.TransactionTableAdapter.spPaymentApply(130, iAmount, Now) looks like it should provide 130 as the CustomerID- but if the code does not compile/work properly, perhaps you should use the configuration wizard.
Or, consider the use of a command object instead. The use of both is outlined here:
How to: Create and Execute an SQL Statement that Returns No Value