A loop in a try..catch - vb.net

Okay, I have a try..catch such that :
Try
Dim dr As OleDbDataReader = performSelect("SELECT * FROM " & table, myConn)
Catch ex As Exception
sheet = InputBox("Re-enter table:")
Dim dr As OleDbDataReader = performSelect("SELECT * FROM " & table, myConn)
End Try
The above will only work for the first instance.
What I want to do is keep displaying the Input box until the dr is a success (i.e. values have been retrieved from the database.
I know I need some sort of while in the catch, but I am unsure how to proceed

Remove the performSelect from inside the catch block, otherwise you could throw an exception there too and it would need to be caught, etc. Put the entire try catch block inside the while loop and exit when you have a value for sheet.

Have a look here why this is not a good idea.
If you are still interested, you can try this:
Dim retries As Integer = 3
While True
Try
Dim sheet as String = InputBox("Enter table:")
Dim dr As OleDbDataReader = performSelect("SELECT * FROM " & sheet, myConn)
Exit While
Catch
retries -= 1
If retries = 0 Then
Throw
Else
System.Threading.Thread.Sleep(1000)
End If
End Try
End While
Change the retries if you want more(infinite-> <=0), change or remove the thread.sleep if you want to retry immediately.

This might work:
Dim inputOk As Boolean = False
Do Until inputOk = True
Try
Dim dr As OleDbDataReader = performSelect("SELECT * FROM " & table, myConn)
inputOk = True
Catch ex As Exception
sheet = InputBox("Re-enter table:")
End Try
End
Loop

Catch block has been designed to handle exceptions, not to execute database queries... You should use Jeff's way. The same I was writing, but he's quicker than I

Related

How to continue the for loop for next query even the previous query has error?

This is a VB.NET coding for extract a set of databases from 100 of databases with the field "flag".
I want to execute this select query in all databases containing in "dbs" arraylist.
For i = 0 To dbs.Count
Sql = " select company, branch, year, flag from " + dbs(i) + ".global"
Try
With Cmd
.CommandText = Sql
.Connection = MyCnn
End With
Catch ex As MySqlException
' do nothing
End Try
Dim reader1 As MySqlDataReader = Cmd.ExecuteReader()
If (reader1("flag").ToString() = "10") Then
MyDbList.Company = reader1("company")
MyDbList.Branch = reader1("branch")
MyDbList.Year = reader1("year")
MyDbList.DbName = reader("Database")
End If
If you want to abort the current iteration of a loop and go straight onto the next one then you can use the Continue keyword. Assuming that you actually write a sensible exception handler, you can put Continue For in the Catch block.
Based on what #jmcilhinney has implied, this should work ...
For i As Integer = 0 To dbs.Count - 1
Try
Sql = $"select company, branch, year, flag from {dbs(i)}.global"
With Cmd
.CommandText = Sql
.Connection = MyCnn
End With
Using reader1 As MySqlDataReader = Cmd.ExecuteReader()
If (reader1("flag").ToString() = "10") Then
With MyDbList
.Company = reader1("company")
.Branch = reader1("branch")
.Year = reader1("year")
.DbName = reader1("database")
End With
End If
End Using
Catch ex As MySqlException ' Other exception types won't be caught by this Catch!
' Maybe log something somewhere?
Continue For
End Try
Next i
Note use of Using and string interpolation and correction to For loop range as per #Mary's comment. As always, turn Option Strict and Option Explicit on - and, IMHO, Option Infer off - to highlight any compilation issues.
Finally, to be clear, this isn't how I would code this up; I'm only trying to address your question here.

how can i push a msgbox if record is not found in Database using vb.net

seeking help how i can push a msgbox error if a record is not in the database or no data in the database. im using vb.net and sql to check the record. not sure how to do,
here is my code
Try
myConnection.Open()
str = "SELECT * FROM tblEmp WHERE (EmpID = '" & ADS.UserEmpID & "')"
Dim cmd As OleDbCommand = New OleDbCommand(str, myConnection)
dr = cmd.ExecuteReader
While dr.Read()
'Main.BGCPnl.Visible = True
BGC1 = dr("PreStartChecks").ToString
BGC2 = dr("EmpName").ToString
//>Here is my code for the error message when record is not
found, im not sure what will be the right code.
i used count parameter
BGCEmp = dr(ADS.UserEmpID)
If BGCEmp.Count = 0 Then
MsgBox("no record")
Exit Sub
End If
End While
Catch ex As Exception
MsgBox("Unable to Connect to BGC DB. You may not have access or DB not available." & ex.ToString)
End Try
myConnection.Close()
You should learn how to properly use the Read method and the HasRows property of your data reader. If there can never be more than one record but there might be none then use just Read:
If myDataReader.Read() Then
'There is a row and you can access its data here.
Else
'There are no rows.
End If
If there may be multiple rows and either there can't be no rows or you don't need to do anything specific in the case that there are no rows then just use Read:
While myDataReader.Read()
'Access the current row here.
End While
If there are no rows then you never enter the loop and execution simply continues after that.
If there may be zero, one or more rows and you do need to do something specific in the case where there are none, use both HasRows and Read:
If myDataReader.HasRows Then
'There is at least one row so read the data.
While myDataReader.Read()
'Access the current row here.
End While
Else
'There are no rows.
End If
There may be situations where you only care whether there is data but you don't need the data itself. In that case, just use HasRows:
If myDataReader.HasRows Then
'There is a at least one row
Else
'There are no rows.
End If
In cases like that though, I'd suggest that you should be doing something like using a COUNT function in your query and calling ExecuteScalar rather than calling ExecuteReader.
Try
myConnection.Open()
str = "SELECT * FROM tblEmp WHERE (EmpID = '" & ADS.UserEmpID & "')"
Dim cmd As OleDbCommand = New OleDbCommand(str, myConnection)
dr = cmd.ExecuteReader
If dr.HasRows Then
While dr.Read()
BGC1 = dr("PreStartChecks").ToString
BGC2 = dr("EmpName").ToString
End While
Else
MessageBox.Show("No Record found", "ERROR", MessageBoxButtons.OK, MessageBoxIcon.Error)
Exit Sub
End If
Catch ex As Exception
MsgBox("Unable to Connect to BGC DB. You may not have access or DB not available." & ex.ToString)
End Try
myConnection.Close()
Read documentation about Read() and HasRows.

How to show error message on forms project when its already catched in library

Have some windows form with nice circural progress bar which i show user during long database function process. There is also task which calls function which inside i got query with transaction and there is catch implemented to rollback if it fails and return either true/false about state of process after finished. I didn't place there memssage box to show error as it's not adviced to store message boxes along in library projects so i would like to show this error on my task level function (in catch). However since i am catching error inside my function i am not able to show it (catch it) on the task's catch. How to accomplish that?
The only idea in my head is to instead of returning only result also return catched error message with use of Tuple like this: Tuple(Of Boolean, String). So i would be able to return two things: result and error message text. I am not so sure if this is right way to do such things. Looking for your advice.
This comes from windows forms project:
Dim pic As New CircuralWindowsForms(eCircularProgressType.Donut)
Dim tsk As Task(Of Boolean) = Task.Factory.StartNew(Of Boolean)(Function()
Dim resu = False
Try
resu = createArticle.ProcessArticle(data)
Catch sqlex As Exception
pic.Invoke(Sub() MessageBox.Show(pic, sqlex.Message))
Finally
'--Close form once done (on GUI thread)
pic.Invoke(New Action(Sub() pic.Close()))
End Try
Return resu
End Function)
'Show circural form
pic.ShowDialog()
Task.WaitAll(tsk)
if tsk.Result = true Then
...
This comes from library project:
Public Function ProcessArticle(data as Data) As Boolean
Dim result = false
Using connection As New SqlConnection(strcon)
Try
connection.Open()
Dim transaction As SqlTransaction
transaction = connection.BeginTransaction()
.....
transaction.Commit()
result=true
Catch ex As Exception
result = False
transaction.Rollback()
End Try
End Using
return result
End Function
Extended question a bit (discussion with sstan):
Public Sub DeleteAllRelated(varId As Integer)
Using con As New SqlConnection(strcon)
Dim commit As Boolean = True
con.Open()
Dim tran As SqlTransaction = con.BeginTransaction
Dim dt As New DataTable
dt = CType(New Variation_VariationAttributeDAL().GetAllByVariationId(varId), DataTable)
If dt IsNot Nothing Then
For Each row As DataRow In dt.Rows
If commit Then commit = commit And New Artikel_VariationDAL().DeleteByVariation_VariationAttribute(CInt(row(0)), tran)
Next
End If
If commit Then commit = commit And New Variation_VariationAttributeDAL().DeleteAllWhereVarId(varId, tran)
If commit Then commit = commit And Delete(varId, tran)
If commit Then commit = commit And New DALSubKategorie_Variation().Delete(varId, tran)
If commit Then
tran.Commit()
Else
tran.Rollback()
End If
End Using
End Sub
this is e.g for: If commit Then commit = commit And New DALSubKategorie_Variation().Delete(varId, tran)
Public Function Delete(varId As Integer, Optional transaction As SqlTransaction = Nothing) As Boolean
Dim result As Boolean = False
If transaction Is Nothing Then
Try
Using con As New SqlConnection(strcon)
Using cmd As New SqlCommand("DELETE FROM " & SharedData.Write.T(SharedData.Tables.SubKategorie_Variation) & " WHERE FK_Variation_ID=#FK_Variation_ID", con)
cmd.CommandType = CommandType.Text
cmd.Parameters.AddWithValue("#FK_Variation_ID", varId)
con.Open()
Dim rowsAffected As Integer = cmd.ExecuteNonQuery()
con.Close()
result = True
End Using
End Using
Catch ex as Exception
Throw
End Try
Else
Using cmd As New SqlCommand("DELETE FROM " & SharedData.Write.T(SharedData.Tables.SubKategorie_Variation) & " WHERE FK_Variation_ID=#FK_Variation_ID", transaction.Connection)
cmd.Transaction = transaction
cmd.CommandType = CommandType.Text
cmd.Parameters.AddWithValue("#FK_Variation_ID", varId)
Dim rowsAffected As Integer = cmd.ExecuteNonQuery()
result = True
End Using
End If
Return result
End Function
You're right that it would be wrong for your library to display message boxes directly. But that doesn't mean it should swallow exceptions either. In, fact, quite the opposite: you really should let the exception bubble up to the caller, and let the caller decide what to do with it.
With that in mind, I would change the ProcessArticle function to the following:
Public Sub ProcessArticle(data as Data)
Using connection As New SqlConnection(strcon)
Try
connection.Open()
Dim transaction As SqlTransaction
transaction = connection.BeginTransaction()
' .....
transaction.Commit()
Catch ex As Exception
transaction.Rollback()
Throw 'Rethrow exception. The caller can decide what to do with it.
End Try
End Using
End Sub
Notice how the exception is still caught to enable the transaction rollback, but the exception is rethrown so that the caller can catch it. This in turn means that you no longer need to return a boolean to indicate success or failure.
EDIT
Not directly related to your question, but I would further move the code around a little bit so that I don't accidentally try to rollback a transaction before it has even begun (With you current code, ask yourself what would happen if an error occurred while trying to open the connection?):
Public Sub ProcessArticle(data as Data)
Using connection As New SqlConnection(strcon)
connection.Open()
Using transaction = connection.BeginTransaction()
Try
' do work here
transaction.Commit()
Catch ex As Exception
transaction.Rollback()
Throw 'Rethrow exception. The caller can decide what to do with it.
End Try
End Using
End Using
End Sub
EDIT 2
More on the Throw Statement:
A Throw statement with no expression can only be used in a Catch statement, in which case the statement rethrows the exception currently being handled by the Catch statement.
EDIT 3: Simplified version of your last edit
Public Sub DeleteAllRelated(varId As Integer)
Using con As New SqlConnection(strcon)
con.Open()
Using transaction = connection.BeginTransaction()
Try
Dim dt As New DataTable
dt = CType(New Variation_VariationAttributeDAL().GetAllByVariationId(varId), DataTable)
If dt IsNot Nothing Then
For Each row As DataRow In dt.Rows
New Artikel_VariationDAL().DeleteByVariation_VariationAttribute(CInt(row(0)), tran)
Next
End If
New Variation_VariationAttributeDAL().DeleteAllWhereVarId(varId, tran)
Delete(varId, tran)
New DALSubKategorie_Variation().Delete(varId, tran)
'If we made it this far without an exception, then commit.
tran.Commit()
Catch ex As Exception
tran.Rollback()
Throw 'Rethrow exception.
End Try
End Using
End Using
End Sub
Public Sub Delete(varId As Integer, Optional transaction As SqlTransaction = Nothing)
If transaction Is Nothing Then
Using con As New SqlConnection(strcon)
Using cmd As New SqlCommand("DELETE FROM " & SharedData.Write.T(SharedData.Tables.SubKategorie_Variation) & " WHERE FK_Variation_ID=#FK_Variation_ID", con)
cmd.CommandType = CommandType.Text
cmd.Parameters.AddWithValue("#FK_Variation_ID", varId)
con.Open()
Dim rowsAffected As Integer = cmd.ExecuteNonQuery()
con.Close()
End Using
End Using
Else
Using cmd As New SqlCommand("DELETE FROM " & SharedData.Write.T(SharedData.Tables.SubKategorie_Variation) & " WHERE FK_Variation_ID=#FK_Variation_ID", transaction.Connection)
cmd.Transaction = transaction
cmd.CommandType = CommandType.Text
cmd.Parameters.AddWithValue("#FK_Variation_ID", varId)
Dim rowsAffected As Integer = cmd.ExecuteNonQuery()
End Using
End If
End Sub
EDIT 4: Example of exception chaining
Public Sub ProcessArticle(data as Data)
Try
' do work here
Catch ex As Exception
' If you want the original error to go up to the "upper levels"
' but with additional information, you need to throw a new
' instance of an exception with a new message that contains the additional information
' but you need to pass the original exception as a parameter to the constructor
' so that exceptions get chained together.
' If an "upper level" caller catches the chained exception,
' doing "ex.ToString" will provide all the information.
' Try it out, see how it works.
Throw New Exception("put your additional information here", ex)
End Try
End Sub

Collection was modified; enumeration operation may not execute. VB.Net

There are a lot of questions that are a duplicate of this question, but none of them seem to actually answer my specific problem.
I am writing an IRC bot, and i am adding users to a List(Of String) as you can see in the below code. However, when i want to go through a foreach loop of the users on another thread, i get the error in the title. I have even put a boolean in place to stop it from adding users, yet it still says it is being modified somehow. Any help would be greatly appreciated.
(Before you read the code, keep in mind i am new to VB.net, and even if it messy, i'd prefer to not be bashed for it as i am new)
Sub Handler()
handlerActive = True
Dim list As New List(Of String)
query = "SELECT * FROM " & channel.Substring(1)
Try
connHost.Close()
Catch
End Try
connHost.Open()
Dim cmd As MySqlCommand = New MySqlCommand(query, connHost)
Dim dataReader As MySqlDataReader = cmd.ExecuteReader()
While (dataReader.Read())
list.Add(dataReader.GetString(1))
End While
dataReader.Close()
If (users.Count > 0) Then
For Each user2 In users
Try
connHost.Close()
Catch ex As Exception
End Try
connHost.Open()
query = ("UPDATE `" & channel.Substring(1) & "` SET `Amount` = Amount + 1 WHERE `Username`='" & user2 & "'")
cmd = New MySqlCommand(query, connHost)
Try
cmd.ExecuteNonQuery()
Catch
Console.WriteLine("Failed to update user - " & user2)
End Try
Next
End If
connHost.Close()
handlerActive = False
End Sub
Below here is where i add users to my List called "users"
If (handlerActive = False And users.Contains(user) = False And user.Trim().CompareTo("") <> 0 Or user.Trim().CompareTo("jtv") <> 0 Or user.Trim().Contains(" ") = False Or user.Trim().Contains(".") = False) Then
users.Add(user)
End If

Better way to print out rows from a datatable in vb.net

I am new to vb.net and I am trying to query a database and print out the records in the row to the console window. I got it to work, but I have a feeling that there is a more concise way to do this. One thing that I am sure is wrong is that I had to convert the dataset to a datatable to be able to retrieve the values. Is that correct? Could you take a look at the code below (especially the for loop) and let me know what I can improve upon?
Thanks!
Module Module1
Sub Main()
Dim constring As String = "Data Source=C:\Users\test\Desktop\MyDatabase1.sdf"
Dim conn As New SqlCeConnection(constring)
Dim cmd As New SqlCeCommand("SELECT * FROM ACCOUNT")
Dim adapter As New SqlCeDataAdapter
Dim ds As New DataSet()
Try
conn.Open()
cmd.Connection = conn
adapter.SelectCommand = cmd
adapter.Fill(ds, "testds")
cmd.Dispose()
adapter.Dispose()
conn.Close()
Dim dt As DataTable = ds.Tables.Item("testds")
Dim row As DataRow
Dim count As Integer = dt.Columns.Count()
For Each row In dt.Rows
Dim i As Integer = 0
While i <= count - 1
Console.Write(row(i))
i += 1
End While
Console.WriteLine(Environment.NewLine())
Next
Catch ex As Exception
Console.WriteLine("There was an error")
Console.WriteLine(ex)
End Try
Console.ReadLine()
End Sub
End Module
Here is how I would rewrite this for a few reasons:
1) You should always use Using statements with disposable objects to ensure they are correctly cleaned up. You had a good start with the dispose commands, but this way is safer.
2) It is more efficient to use ExecuteReader than loading everything into a dataset.
3) Your try/catch statement should include object creation as well as execution.
Finally, in response to your question about datasets and datatables, that code was absolutely correct: a dataset consists of zero or more datatables, so you were just extracting the existing datatable from the dataset.
Try
Dim constring As String = "Data Source=C:\Users\test\Desktop\MyDatabase1.sdf"
Using conn As New SqlCeConnection(constring)
conn.Open()
Using cmd As New SqlCeCommand("SELECT * FROM ACCOUNT", conn)
Dim reader As SqlCeDataReader
reader = cmd.ExecuteReader()
Do While reader.Read
For i As Integer = 0 To reader.FieldCount - 1
Console.Write(reader.GetString(i))
Next
Console.WriteLine(Environment.NewLine())
Loop
End Using
End Using
Catch ex As Exception
Console.WriteLine("There was an error")
Console.WriteLine(ex)
End Try
Console.ReadLine()
End Sub
One last note: since you are just printing to the console, it doesn't matter as much, but whenever you deal with a lot of strings, especially those that are to be concatenated, you should always consider using System.Text.StringBuilder.
Here is an example rewrite of the loop that prints to the console using stringbuilder (builds the string in memory, then dumps it to the console; I have also added the field name for good measure):
Dim sbOutput As New System.Text.StringBuilder(500)
For i As Integer = 0 To reader.FieldCount - 1
If sbOutput.Length <> 0 Then
sbOutput.Append("; ")
End If
sbOutput.Append(reader.GetName(i)).Append("=").Append(reader.GetString(i))
Next
sbOutput.AppendLine()
Console.Write(sbOutput.ToString)