Execute multiple stored procedures in VB.NET - mysql

I'm creating a WinService in VB.NET to get some data from a Table, do some things with these data and then upload new data into this Table.
What I need is something like this:
Dim conn As New MySqlConnection(my_connString)
conn.Open()
Dim cmd As New MySqlCommand("my_Stored_Procedure_1", conn)
cmd.CommandType = CommandType.StoredProcedure
Dim reader As MySqlDataReader = cmd.ExecuteReader()
While reader.Read()
Try
' SP to SELECT Data from DB table '
Dim columnData As String
columnData = reader("ColumnName")
columnData_2 = reader("ColumnName_2")
' (...) Do something with this Data '
Try
' SP to UPDATE Data into the same DB table '
'cmd.Dispose() '
cmd = New MySqlCommand("my_Stored_Procedure_2", conn)
cmd.CommandType = CommandType.StoredProcedure
cmd.ExecuteReader()
' (...) Do something else '
Catch ex As Exception
Console.WriteLine("ERROR: " & ex.Message)
End Try
Catch ex As Exception
Console.WriteLine("ERROR: " & ex.Message)
End Try
End While
reader.Close()
conn.Close()
The problem is that this doesn't work. It says There is already an open DataReader associated with this Connection which must be closed first. So I tried to create different SQL commands, close and re-open the connection, and create different connections like suggested here but all of them without success. This class seems to be useful but that's a lot of code for a simple (?) task. I've read a lot of similar questions but I didn't found what I need yet.
How can I handle this issue? Some help would be nice.

