How to pass CommandText to another MySqlCommand? - mysql

The title is a bit furviant, I'll try to explain better. So in my application I've two connection string, one for the local database, and another for the web database. This two database must be updated with the same records. Now in my app when I add a record in the local database I execute a function that pass the MySqlCommand object to another function that use another connection string for the web database. In this second function I need to execute the same operation already performed in the local database. Example code:
Function local database
Dim query = "INSERT INTO text_app (name, last_name)
VALUES(#namep, #last_namep)"
Dim MySqlCommand = New MySqlCommand(query, dbCon)
MySqlCommand.Parameters.AddWithValue("#namep", name.Text)
MySqlCommand.Parameters.AddWithValue("#last_namep", last_name.Text)
MySqlCommand.ExecuteNonQuery()
Sync.SyncOut(MySqlCommand) 'Pass the object to another function
Function web database (SyncOut)
Using dbCon As MySqlConnection = establishWebConnection()
Try
dbCon.Open()
Dim MySqlCommand = New MySqlCommand(query_command, dbCon)
MySqlCommand.ExecuteNonQuery()
Return True
Catch ex As Exception
MessageBox.Show(ex.Message)
End Try
End Using
Now query_command contain the MySqlCommand passed from the local function, dbCon is the new connection object.
When I perform the .ExecuteNonQuery on the SyncOut function I get this error:
Invalid Cast from 'MySqlCommand' type to 'String' type.
What I need is take the .CommandText property contained in the query_command, but I can't access to this property.
What exactly am I doing wrong? How I can achieve this?

The first thing to do is change the name of the variable that represent the MySqlCommand. I can't find any plausible reason to allow a confusion as this to spread along your code. Do not name a variable with the same name of its class even if the language permits, it is very confusing
Dim query = "INSERT INTO text_app (name, last_name)
VALUES(#namep, #last_namep)"
Dim cmd = New MySqlCommand(query, dbCon)
and, of course, change every reference to the old name with the new one.
Now in the declaration of SyncOut write
Public Sub SyncOunt(ByVal cmd As MySqlCommand)
...
' Do not forget to open the connection '
dbCon.Open()
Dim aDifferentCommand = cmd.Clone()
aDifferentCommand.Connection = dbCon
....

Related

Parameterize SQL Queries

I want parameterize some SQL Statements so my code isn't vunerable to SQL Injections any longer But i have actually no plan how to parameterize for example a where clause.
Dim accID As String = DatabaseConnecter.readField("SELECT ID FROM accounts WHERE accountname ='" & user & "' AND password='" & pw & "';")
The Problem is if you type in a given username, for example test and extend the username with. You can log in without entering the password into the Application.
Edit:
Public Function readField(ByVal sql As String) As String
Dim output As String = "ERROR"
Using cn = New MySqlConnection(connString.ToString())
Using cmd = New MySqlCommand(sql, cn)
cn.Open()
Using rd = cmd.ExecuteReader()
Try
rd.Read()
output = rd.GetString(0)
rd.Close()
Catch ex As Exception
End Try
End Using
cn.Close()
End Using
End Using
Return output
End Function
ยดยดยด
To have a parameterized query you need to create parameters and write a proper SQL text where, in place of values typed directly from your user, you have parameter placeholders.
So, for example, you sql text should be something like this
Dim sqlText = "SELECT ID FROM accounts WHERE accountname =#name AND password=#pwd"
Now you have a parameterized text, but stil we need to create the parameters that will be sent to the database engine together with your sql command.
You can create the parameter (two in this case) in this way before calling the method that executes the query
Dim p1 as MySqlParameter = new MySqlParameter("#name", MySqlDbType.VarChar)
p1.Value = user
Dim p2 as MySqlParameter = new MySqlParameter("#pwd", MySqlDbType.VarChar)
p2.Value = password
Dim pms As List(Of MySqlParameter) = new List(Of MySqlParameter)()
pms.Add(p1)
pms.Add(p2)
Now we need to pass this list to your method (and this requires changes to your method signature)
DatabaseConnecter.readField(sqlText, pms)
The method itself should change to something like
Public Function readField(ByVal sql As String, Optional pms As List(Of MySqlParameter) = Nothing) As String
Dim output As String = "ERROR"
Using cn = New MySqlConnection(connString.ToString())
Using cmd = New MySqlCommand(sql, cn)
cn.Open()
' This block adds the parameter defined by the caller to the command
' The parameters are optional so we need to check if we have really received the list or not
if pms IsNot Nothing Then
cmd.Parameters.AddRange(pms.ToArray())
End If
Using rd = cmd.ExecuteReader()
Try
rd.Read()
output = rd.GetString(0)
rd.Close()
Catch ex As Exception
End Try
End Using
' no need to close when inside a using block
' cn.Close()
End Using
End Using
Return output
End Function
The method now has an optional parameter that will contain the list of the parameters required by the query (or nothing if your query doesn't require parameters). This list is added to the command parameters collection and the query is now executed.
Final Note: Storing passwords in clear text into a database is a well known security problem. I suggest you to search about how to store passwords in a database.
Private Function GetID(User As String, pw As String) As String
Using cmd As New SqlCommand("SELECT ID FROM accounts WHERE accountname =#user AND password=#password", New SqlConnection(SQLConnString))
cmd.Parameters.AddWithValue("#user", User)
cmd.Parameters.Add("#password", SqlDbType.NVarChar)
cmd.Parameters("#password").Value = pw
Try
cmd.Connection.Open()
Return cmd.ExecuteScalar()
Catch ex As Exception
'handle error
Return Nothing
Finally
cmd.Connection.Close()
End Try
End Using
End Function
I've demostrated two methods of setting the parameters. Search for more info or comparison.

Transfer the sum of query to label

the query is working on mysql command line but how can i put the sum to the label
Dim SDA As New MySqlDataAdapter
Dim bSource As New BindingSource
Dim dbDataSet As New DataTable
Try
MysqlConn.Open()
Dim Query As String
Query = "select sum(No_Of_Case_To_Be_Deliver) from ordered= '" & totalcase.Text & "'"
COMMAND = New MySqlCommand(Query, MysqlConn)
SDA.SelectCommand = COMMAND
SDA.Fill(dbDataSet)
bSource.DataSource = dbDataSet
MysqlConn.Close()
Catch ex As Exception
MessageBox.Show(ex.Message)
Finally
MysqlConn.Dispose()
End Try
Never concatenate strings to build an Sql statement. Use parameters. You are risking damage to your database.
A DataAdapter will open and close its connection for you as part of the .Fill method. However, if it finds the connection open it leaves it open.
Glad to see you called .Dispose on your connection but you can save yourself the trouble by using `Using...End Using blocks. This will ensure that your database objects are closed and disposed even if there is an error.
Now to the code. You are not Filling or Updating anything so you don't need a DataAdapter for this query. You are not Binding anything so no BindingSource. Bad name for DataTable (dbDataSet) because a DataSet is a different type of object. Anyone trying to maintain your code could be easily confused.
By using parameters you not only save yourself from SQL injection but greatly simplify the Sql statement. No worries about double quotes, single quotes, etc.
Since you are retrieving only a single piece of data, you can use .ExecuteScalar which returns the first column of the first row of the result set.
I separated the code into a Data Access function and User Interface part. This way you can migrate your application to a different platform, say a web app, by just picking up the function as a whole.
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
lblTotalCases.Text = DirectCast(GetTotalCases(CInt(totalcase.Text)), String)
End Sub
Private Function GetTotalCases(OrderID As Integer) As Integer
Dim TotalCases As Integer
'I made up a query since your query didn't make sense.
Dim Query = "select sum(No_Of_Case_To_Be_Deliver) from OrderDetails Where OrderID = #ID;"
Using MysqlConn As New MySqlConnection("Your Connection String")
Using Command As New MySqlCommand(Query, MysqlConn)
Command.Parameters.Add("#ID", MySqlDbType.Int32).Value = OrderID
MysqlConn.Open()
TotalCases = CInt(Command.ExecuteScalar)
End Using
End Using
Return TotalCases
End Function

How to make a vb application work with a SQL database from any computer?

Most of my programming experience is c-based, so I don't have the clearest understanding of how SQL (specifically, the mySQL platform) works.
My vb.net application works completely fine on the computer I made it on. However, if I download it onto another computer, the mySQL connection fails to open. From what I've read on other StackOverflow posts, that's the fault of the connection string. My connection string looks something like this:
"server=xxx.xxx.xxx.xxx;uid=username;pwd=password;database=db;default command timeout=300"
Again, for the base computer, this works fine without any problems at all. It's when I run the program from a different computer when I run into trouble. Do I need to change the server number? I've tried doing that, but the program still doesn't work. Is there one more field I need to add into the string? Or do I need to configure mySQL settings in some way on each computer? I would like to change the program in a way that allows anybody to use it immediately upon download. Would any of the methods listed above work, or a completely different one entirely?
Thanks.
This is a simple class I use with MySQL. Replace everything in [bracket] by your values. if It's not working look at your firewall and also use MySQL WorkBench to create user/password/permission/schema. Make sure you can connect to your database with MySQL workbench then you know you have everything setup right.
Imports MySql.Data.MySqlClient
Public Class mysql
'Connection string for mysql
Public SQLSource As String = "Server=[x.x.x.x];userid=[yourusername];password=[yourpassword];database=[defaultdatabaseifany];"
'database connection classes
Private DBcon As New MySqlConnection
Private SQLcmd As MySqlCommand
Public DBDA As New MySqlDataAdapter
Public DBDT As New DataTable
' parameters
Public Params As New List(Of MySqlParameter)
' some stats
Public RecordCount As Integer
Public Exception As String
Function ExecScalar(SQLQuery As String) As Long
Dim theID As Long
DBcon.ConnectionString = SQLSource
Try
DBcon.Open()
SQLcmd = New MySqlCommand(SQLQuery, DBcon)
'loads params into the query
Params.ForEach(Sub(p) SQLcmd.Parameters.AddWithValue(p.ParameterName, p.Value))
'or like this is also good
'For Each p As MySqlParameter In Params
' SQLcmd.Parameters.AddWithValue(p.ParameterName, p.Value)
' Next
' clears params
Params.Clear()
'return the Id of the last insert or result of other query
theID = Convert.ToInt32(SQLcmd.ExecuteScalar())
DBcon.Close()
Catch ex As MySqlException
Exception = ex.Message
theID = -1
Finally
DBcon.Dispose()
End Try
Return theID
End Function
Sub ExecQuery(SQLQuery As String)
DBcon.ConnectionString = SQLSource
Try
DBcon.Open()
SQLcmd = New MySqlCommand(SQLQuery, DBcon)
'loads params into the query
Params.ForEach(Sub(p) SQLcmd.Parameters.AddWithValue(p.ParameterName, p.Value))
'or like this is also good
'For Each p As MySqlParameter In Params
' SQLcmd.Parameters.AddWithValue(p.ParameterName, p.Value)
' Next
' clears params
Params.Clear()
DBDA.SelectCommand = SQLcmd
DBDA.Update(DBDT)
DBDA.Fill(DBDT)
DBcon.Close()
Catch ex As MySqlException
Exception = ex.Message
Finally
DBcon.Dispose()
End Try
End Sub
' add parameters to the list
Public Sub AddParam(Name As String, Value As Object)
Dim NewParam As New MySqlParameter(Name, Value)
Params.Add(NewParam)
End Sub
End Class

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.

How to execute two separate queries from one DBCommand object?

I have the following code that tries to get records from two different tables and then add them to the particular comboboxes. Only the first query works and the second one is ignored.
Try
sqlConn = New MySqlConnection
connStr = New String("Server = localhost; Database = gen_database; Uid = root; Pwd =")
sqlConn.ConnectionString = connStr
myCommand = New MySqlCommand("Select DevCompanyName from developer_name_table; Select DevType from development_type_table")
myCommand.CommandType = CommandType.Text
myCommand.Connection = sqlConn
ComboBox1.Items.Clear()
sqlConn.Open()
MsgBox("Connection Open.")
dR = myCommand.ExecuteReader()
Do While dR.Read()
ComboBox1.Items.Add(dR("DevCompanyName"))
ComboBox2.Items.Add(dR("DevType")) 'Error shows here Could not find specified column in results: DevType
Loop
Catch ex As MySqlException
MsgBox(ex.ToString)
Finally
dR.Close()
sqlConn.Close()
End Try
I can think of another way which is to do it in multiple query but can the code be simplified to something like this?
Using a DBDataReader with 2 queries, only only the first executes because there is no way to signal which table/query each read item is from. Your "double read" loop seems to assume they will be returned at the same time (rather than one query after the other - the same way they are sent to the DBCOmmand); if it did work that way, it would fail whenever there are not the same number of rows in each table.
Using DataTables affords you the chance to simply bind the result to your combos rather than copying data into them:
Dim SQL = "SELECT * FROM Sample; SELECT * FROM Simple"
Dim ds As New DataSet
Using dbcon As New MySqlConnection(MySQLConnStr),
cmd As New MySqlCommand(SQL, dbcon)
dbcon.Open()
Dim da As New MySqlDataAdapter(cmd)
da.Fill(ds)
End Using
' debug results
Console.WriteLine(ds.Tables.Count)
Console.WriteLine(ds.Tables(0).Rows.Count)
Console.WriteLine(ds.Tables(1).Rows.Count)
If I look at the output window, it will print 2 (tables), 10000 (rows in T(0)) and 6 (rows in T(1)). Not all DBProviders have this capability. Access for instance will choke on the SQL string. Other changes to the way your code is composed:
DBConnections and DBCommand objects need to be disposed. They allocate resources, so they need to be created, used and disposed to release those resources.
The Using block does that for us: The target objects are created at the start and closed and disposed at End Using.
The code above stacks or combines 2 such blocks.
The code fills a DataSet from the query, in this case creating 2 tables. Rather than copying the data from one container to another (like a control), you can use a DataTable as the DataSource:
cboDevName.DataSource = ds.Tables(0)
cboDevName.DisplayMember = "DevName" ' column names
cboDevName.ValueMember = "Id"
cboDevType.DataSource = ds.Tables(1)
cboDevType.DisplayMember = "DevType"
cboDevType.ValueMember = "DevCode"
The result will be all the rows from each table appearing in the respective combo control. Typically with this type of thing, you would want the ID/PK and the name which is meaningful to the user in the query. The user sees the friendly name (DisplayMember), which the code can easily access the unique identifier for the selection (ValueMember).
When using bound list controls, rather than using SelectedIndex and the SelectedIndexChanged event, you'd use SelectedValue and SelectedItem to access the actual data.
MSDN: Using Statement (Visual Basic)
You can use .net connector (download here)
Then you can install it and add it to your project (project -> references -> add -> browse).
Finally add import:
Imports MySql.Data.MySqlClient
So you'll be able to use this:
Dim connStr as String = "Server = localhost; Database = gen_database; Uid = root; Pwd ="
Dim SqlStr as String = "Select DevCompanyName from developer_name_table; Select DevType from development_type_table"
Dim ds As DataSet = MySqlHelper.ExecuteDataset(CnStr, SqlStr)
ds will contain two datatables: one for each query