I have a function to get information on a SQL Server Table. I know the function is correct and returning everything as suposed too (I used msgBox to check it). The problem is when I try to use that function it displays an error:
My WCF Function:
Public Function GetCLInfo(ByVal cl_ID As Integer) Implements SQLOperator.GetCLInfo
Dim CLInfo(7) As String
Try
SQLCon.ConnectionString = SQLConString
SQLCon.Open()
SQLCMD = New SqlCommand("Select * from dbo.cl Where ID=" & cl_ID & ";", SQLCon)
SQLDataReader = SQLCMD.ExecuteReader()
While SQLDataReader.Read
CLInfo(0) = SQLDataReader("ID")
CLInfo(1) = SQLDataReader("Name")
CLInfo(2) = SQLDataReader("Address")
CLInfo(3) = SQLDataReader("Contact")
End While
Return CLInfo
Catch ex As Exception
MsgBox("Error: " & Environment.NewLine & ex.ToString)
CLInfo(0) = "False"
Return CLInfo
Finally
SQLCon.Close()
SQLCon.Dispose()
End Try
Return CLInfo
End Function
In the picture you can see aswell how I'm trying to use the function.
Can someone kindly tell me what am I doing wrong?
Your problem is that you are calling a function to return string array and program doesn't catch by itself. As #Matt Wilko point out use Option Strict On.
Second if you have more than one client your logic fails.
For I as integer = 1 to AllCl
Dim cl(7) as String
cl = Service.GetClInfo(I)
next
Above code will reset every each time, plus when you leave for loop you are loosing access to cl.
Ugly fix could be multidimensional array.
Now you should specify your return type
Public Function GetCLInfo(ByVal cl_ID As Integer) As String() Implements SQLOperator.GetCLInfo
Related
I'm attempting to overload the "Delete" method of a TableAdapter (approach). How can I execute an SQL statement from 'here' to handle the delete?
I've got:
Namespace AFL_BackendDataSetTableAdapters
Partial Class Log_entry_unitTableAdapter
Public Overloads Function Delete(ByVal LogEntryID As Integer) As Integer
Dim SQL As String
SQL = "DELETE FROM log_entry_unit WHERE log_entry_unit_id=" & LogEntryID
'?????.Execute SQL
Return 0
End Function
End Class
End Namespace
The overload is working fine, but I don't know how to do the hard part and actually manipulate the data from here. Previously, I've just gone into the Dataset Designer and manually updated the generated methods to work like I want them, but whenever I use the wizard to regenerate the dataset, that (as expected) gets overwritten.
I've previously only ever manipulated Data using the generated methods, and now I'm stuck.
EDIT w/ Final Answer
Based on William's help below here's the final working solution (Note I just had to use OleDb instead of SQL since my Dataset is Access:
Imports System.Data.OleDb
Namespace AFL_BackendDataSetTableAdapters
Partial Class Log_entry_unitTableAdapter
Public Overloads Function Delete(ByVal LogEntryID As Integer) As Integer
Dim queryString As String = "DELETE FROM log_entry_unit WHERE log_entry_unit_id=" & LogEntryID
Dim command As New OleDbCommand(queryString, Connection)
Dim r As Integer
Try
Connection.Open()
r = command.ExecuteNonQuery()
Catch ex As Exception
MsgBox(ex.Message)
r = 0
Finally
Connection.Close()
End Try
Return r
End Function
End Class
End Namespace
I hardcoded a connection string for reference only. This should be in a config file. As an example:
Dim connectionString As String = _
"Data Source=(local);Initial Catalog=YourDatabase;" _
& "Integrated Security=true"
Dim queryString As String = "DELETE FROM log_entry_unit WHERE log_entry_unit_id=" & LogEntryID
' Create and open the connection in a using block. This
' ensures that all resources will be closed and disposed
' when the code exits.
Using connection As New SqlConnection(connectionString)
' Create the Command
Dim command As New SqlCommand(queryString, connection)
' Open the connection in a try/catch block.
Try
connection.Open()
command.ExecuteNonQuery()
Catch ex As Exception
' handle exception here
End Try
End Using
EDIT
I probably should of mentioned you will probably want to fill your adapter again after the delete.
I get the following error:
There is already an open DataReader associated with this Command which must be closed first.
Exception Details: System.InvalidOperationException: There is already an open DataReader associated with this Command which must be closed first.
And this is my code so far:
Public Function GetPrice() As Decimal
Dim aParams(1) As String
Dim dPrice As Decimal
aParams(0) = Me.ID
aParams(1) = Me.Date
With GetDb().GetRsWithSqlId("GET_PRICE", aParams)
If .Read() Then
dPrice = Convert.ToDecimal(IIf(TypeOf .Item("TS_PRICE") Is DBNull, 0.0, .Item("TS_PRICE")))
End If
.Close()
End With
Return dPrice
End Function
Public Function GetRsWithSqlId(ByVal sId As String, ByVal arrParams() As Object) As OleDbDataReader
Dim i As Integer
If m_oOleDbConnection.State <> ConnectionState.Open Then
If Not Connect() Then Throw New ArgumentException("Can't connect to database!" & Chr(13) & "LastError: " & LastError())
End If
m_oOleDbCommand.Parameters.Clear()
m_oOleDbCommand.CommandType = CommandType.Text
m_oOleDbCommand.CommandText = GetSql(sId)
If Not IsNothing(m_oOleDbTransaction) Then
m_oOleDbCommand.Transaction = m_oOleDbTransaction
End If
For i = 0 To arrParams.GetLength(0) - 1
m_oOleDbCommand.Parameters.Add(New OleDbParameter("?", arrParams(i)))
Next
Return m_oOleDbCommand.ExecuteReader()
End Function
The error message says that the DataReader is already in use with another command on the same connection, but I can't figure out where the reader is open.
Is there a solution to close the DataReader if it is open or to avoid using DataReader? My understanding is that without DataReader many commands can be used on the same connection at the same time or?
i have the following function, the problem is since im using ExecuteScalar, the connection never closes when using any other function again...
Public Function Valor(tabla As String, campos As String, condicion As String)
cn.Open()
Dim sql As String = "SELECT " & campos & " FROM " & tabla & " WHERE " & condicion
comando = New SqlCommand(sql, cn)
Return comando.ExecuteScalar
If cn.State = ConnectionState.Open Then
cn.Close()
End If
End Function
This Function returns me a Time value from SQL time(7) to TIMESPAN on the app, i am able to get the value but Since Return skips anything after it, connection is not closed.
ANy idea how to close the connection this?
or there is another method on how can i get the value of my query.
Thanks in advance
First of all, connections in .Net work best when you create an entirely new object for each query. Don't try to re-use the same connection all the time.
Second, that code could still leak connections even if you had closed the connection before returning, because it would never reach .Close() function call if an exception is thrown.
Finally, that code is horribly vulnerable to sql injection. It's practically begging to get hacked.
Here is code that solves all three problems:
Public Function Valor(ByVal sql As String, ByVal ParamArray condicion() As SqlParameter)
'cnString is a made-up string variable for the connection string that you will create in the same place (and instead of) that you currently have cn
Using cn As New SqlConnection(cnString), _
cmd As New SqlCommand(sql, cn)
If condicion IsNot Nothing Then cmd.Parameters.AddRange(condicion)
cn.Open()
Return cmd.ExecuteScalar()
End Using
End Function
Instead of returning immediately, store the result in a variable, clean up everything, then return the cached variable:
Public Function Valor(tabla As String, campos As String, condicion As String)
cn.Open()
Dim sql As String = "SELECT " & campos & " FROM " & tabla & " WHERE " & condicion
comando = New SqlCommand(sql, cn)
Dim retorno As Object = comando.ExecuteScalar()
If cn.State = ConnectionState.Open Then
cn.Close()
End If
Return retorno
End Function
In my VB.Net program I have found two (2) warnings shows the following messages:
Function doesn't return a value on all code paths. A null reference exception could occur at run time when the result is used
help me where is the problem and to fix it. The following snapshot is where the warning error indicates:
The Firs Warning near 'End Function'
'Executes SQL commands to the system database
Public Function ExecSQL(ByVal sql As String)
Dim com As New MySqlCommand(sql)
Try
RefreshConnection()
com.Connection = con
com.ExecuteNonQuery()
Catch ex As Exception
Return ex
MsgBox(ex.Message, MsgBoxStyle.Critical, "Error")
End Try
End Function
The Second Waring near 'End Function'
'Get the value of an specific field in a given sql string
Public Function GetField(ByVal sql As String, ByVal field As String)
Try
RefreshConnection()
Dim com As New MySqlCommand(sql, con)
Dim dReader As MySqlDataReader = com.ExecuteReader
While dReader.Read
GetField = dReader(field).ToString
End While
dReader.Close()
Catch ex As Exception
Return ex
MsgBox(ex.Message, MsgBoxStyle.Critical, "Error")
End Try
End Function
This occurs because in the exception handler you actually return the exception object ex. You're not returning anything if no exception occurs. You could explicitly add Return Nothing just above Catch ex as Exception, but I guess from the message that VB is doing that automatically for you...
By the way: What's the point returning the exception to the caller? Put the message box into the calling method and wrap your call in Try-Catch there. A method that's obviously not meant to interact with the user shouldn't show messages at all. And then you can just pass the exception up until you end up the presentation layer.
Long story short: No message boxes in business logic. User information only in presentation layer.
After reading your second method: Things are even worse here! If the data reader has rows, you set the result of the method to the last (!) result you found (this is a WTF in itself, but another story). If an error occurs, the result is an object of type Exception, so the calling code even needs to make sure to decide whether the result of the function call is actually a field value or an exception!
This is really bad design my friend.
You asked for fixed code:
'Executes SQL commands to the system database
Public Sub ExecSQL(ByVal sql As String)
Dim com As New MySqlCommand(sql)
RefreshConnection()
com.Connection = con
com.ExecuteNonQuery()
End Sub
'Get the value of an specific field in a given sql string
Public Function GetField(ByVal sql As String, ByVal field As String)
RefreshConnection()
Dim com As New MySqlCommand(sql, con)
Dim dReader As MySqlDataReader = com.ExecuteReader
GetField = Nothing
While dReader.Read
GetField = dReader(field).ToString
End While
dReader.Close()
End Function
There are several flaws here. In the interest of brevity, I'll just post some improved code:
'Executes SQL commands to the system database
Private Function ExecSQL(ByVal sql As String, ByVal params As IEnumerable(Of MySqlParameter))
Using cn As New MySqlConnection(GetConnectionString()), _
com As New MySqlCommand(sql, cn)
For Each param As MySqlParameter in params
com.Parameters.Add(param)
Next param
cn.Open()
com.ExecuteNonQuery()
End Using
End Function
'Get the value of an specific field in a given sql string
Private Function GetField(Of T)(ByVal sql As String, ByVal params As IEnumerable(Of MySqlParameter), ByVal field As String) As T
Using cn As New MySqlConnection(GetConnectionString())
com As New MySqlCommand(sql, cn)
For Each param As MySqlParameter In params
com.Parameters.Add(param)
Next param
cn.Open()
Using rdr As MySqlDataReader = com.ExecuteReader()
While rdr.Read()
Return CType(rdr(field), T)
End While
End Using
End Using
Return Nothing
End Function
Ok, here's my Code:
Imports IBM.Data.DB2.iSeries
Module ConsoleData1
Sub Main()
'program identifier (to verify that you're using the right module)
Console.WriteLine("ConsoleData1.vb")
'create the connection object to the IBM i
Using cn As New iDB2Connection("DataSource=xxxxxxxxxxxxx")
Try
cn.Open()
Catch ex As iDB2Exception
Console.WriteLine("An error occurred on cn.Open()")
Exit Sub
End Try
'create a command object, initialize to valid SQL statement
Using cmd As New iDB2Command("select * from testlib.finishk", cn)
'create a command object, initialize to valid SQL statement
Console.Write("Enter the Color Code!==>")
Dim baldue = Console.ReadLine()
Dim sql As String = "Select * from testlib.finishk where FINCOD >=" & FINCOD
Using cmdx As New iDB2Command(sql, cn)
End Using
'create a data reader object, fill by executing the command
Try
Using dr As iDB2DataReader = cmd.ExecuteReader
'display data in a table
Console.Write("FINCOD" & vbTab)
Console.Write("FINDES" & vbTab)
While dr.Read()
Console.Write("{0}{1}", dr("FINCOD"), vbTab)
Console.Write("{0}{1}", dr("FINDES"), vbTab)
End While
End Using
Catch ex As iDB2Exception
Console.WriteLine("An error occurred on cmd.ExecuteReader()")
Exit Sub
End Try
End Using
End Using
Console.WriteLine("Press ENTER to end")
Console.ReadLine()
End Sub
Sub HandleError(ByVal errorOccurred As String, ByVal ex As iDB2Exception)
Console.WriteLine(errorOccurred)
Console.WriteLine("Source: {0}", ex.Source)
Console.WriteLine("SQLState: {0}", ex.SqlState)
Console.WriteLine("Message: {0}", ex.Message)
Console.WriteLine("MessageCode: {0}", ex.MessageCode)
Console.WriteLine("MessageDetails: {0}", ex.MessageDetails)
Console.ReadLine()
End Sub
Private Function Screen() As Object
Throw New NotImplementedException
End Function
End Module
Now, how would I make it so that I can resize the cmd window to be larger and still retain the integrity of the table structure? If I resize the cmd window, it ends up looking like someone just threw the values into a window in random placement.
Thanks.
You need two things: a line break between each record and length information in your format string (to shoot for fixed width columns).
'You can play the -30 numbers here to get different spacing, based on the nature of your data
Dim recordFormat As String = "{0,-30} {1,-30}"
Console.WriteLine(recordFormat, "FINCOD","FINDES")
While dr.Read()
Console.WriteLine(recordFormat, dr("FINCOD"), dr("FINDES"))
End While
Finally, I'm not familiar with DB2, but please tell me there's a better way than string concatenation to add the user's FINDCOD value to the sql statement? What you're doing is horribly unforgivably insecure. What if I put the text 0;DROP TABLE testlib.finishk;-- into your system instead of a FINDCOD? Any sane code would use parameterized queries for this.