This sure looks like a duplicate of the question you linked to, but the answer there doesn't provide a lot of detail on how to fix the error. As the error says, you can only have one open reader per connection, so you need to use a different connection for the update. You say you have tried that, but perhaps your attempt was incorrect. As suggested in the linked question, you should also use Using statements for resource management.
So, you probably want something like this (untested, of course!):
Try
Using conn1 As New MySqlConnection(my_connString),
conn2 As New MySqlConnection(my_connString)
conn1.Open()
conn2.Open()
Using cmd1 As New MySqlCommand("my_Stored_Procedure_1", conn1)
cmd1.CommandType = CommandType.StoredProcedure
Using reader1 As MySqlDataReader = cmd1.ExecuteReader()
While reader1.Read()
' SP to SELECT Data from DB table '
Dim columnData As String
columnData = reader1("ColumnName")
columnData_2 = reader1("ColumnName_2")
' (...) Do something with this Data '
' SP to UPDATE Data into the same DB table '
Using cmd2 As New MySqlCommand("my_Stored_Procedure_2", conn2)
cmd2.CommandType = CommandType.StoredProcedure
Using reader2 As MySqlDataReader = cmd2.ExecuteReader()
' (...) Do something else '
End Using ' reader2
End Using ' cmd2
End While
End Using ' reader1
End Using ' cmd1
End Using ' conn1, conn2
Catch ex As Exception
Console.WriteLine("ERROR: " & ex.Message)
End Try
As you can see from the levels of nesting, there is quite a lot going on here in terms of the resource scopes, so you may want to refactor this into multiple methods. You could also use a data adapter to populate a DataTable for the results of my_Stored_Procedure_1, instead of using a DataReader, and then just need a single connection (assuming the data isn't too large for that).

Related

I get two error messages when trying to insert a data to MySQL table

I get these two error messages,
'ExecuteReader' is not a member of 'WindowsApplication1.MySqlCommand'
Too many arguments to 'Public Sub New()' {This error occurs in (query, conn) field}
Little explanation about what I am trying to do
Here I am trying to make an application that will input data to separate databases (Men, Women and Kids) using If pub = "" (pub is mentioned in another) Then according to ComboBox1 the table may get differ. According to the table the values need to be inserted.
This is the code I am using
Dim T As String
T = ComboBox1.Text
If pub = "Women" Then
conn = New MySqlConnection
conn.ConnectionString = "server=localhost;user=root;password=1234;database=women_clothing"
Dim reader As MySqlDataReader
Try
conn.Open()
Dim query As String
query = "INSERT INTO [" + T + "] VALUES ('" & TextBox1.Text & "','" & TextBox2.Text & "','" & TextBox3.Text & "','" & TextBox4.Text & "'," & Val(TextBox5.Text) & "," & Val(TextBox6.Text) & ");"
command = New MySqlCommand(query, conn)
reader = Command.ExecuteReader
MessageBox.Show("Data Saved")
conn.Close()
Catch ex As Exception
MessageBox.Show(ex.Message)
Finally
conn.Dispose()
End Try
loadtable()
ElseIf pub = "Men" Then
Else
End If
loadtable()
Thank You
You shouldn't use ExecuteReader for executing a statement that inserts data. ExecuteReader is for SELECT queries. ExecuteNonQuery is for INSERTs
Your code should look like:
Using conn = New MySqlConnection("server=localhost;user=root;password=1234;database=women_clothing")
Using cmd = New MySqlCommand("INSERT INTO tableName VALUES (#p1,#p2,#p3,#p4,#p5,#p6)", conn)
conn.Open()
cmd.Parameters.AddWithValue("#p1", TextBox1.Text)
cmd.Parameters.AddWithValue("#p2", TextBox2.Text)
cmd.Parameters.AddWithValue("#p3", TextBox3.Text)
cmd.Parameters.AddWithValue("#p4", TextBox4.Text)
cmd.Parameters.AddWithValue("#p5", TextBox5.Text)
cmd.Parameters.AddWithValue("#p6", TextBox6.Text)
cmd.ExecuteNonQuery()
End Using
End Using
I've also demonstrated how to use parameters; always use parameters. Your code as it stands is massively at risk of SQL injection attack hacking, and if that doesn't dissuade you enough to not write code in this way, bear in mind that your app will crash if anyone enters an apostrophe in one of your textboxes (which will then give the more astute tech whizzkids using it the idea that it's prone to injection hacking, then they will break into it) which looks bad to the end user (and makes them complain to your helpdesk/you).
It doesn't matter that "it's only a simple app for my grandma to index her vinyl collection" - this is about NOT learning a pattern of behavior that is plain risky, bordering on career-limiting if you want to take your coding skills into the software development world. As the VTech hack linked above shows, there are now 5 million images of children floating around out there whose parents never authorized their release, all because a few people didn't take proper precautions in doing their job. If any of my developers wrote an injection prone SQL, given the nature of one of the industries I work in, they'd just get fired.
Also, please get into the habit of renaming your textboxes after you add them to the form. It's incredibly difficult for anyone (yourself included, 6 months down the line) to have to constantly look up "hmm, what is the first name textbox? is it textbox2 or textbox3?" it takes about 2 seconds to type something new in the (Name) line of the property grid after you add the textbox to the form; firstNameTextBox is far better than textBox2
With credit to #Caius, this is a variation using ConnectionStringBuilder, a With block, a composite Using block and string interpolation, just for illustration.
NOTE the use of As in lieu of = when declaring the connection and command; this is to formally set the types rather than rely on Infer to determine them.
Dim connStr As New MySqlConnectionStringBuilder() With {
.Server = "localhost",
.Database = "women_clothing",
.UserID = "root",
.Password = "1234"
}
' -> "server=localhost;database=women_clothing;user id=root;password=1234"
Using conn As New MySqlConnection(connStr.ConnectionString),
cmd As New MySqlCommand($"INSERT INTO [{T}] VALUES (#p1, #p2, #p3, #p4, #p5, #p6)", conn)
conn.Open()
With cmd
.Parameters.AddWithValue("#p1", TextBox1.Text)
.Parameters.AddWithValue("#p2", TextBox2.Text)
.Parameters.AddWithValue("#p3", TextBox3.Text)
.Parameters.AddWithValue("#p4", TextBox4.Text)
.Parameters.AddWithValue("#p5", TextBox5.Text)
.Parameters.AddWithValue("#p6", TextBox6.Text)
.ExecuteNonQuery()
End With
conn.Close()
End Using

Redefining/Re-setting parameters in MySQL query

I have the following code for inserting data into a table using a MySQL query in VB.NET
Dim MySqlCmdStr = "INSERT INTO tb_idlink(id1,id2) " &
"VALUES (#par1,#par2)"
MySqlCmd.CommandText = MySqlCmdStr
Dim checkedItem As Object
For Each checkedItem In CheckedListBox_1.CheckedItems
Try
MySqlCmd.Connection = MySqlConn
With MySqlCmd
.Parameters.AddWithValue("#par1", currentID)
.Parameters.AddWithValue("#par2", checkedItem.ToString())
End With
MySqlConn.Open()
MySqlCmd.ExecuteNonQuery()
MySqlConn.Close()
Catch ex As MySqlException
MessageBox.Show(ex.Message)
End Try
Next
My problem is if I have more than one box checked in CheckedListBox_1 then on the second loop an exception that says something like "parameter #par1 already defined". Is there a way I can re-define it? I'm not entirely familiar with the whole API.
Also, I'm not 100% sure if looping it is the best way to do this, but it's the first thing that popped into my head. Feel free to suggest an alternative way of doing this.
You dont redefine the parameters, you just supply a new value:
Dim SQL = "INSERT INTO tb_idlink (id1,id2) VALUES (#par1,#par2)"
Using dbcon As New MySqlConnection(MySQLConnStr)
Using cmd As New MySqlCommand(SQL, dbcon)
' define the parameter names and types
cmd.Parameters.Add("#par1", MySqlDbType.Int32)
cmd.Parameters.Add("#par2", MySqlDbType.Int32) ' ????
dbcon.Open()
For Each checkedItem In CheckedListBox1.CheckedItems
' provide the parameter values
cmd.Parameters("#par1").Value = currentID
cmd.Parameters("#par2").Value = Convert.ToInt32(checkedItem)
cmd.ExecuteNonQuery()
Next
End Using
End Using
Your code appears to reuse a global connection, that is ill-advised. The above uses Using blocks to create, use and and dispose of the DbConnection and DbCommand objects in the smallest scope possible
You should favor Add rather than AddWithValue so you can specify the datatype rather than forcing the the DB Provider guess and reduce the chance of data type mismatch errors.
These datatypes are a guess; CurrentId is not defined anywhere and given the names, both seem to be integers not strings.

MySqlDataReader not returning data

As part of a project to import data into wordpress via screen scraping I've a database table of old and new URL's stored in a MySQL database. In the example below the ExecuteReader command doesn't appear to be returning any data (-1 rows effected), I've ran the SQL via workbench and that returns data, so it's not the SQL or data in the database.
At other times within the code I've called ExecuteNonQuery() and ExecuteScalar() both without issue (so it isn't the connection string).
Any ideas what to try next?
Dim SQL As String
Dim conn As MySqlConnection = New MySqlConnection(_CONNECTIONSTRING)
SQL = "SELECT OrgURL, NewURL FROM `wp_user`.`tbl_linkdata`"
Try
conn.Open()
Dim cmd As MySqlCommand = New MySqlCommand(SQL, conn)
Dim dr As MySqlDataReader = cmd.ExecuteReader()
While (dr.Read)
LinkHashMap.Add(dr.GetString(0), dr.GetString(1))
End While
Console.ForegroundColor = ConsoleColor.Cyan
Console.WriteLine("The Hash map contains " + dr.RecordsAffected + " rows")
dr.Close()
Catch ex As Exception
Console.ForegroundColor = ConsoleColor.Red
Console.WriteLine("Exception loading the hashtable : " + ex.Message)
Finally
conn.Dispose()
End Try
DataReader.RecordsAffected always returns -1 for a SELECT command. What does LinkHashMap.Count return? In MySqlDataReader it is the same:
"The number of rows changed, inserted, or deleted. -1 for SELECT
statements"
If you want to count the number of records you can use LinkHashMap.Count.
You: "LinkHashMap is "Nothing" "
How do you want to add something to it without initializing it first? A NullReferenceException should have happened. So initialize the dictionary (or whatever it is) first via constructor:
Dim LinkHashMap As New Dictionary(Of String, String)
While (dr.Read)
LinkHashMap.Add(dr.GetString(0), dr.GetString(1))
End While

VB .NET Oracle Query

I cannot seem to find the answer to my problem anywhere on the internet. I have seen solutions for others such as adding keys to appsettings etc, none of which have worked for me.
Imports Oracle.DataAccess.Client
Imports Oracle.DataAccess.Types
Imports System.Data
Sub Button1Click(sender As Object, e As EventArgs)
Dim oradb As String = "DATA SOURCE=INITIATE;PASSWORD=pASS;PERSIST SECURITY INFO=True;USER ID=uSER"
Dim conn As New OracleConnection(oradb)
Try
Dim sql As String = "select MEMRECNO from INIT.MPI_MEMHEAD where MEMIDNUM = '" + txtMRN.Text + "'"
Dim cmd As New OracleCommand(sql, conn)
cmd.CommandType = CommandType.Text
Dim dr As OracleDataReader = cmd.ExecuteReader()
dr.Read()
txt1.Text = dr.GetInt32(0)
Catch ex As Exception
richTextBox1.Text = richTextBox1.Text + vbCrLf + ex.Message
End Try
End Sub
Now, when I run this code, if I enter a value in the top 25 records (when visually looking at the table in Oracle) it returns the result. However, when I enter a value that might be record number 1 million, i get this error: "Operation is not valid due to the current state of the object."
Yes, the value does exist. Because if I run the exact same query in Oracle, I get the result.
This leads me to believe that the connection is timing out, closing, or there is a limit on how many rows can be returned using the Oracle Data Access Client.

How to use data reader in vb.net

Please help, how do I really use data reader in vb.net. I'm using odbc to connect mysql and vb.net.
Function I declared on a module:
Public Function form2search(ByVal drugname As String) As OdbcDataReader
cmd.CommandText = "SELECT * FROM drug WHERE Drug_name LIKE'%" & drugname & "' "
Return cmd.ExecuteReader
End Function
text_changed event:
con.drugname=textBoxdrugname.text
Dim rdr As Odbc.OdbcDataReader
rdr = con.form2search(drugname)
if rdr.hasrows=true then
rdr.read()
TextBoxdrugname.Text = rdr("Drug_name").ToString
TextBoxdrugcode.Text = rdr("Drug_code").ToString
drugtype.Text = rdr("Drug_type").ToString
end if
I see a result, but it only loads the first item on the database. I've put this code in the text_changed event. What's the proper way of doing this? And what's wrong with the 2nd code, why is it only loading the first data
As you can see the con is the module where I declared the function. Then I created an object of it in the form.
The DataReader is a low level implementation that does not support navigation and only reads a single row every time you call
reader.Read()
For a Windows Forms app you probably should use a DataSet / DataTable approach or a ORM. And you should consider using the mysql connector net over the odbc driver. It is available at mysql.com.
Here is a little demo code:
dim table as new DataTable("table1")
' Create a Connection
using conn as new MysqlConnection("...connectionstring")
conn.Open() ' Open it
' Create a new Command Object
using cmd as new MysqlCommand("SELECT * FROM table", conn)
' Create a DataAdapter
' A DataAdapter can fill a DataSet or DataTable
' and if you use it with a CommandBuilder it also
' can persist the changes back to the DB with da.Update(...)
using da as new MysqlDataAdapter(cmd)
da.Fill(table) ' Fill the table
end using
end using
end using
' A Binding Source allows record navigation
dim bs as new BindingSource(table, nothing)
' You can bind virtually every property (most common are "text" "checked" or "visible"
' of a windows.forms control to a DataSource
' like a DataTable or even plain objects
textBox1.DataBindings.Add("Text", bs, "columnName")
' Now you can navigate your data
bs.MoveNext()
' Even a ComboBox can be bound to a List and display the related value
' of your current row
comboBox1.DataSource = table2
comboBox1.DisplayMember = "name"
comboBox1.ValueMember = "id"
comboBox1.DataBindings.Add("SelectedValue", bs, "id")
You have to just put the object of data reader in while loop.Here is the sample code:
dr = myCommand.ExecuteReader()
While dr.Read()
'reading from the datareader
MessageBox.Show("colname1" & dr(0).ToString())
MessageBox.Show("colname2" & dr(1).ToString())
MessageBox.Show("colname3" & dr(2).ToString())
MessageBox.Show("colname4" & dr(3).ToString())
MessageBox.Show("colname5" & dr(4).ToString())
'displaying the data from the table
End While
dr.Close()