Issue connecting to MySQL using a Class for Connectionstring and Command? - vb.net

Apologies for the poor wording in the title, I'm new to OOP VB and I have no idea how to describe the problem I'm having!
I'm attempting to create a login form that's connecting to MySQL via a class that handles the logging in.
It connects no problem but I'm having issues with creating an SQL command to pull data from the database.
Here's the SQLConnection Class
Imports MySql.Data.MySqlClient
Imports System.Threading
Public Class SQLConnection
Private serverhost As String
Private db As String
Private userid As String
Private pwd As String
Private Shared cn As New MySqlConnection
Private Shared commandstring As New MySqlCommand
Public Property Server As String
Get
Return serverhost
End Get
Set(ByVal value As String)
serverhost = value
End Set
End Property
Public Property Database As String
Get
Return db
End Get
Set(ByVal value As String)
db = value
End Set
End Property
Public Property User As String
Get
Return userid
End Get
Set(ByVal value As String)
userid = value
End Set
End Property
Public Property Password As String
Get
Return pwd
End Get
Set(ByVal value As String)
pwd = value
End Set
End Property
Public Property command As MySqlCommand
Get
Return commandstring
End Get
Set(ByVal value As MySqlCommand)
commandstring = value
End Set
End Property
Private Shared ReadOnly Property Conn As MySqlConnection
Get
Return cn
End Get
End Property
Public Shared Function TryConn(ByVal obj As SQLConnection) As Boolean
Try
Dim connectionstring As String =
"server=" & obj.Server &
";database=" & obj.Database &
";user id=" & obj.User &
";password=" & obj.Password
cn = New MySqlConnection
cn.ConnectionString = connectionstring
If cn.State = ConnectionState.Closed Then
cn.Open()
End If
Return True
cn.ConnectionString = ""
Catch ex As Exception
Return False
End Try
End Function
End Class
Here's the login form Code snippet:
Try
Dim Conn As New SQLConnection
Dim reader As MySqlDataReader
With Conn
.Server = "localhost"
.Password = ""
.User = "root"
.Database = "customers"
End With
If SQLConnection.TryConn(Conn) = True Then
Dim Query As String
Query = String.Format("SELECT * FROM users WHERE Username = '{0}' AND Password = '{1}'", Me.UsernameTextBox.Text.Trim(), Me.PasswordTextBox.Text.Trim())
sql = New MySqlCommand(Query, Conn)
reader = sql.ExecuteReader
Dim count As Integer
count = 0
While reader.Read
count = count + 1
End While
If count = 1 Then
Me.Hide()
sqlloading.Show()
ElseIf count > 1 Then
errorform.FlatAlertBox1.Text = "Username and password are duplicate"
errorform.Show()
Else
errorform.FlatAlertBox1.Text = "Wrong username or password"
errorform.Show()
End If
Me.Hide()
Else
End If
Catch
End Try
When running this I get
"Value of type 'WindowsApplication1.SQLConnection' cannot be converted to 'MySql.Data.MySqlClient.MySqlConnection'.

The problem causing the error message appears to be on this line:
sql = New MySqlCommand(Query, Conn)
Because Conn is an instance of your SQLConnection type, but has to be a MySql.Data.MySqlClient.MySqlConnection instance, that you have created as a private property of SQLConnection.
You need to make a few changes:
Make Conn and TryConn methods normal, non shared methods
Make the cn field non shared.
Have the SQLConnection class implement IDisposable, making sure to dispose cn when it is disposed.
Change the login form code block to the below:
Try
Using Conn As New SQLConnection
With Conn
.Server = "localhost"
.Password = ""
.User = "root"
.Database = "customers"
End With
If SQLConnection.TryConn(Conn) = True Then
Const Query As String = "SELECT * FROM users WHERE Username = #username AND Password = #password"
' The line below fixes the error.
Using sql As MySqlCommand = New MySqlCommand(Query, Conn.Conn)
sql.Parameters.AddWithValue("#username", Me.UsernameTextBox.Text.Trim())
sql.Parameters.AddWithValue("#password", Me.PasswordTextBox.Text.Trim())
Using reader As MySqlDataReader = sql.ExecuteReader
Dim count As Integer
count = 0
While reader.Read
count = count + 1
End While
If count = 1 Then
Me.Hide()
sqlloading.Show()
ElseIf count > 1 Then
errorform.FlatAlertBox1.Text = "Username and password are duplicate"
errorform.Show()
Else
errorform.FlatAlertBox1.Text = "Wrong username or password"
errorform.Show()
End If
Me.Hide()
End Using
End Using
End If
End Using
Catch
' at least log the error?
End Try
This will solve you immediate problems, but there is room left for improvement. Instead of formatting your SQL yourself like this:
Query = String.Format(
"SELECT * FROM users WHERE Username = '{0}' AND Password = '{1}'",
Me.UsernameTextBox.Text.Trim(), Me.PasswordTextBox.Text.Trim())
You should really use SQL parameters to prevent SQL injection attacks, refer to the Little Bobby Tables story for reasons why. I have updated the code snippet above to improve this.
A final remark: you are now storing passwords as unencrypted plain text in your database. That is considered to be not secure. Passwords should always be stored and transmitted across the wire in an encrypted format.

Your Dim Conn As New SQLConnection is using the SQLConnection class.
If you're using MySQL, try have a look here.

I added the whole function from the login form to the SQLConnection class
Then Called it using:
Try
Dim Conn As New sqltryclass
With Conn
.Server = "localhost"
.Password = ""
.User = "root"
.Database = "adminapp"
End With
If sqltryclass.TryLogin(Conn) = True Then
Me.Hide()
sqlloading.Show()
Else
End If
Catch
End Try

Related

Database query not returning results from value

I am making a work management system and I am fairly new to Visual Basic.
What I am trying to do is retrieve the name of the employee from the database with the given ID. After that I want this name to be displayed in a Label. After that, he can press the Work Start or Work end buttons.
Here is the code:
Private Function employeeSearchwithID(PersonalNr As String) As String
Dim mitarbeiter As String
Dim r As DataRow
Access.ExecQuery("SELECT [Vorname], [Name] from [TA-Personal] WHERE ([Personal_Nr] = '" & PersonalNr & "');")
'Report and Abort on Erros or no Records found
If NoErros(True) = False Or Access.RecordCount < 1 Then Exit Function
r = Access.DBDT.Rows(0)
'Populate Label with Data
mitarbeiter = r.Item("Vorname") & " " & r.Item("Name")
Return mitarbeiter
End Function
It is used like this:
Private Sub tbxUserInput_KeyDown(sender As Object, e As KeyEventArgs) Handles tbxUserInput.KeyDown
If e.KeyCode = Keys.Enter Then 'employeeIDnumbersSelect()
Label5.Text = employeeSearchwithID(tbxUserInput.ToString)
End If
End Sub
So, the plan is to have this program running on a tablet connected to a scanner. Every employee will have a personal card. When they scan the card, I want their names to be displayed. Of course, the card will be with the ID. But I am having trouble with the names: when I give my personal number it comes up as an empty string.
I have a separate DB Module. I learned from a tutorial:
Imports System.Data.OleDb
Public Class DBControl
' DB Connection
Public DBCon As New OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0; Data Source=D:\recycle2000.mdb;")
'DB Command
Public DBCmd As OleDbCommand
'DB Data
Public DBDA As OleDbDataAdapter
Public DBDT As DataTable
'Public Myreader As OleDbDataReader = DBCmd.ExecuteReader
'Query Paramaters
Public Params As New List(Of OleDbParameter)
' Query Stats
Public RecordCount As Integer
Public Exception As String
Public Sub ExecQuery(Query As String)
'Reset Query Stats
RecordCount = 0
Exception = ""
Try
'Open a connection
DBCon.Open()
'Create DB Command
DBCmd = New OleDbCommand(Query, DBCon)
' Load params into DB Command
Params.ForEach(Sub(p) DBCmd.Parameters.Add(p))
' Clear params list
Params.Clear()
' Execute command & fill Datatable
DBDT = New DataTable
DBDA = New OleDbDataAdapter(DBCmd)
RecordCount = DBDA.Fill(DBDT)
Catch ex As Exception
Exception = ex.Message
End Try
' Close your connection
If DBCon.State = ConnectionState.Open Then DBCon.Close()
End Sub
' Include query & command params
Public Sub AddParam(Name As String, Value As Object)
Dim NewParam As New OleDbParameter(Name, Value)
Params.Add(NewParam)
End Sub
End Class
Without knowledge of the Access class, I have to recommend a different approach to querying the database. It is important to make sure that the database is not vulnerable to SQL injection, be it deliberate or accidental. The way to do that is to use what are known as SQL parameters: instead of putting the value in the query string, the value is supplied separately.
Private Function EmployeeSearchwithID(personalNr As String) As String
Dim mitarbeiter As String = String.Empty
Dim sql = "SELECT [Vorname], [Name] from [TA-Personal] WHERE [Personal_Nr] = ?;"
Using conn As New OleDbConnection("your connection string"),
cmd As New OleDbCommand(sql, conn)
cmd.Parameters.Add(New OleDbParameter With {.ParameterName = "#PersonalNr",
.OleDbType = OleDbType.VarChar,
.Size = 12,
.Value = personalNr})
conn.Open()
Dim rdr = cmd.ExecuteReader()
If rdr.Read() Then
mitarbeiter = rdr.GetString(0) & " " & rdr.GetString(1)
End If
End Using
Return mitarbeiter
End Function
Private Sub tbxUserInput_KeyDown(sender As Object, e As KeyEventArgs) Handles tbxUserInput.KeyDown
If e.KeyCode = Keys.Enter Then 'employeeIDnumbersSelect()
Dim employeeName = EmployeeSearchwithID(tbxUserInput.Text.Trim())
If String.IsNullOrEmpty(employeeName) Then
MessageBox.Show("Not found.", "Problem", MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
Else
Label5.Text = employeeName
End If
End If
End Sub
The Using command makes sure that the "unmanaged resources" involved in querying a database are cleaned up afterwards, even if something goes wrong.
You will need to change the value of OleDbType.VarChar and .Size = 12 to match the type and size of that column in the database.
The name of the parameter is only for convenience with OleDb because it is ignored in the actual query, which uses "?" as a placeholder. Please see OleDbCommand.Parameters Property for full information.
If it still does not work, then please enter the ID manually in the tbxUserInput and see if you can make it work that way.
Hang on... tbxUserInput.ToString should be tbxUserInput.Text. But everything else I wrote still applies.

a beginner in vb.net.. working on a login form

Imports MySql.Data.MySqlClient
Public Class Form1
Dim cmd As New MySqlCommand
Dim da As New MySqlDataAdapter
Dim con As MySqlConnection = JOKENCONN()
Public Function JOKENCONN() As MySqlConnection
Return New MySqlConnection("server=localhost; user id=root; password=; database =studentdb")
End Function
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
GroupBox1.Enabled = False
End Sub
Private Sub LBLLOGIN_CLICK(sender As Object, e As EventArgs) Handles lbllogin.Click
lbllogin.Text = "Login"
lbllogin.Text = "Login"
lblname.Text = "Hi, Guest"
If lbllogin.Text = "Login" Then
GroupBox1.Enabled = True
End If
End Sub
Private Sub BTNOK_CLICK(sender As Object, e As EventArgs) Handles btnok.Click
Dim Sql As String
Dim publictable As New DataTable
Try
If txtusername.Text = "" And txtpass.Text = "" Then
MsgBox("Password or username is incorrect!")
Else
Sql = "select ' from tbluseraccount where username='" & txtusername.Text & "' and userpassword='" & txtpass.Text & "'"
With cmd
.Connection = con
End With
da.SelectCommand = cmd
da.Fill(publictable)
If publictable.Rows.Count > 0 Then
Dim user_type As String
user_type = publictable.Rows(0).Item(4)
Name = publictable.Rows(0).Item(1)
If user_type = "Admin" Then
MsgBox("Welcome " & Name & "you login as Administrator")
lbllogin.Text = "logout"
lblname.Text = "Hi, " & Name
GroupBox1.Enabled = False
txtusername.Text = ""
txtpass.Text = ""
ElseIf user_type = "cetakoradi2" Then
MsgBox("Welcome " & Name & "you login as cetakoradi2")
lbllogin.Text = "logout"
lblname.Text = "Hi, " & Name
GroupBox1.Enabled = False
txtusername.Text = ""
txtpass.Text = ""
Else
End If
Else
MsgBox("contact administrator to register")
txtusername.Text = ""
txtpass.Text = ""
End If
da.Dispose()
End If
Catch ex As Exception
MsgBox(ex.Message)
con.Close()
End Try
End Sub
End Class
this the error i received
ExecuteReader CommandText property has not been properly initialized
i really need help on that. this is the error that i receives. thank you
Assuming that the name of the field represented in publictable.Rows(0).Item(4) is named user_type, then you could use the following:
'Declare the object that will be returned from the command
Dim user_type As String
'Declare the connection object
Dim con As OleDbConnection
'Wrap code in Try/Catch
Try
'Set the connection object to a new instance
con = JOKENCONN()
'Create a new instance of the command object
Using cmd As OleDbCommand = New OleDbCommand("SELECT user_type FROM tbluseraccount WHERE username=#0 AND userpassword=#1;", con)
'Paramterize the query
cmd.Parameters.AddWithValue("#0", txtusername.Text)
cmd.Parameters.AddWithValue("#1", txtpass.Text)
'Open the connection
con.Open()
'Use ExecuteScalar to return a single value
user_type = cmd.ExecuteScalar()
'Close the connection
con.Close()
End Using
Catch ex As Exception
'Display the error
Console.WriteLine(ex.Message)
Finally
'Check if the connection object was initialized
If con IsNot Nothing Then
If con.State = ConnectionState.Open Then
'Close the connection if it was left open(exception thrown)
con.Close()
End If
'Dispose of the connection object
con.Dispose()
End If
End Try
If (String.IsNullOrWhitespace(user_type)) Then
'Failed login
ElseIf (user_type = "Admin") Then
'Admin login
ElseIf (user_type = "cetakoradi2") Then
'cetakoradi2 login
Else
'Not a failed login, but also not an admin or cetakoradi2 either
End If
What this code does is setup a parameterized query to get just the user_type where the username and password match the parameterized values. Since there should only ever be one record that matches those conditions (presumably) then we're able to use ExecuteScalar to return just that single field value.
Just to reinforce the point, MySqlCommand.ExecuteScalar, just like the Microsoft counterparts, "executes the query, and returns the first column of the first row in the result set returned by the query. Extra columns or rows are ignored" and returns " The first column of the first row in the result set, or a null reference if the result set is empty ".
The proposed code by #David checks for this condition using IsNullOrWhitespace.
ExecuteScalar is effective but retrieves only one value at a time.
The other option pursued by the OP is to return a datarow, which is a valid approach if he wants to return several fields at the same time. In his example he retrieves two fields for variables user_type and Name respectively.
Be careful, VB.net like any other programming language has reserved keywords. If you do not take a habit of using good naming conventions you might one day stumble upon on one of those keywords, possibly hit obscure bugs. Name is not a good name for a variable and has the potential for confusion since every object has a name property.
To address the specific issue at hand, the error message ExecuteReader CommandText property has not been properly initialized is self-explanatory. What should have been done is simply:
With cmd
.Connection = con
.CommandText = Sql
End With
You defined a command, but did not tell it what to do. In your code variable Sql is defined but unused. With this missing bit there is a chance the code will work as expected.
Small details:
Not critical, but his condition does not work if you enter whitespace for example:
If txtusername.Text = "" And txtpass.Text = "" Then
An improvement is to simply trim the values from the textboxes:
If txtusername.Text.Trim = "" And txtpass.Text.Trim = "" Then
But I think what you want is not an And but Or. I don't think you want to allow logins without passwords.
Instead of doing multiple If/ElseIf you could have a Select Case

Database insert only works with integers, not strings

I'm attempting to insert into my database but this code only works when the variables are integers, any reason for this? Could using parameters fix this?
Public Class Floshcods
Private CardCodes As Integer
Private KeyWord As String
Private Definition As String
Public Sub New(ByVal CC As Integer)
CardCodes = CC
KeyWord = InputBox("Enter keyword")
Definition = InputBox("Enter definiton")
End Sub
Public Function GetCardCodes() As Integer
Return CardCodes
End Function
Public Function GetKeyWord() As String
Return KeyWord
End Function
Public Function GetDefinition() As String
Return Definition
End Function
End Class
================================================================
sub new()
Dim numb
Dim cn = ConnectDB()
Dim cmd As New MySqlCommand
Dim sql = "SELECT * FROM " & Tabelname & " ORDER by CardCode desc limit 1"
cmd.Connection = cn
cmd.CommandText = sql
cn.Open()
Dim result As MySqlDataReader = cmd.ExecuteReader
result.Read()
Try
numb = result.GetString(0)
Catch ex As Exception
MessageBox.Show("The selected table has no previous cards")
numb = 0
End Try
cn.Close()
Dim cardcode As Integer = numb + 1
Dim p As New Floshcods(cardcode)
Me.Controls("words" & 0).Text = p.GetKeyWord
Me.Controls("words" & 1).Text = p.GetDefinition
sql = "insert into " & Tabelname & " (CardCode,Keyword,Definition) VALUES (" & cardcode & ", " & p.GetKeyWord & ", " & p.GetDefinition & ");"
cmd.CommandText = sql
Try
cn.Open()
cmd.ExecuteNonQuery()
cn.Close()
Catch ex As Exception
MessageBox.Show("failed to add card")
End Try
end
If you need more of my code to understand, please ask.
The error I get is MySql.Data.MySqlClient.MySqlException: 'Unknown column 'test' in 'field list'.
Yes, the problem would be solved if you used SQL parameters for the values in the SQL query.
There are a few other changes that you might like to make to your code too:
It is usual to use properties rather than functions for values in a class.
Avoid Try..Catch where possible, e.g. if you can check for a value instead of having an error thrown, do so.
Some classes in .NET need to have .Dispose() called on their instances to tell it to clean up "unmanaged resources". That can be done automatically with the Using statement.
Option Strict On is useful to make sure that variable types are correct. You will also want Option Explict On. Option Infer On saves some keystrokes for when variable declarations have types obvious to the compiler.
So I suggest that you use:
Public Class Floshcods
Public Property CardCode As Integer
Public Property Keyword As String
Public Property Definition As String
Public Sub New(ByVal cardCode As Integer)
Me.CardCode = cardCode
Me.Keyword = InputBox("Enter keyword")
Me.Definition = InputBox("Enter definiton")
End Sub
End Class
and
Dim latestCardCode As Integer = 0
Dim sql = "SELECT CardCode FROM `" & Tabelname & "` ORDER BY CardCode DESC LIMIT 1;"
Using cn As New MySqlConnection("your connection string"),
cmd As New MySqlCommand(sql, cn)
cn.Open()
Dim rdr = cmd.ExecuteReader()
If rdr.HasRows Then
latestCardCode = rdr.GetInt32(0)
Else
MessageBox.Show("The selected table has no previous cards.")
End If
End Using
Dim cardcode = latestCardCode + 1
Dim p As New Floshcods(cardcode)
Me.Controls("words0").Text = p.Keyword
Me.Controls("words1").Text = p.Definition
sql = "INSERT INTO `" & Tabelname & "` (CardCode,Keyword,Definition) VALUES (#CardCode, #Keyword, #Definition);"
Using cn As New MySqlConnection("your connection string"),
cmd As New MySqlCommand(sql, cn)
cmd.Parameters.Add(New MySqlParameter With {.ParameterName = "#CardCode", .MySqlDbType = MySqlDbType.Int32, .Value = cardcode})
cmd.Parameters.Add(New MySqlParameter With {.ParameterName = "#Keyword", .MySqlDbType = MySqlDbType.VarChar, .Size = 64, .Value = p.Keyword})
cmd.Parameters.Add(New MySqlParameter With {.ParameterName = "#Definition", .MySqlDbType = MySqlDbType.VarChar, .Size = 99, .Value = p.Definition})
cn.Open()
Dim nRowsAdded As Integer = cmd.ExecuteNonQuery()
cn.Close()
If nRowsAdded = 0 Then
MessageBox.Show("Failed to add card.")
End If
End Using
You will need to change the .MySqlDbType and .Size to match how they are declared in the database.
I put backticks around the table name to escape it in case a reserved word is used or the name has spaces in it.
I changed the Functions to Properties in the Floshcods class and moved the InputBoxes to the User Interface code.
Public Class Floshcods
Public Property CardCodes As Integer
Public Property KeyWord As String
Public Property Definition As String
Public Sub New(ByVal CC As Integer)
CardCodes = CC
End Sub
End Class
I pulled out the data access code to separate methods so it will be easy to refactor to a data access layer. Also a method should be doing a single job and your Sub New was doing too much.
Using...End Using blocks ensure that your database objects are closed and disposed even if there is an error.
Always use parameters to avoid Sql injection. Datatypes are guesses. Check your database for the actual types.
Sub New()
Dim TableName = "SomeTableName"
Dim numb = GetMaxCarCode(TableName)
Dim cardcode As Integer = numb + 1
Dim p As New Floshcods(cardcode)
p.KeyWord = InputBox("Enter keyword")
p.Definition = InputBox("Enter definiton")
Controls("words0").Text = p.KeyWord
Controls("words1").Text = p.Definition
InsertFloshcods(TableName, p)
End Sub
Private Function GetMaxCarCode(Tablename As String) As Integer
Dim numb As Integer
Using cn As New MySqlConnection("Your connection string."),
cmd As New MySqlCommand($"Select MAX(CardCode) From {Tablename};", cn)
cn.Open()
numb = CInt(cmd.ExecuteScalar)
End Using
Return numb
End Function
Private Sub InsertFloshcods(TableName As String, f As Floshcods)
Using cn As New MySqlConnection("your connection String"),
cmd As New MySqlCommand($"Insert Into {TableName} (CardCode, Keyword, Definition) Values (#Code, #Keyword, #Def);", cn)
With cmd.Parameters
.Add("#Code", MySqlDbType.Int32).Value = f.CardCodes
.Add("#Keyword", MySqlDbType.VarChar).Value = f.KeyWord
.Add("#Def", MySqlDbType.VarChar).Value = f.Definition
End With
cn.Open()
cmd.ExecuteNonQuery()
End Using
End Sub

Path to Access Database in Visual Studio 2015

I'm working on a project in VB.NET, using Visual Studio 2015. My goal is that the user can create contacts, which the program puts in an Access database and lists in a DataGridView. I am using the following connection string:
Provider=Microsoft.ACE.OLEDB.12.0; Data Source = C:\Users\Jacob\Documents\Visual Studio 2015\Projects\Accustomer\Accustomer\Global.accdb
This works fine when I publish it to my own PC, but the problem is that when I try to run it on another PC, it does not work because of the data source path not existing on that PC(pretty logic). I have tried to use "Data Source = Global.accdb". This works for displaying the data, but not for implementing the data into the database. Is there any way that the program recognizes the database without implementing the full path to the database?
As said in comments by krish,you should "build" your own path...so everytime you run you programm on another machine YOUR CODE creates the path using the right parameters.
You dont give a fix path, your code creates the right one for every machine it runs on. So the filename can be hardcoded, but the path to it should be "flexible".
In krish example [projectPath] and [db] are variables, that contain the right values depending on the PC.
$"Provider=Microsoft.ACE.OLEDB.12.0; Data Source = {projectPath}\{db}\Global.accdb"
What's presented below has already been mentioned and is correct, here I'm simply showing a full working example.
I created a class named Operations with a public (could be private) variable to create a connection string where the connection points to Global.accdb in the same folder as the application's executable file. If the database were a folder below you would add that path in the IO.Path.Combine right before the hard coded string. Note I used AppDomain.CurrentDomain.BaseDirectory as Application.StartupPath is not always available in other types of projects.
Create the connection string.
Private Builder As New OleDbConnectionStringBuilder With
{
.Provider = "Microsoft.ACE.OLEDB.12.0",
.DataSource = IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Global.accdb")
}
Now here is the full code which I wrote for replying to questions for constructing not only the connection but for read/add/update and delete encapsulated into a single class for simplicity.
Public Class Operations
''' <summary>
''' Points to Global.accdb in the same folder as the application's
''' executable file
''' </summary>
Private Builder As New OleDbConnectionStringBuilder With
{
.Provider = "Microsoft.ACE.OLEDB.12.0",
.DataSource = IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Global.accdb")
}
Public Sub New()
If Not IO.File.Exists(Builder.DataSource) Then
Throw New IO.FileNotFoundException("Failed to find application's database")
End If
End Sub
Public Sub GetCustomerData()
Dim DataTable As New DataTable
Using cn As New OleDbConnection With {.ConnectionString = Builder.ConnectionString}
Using cmd As New OleDbCommand With {.Connection = cn}
cmd.CommandText =
<SQL>
SELECT Identifier, ContactTitle, Country, CompanyName
FROM Customers
ORDER BY CompanyName ASC
</SQL>.Value
cn.Open()
DataTable.Load(cmd.ExecuteReader)
End Using
End Using
End Sub
Public Function AddNewRow(ByVal Name As String, ByVal Contact As String, ByRef Identfier As Integer) As Boolean
Dim Success As Boolean = True
Dim Affected As Integer = 0
Try
Using cn As New OleDbConnection With {.ConnectionString = Builder.ConnectionString}
Using cmd As New OleDbCommand With {.Connection = cn}
cmd.CommandText =
<SQL>
INSERT INTO Customer
(
CompanyName,
ContactName
)
Values
(
#CompanyName,
#ContactName
)
</SQL>.Value
cmd.Parameters.AddWithValue("#CompanyName", Name)
cmd.Parameters.AddWithValue("#ContactName", Contact)
cn.Open()
Affected = cmd.ExecuteNonQuery()
If Affected = 1 Then
cmd.CommandText = "Select ##Identity"
Identfier = CInt(cmd.ExecuteScalar)
End If
End Using
End Using
Catch ex As Exception
Success = False
End Try
Return Success
End Function
Public Function UpdateCustomer(ByVal CustomerId As Integer, ByVal Name As String, ByVal Contact As String) As Boolean
Dim Success As Boolean = True
Dim Affected As Integer = 0
Try
Using cn As New OleDbConnection With {.ConnectionString = Builder.ConnectionString}
Using cmd As New OleDbCommand With {.Connection = cn}
cmd.CommandText =
<SQL>
UPDATE Customer
SET CompanyName = #CompanyName, ContactName = #ContactName
WHERE Identifier = #Identifier
</SQL>.Value
cmd.Parameters.AddWithValue("#CompanyName", Name)
cmd.Parameters.AddWithValue("#ContactName", Contact)
cmd.Parameters.AddWithValue("#Identifier", Contact)
cn.Open()
Affected = cmd.ExecuteNonQuery()
If Affected = 1 Then
Success = True
End If
End Using
End Using
Catch ex As Exception
Success = False
End Try
Return Success
End Function
Public Function DeleteCustomer(ByVal CustomerId As Integer) As Boolean
Dim Success As Boolean = True
Dim Affected As Integer = 0
Try
Using cn As New OleDbConnection With {.ConnectionString = Builder.ConnectionString}
Using cmd As New OleDbCommand With {.Connection = cn}
cmd.CommandText = "DELETE FROM Customers WHERE Identifier = #Identifier"
cmd.Parameters.AddWithValue("#Identifier", CustomerId)
cn.Open()
Affected = cmd.ExecuteNonQuery()
If Affected = 1 Then
Success = True
End If
End Using
End Using
Catch ex As Exception
Success = False
End Try
Return Success
End Function
End Class

vb.net call the same function twice from button Event Click

This is my code on button event click function
Dim con As New Koneksi
DataGridView1.Rows.Add(con.getIdTambahBarang(cbBarang.Text), _
con.getNamaTambahBarang(cbBarang.Text), _
con.getHargaTambahBarang(cbBarang.Text), _
txtJumlah.Text)
This is my class Koneksi code :
Public Function getIdNamaHargaTambahBarang(ByVal namaBarang As String, ByVal params As String) As String
Dim id As String = ""
Dim nama As String = ""
Dim harga As String = ""
Try
bukaKoneksi()
cmd.Connection = con
cmd.CommandType = CommandType.Text
cmd.CommandText = "SELECT * FROM barang WHERE nama_barang like '" & namaBarang & "'"
reader = cmd.ExecuteReader
If (reader.Read()) Then
If params = "getNama" Then
nama = reader.GetString(1)
Return nama
End If
If params = "getHarga" Then
harga = reader.GetDouble(2).ToString
Return harga
End If
If params = "getId" Then
id = reader.GetString(0)
Return id
End If
End If
tutupKoneksi()
Catch ex As Exception
End Try
End Function
Public Function getIdTambahBarang(ByVal namaBarang As String) As String
Return getIdNamaHargaTambahBarang(namaBarang, "getId")
End Function
Public Function getNamaTambahBarang(ByVal namaBarang As String) As String
Return getIdNamaHargaTambahBarang(namaBarang, "getNama")
End Function
Public Function getHargaTambahBarang(ByVal namaBarang As String) As String
Return getIdNamaHargaTambahBarang(namaBarang, "getHarga")
End Function
Both of the code above, produces
'System.InvalidOperationException' occurred in System.Data.dll error.
When I debug it, the second call of con produces this error. It seems that in VB.NET, the instance class function only can called once at a time, any solution?
Consider refactoring your code. You're actually hitting the database 3 times, each with a LIKE clause, where you really only need to do this once.
Suggest something like this, which performs the same business logic that you've got, with only one call to your database. It's also got some SQL injection prevention.
Dim con As New Koneksi
Dim barang As Barang = Koneksi.GetBarang()
DataGridView1.Rows.Add(barang.id,
barang.nama, _
barang.harga, _
txtJumlah.Text)
Public Class Koneksi
Public Function GetBarang(nama_barang As String)
Dim barang As New Barang
bukaKoneksi()
cmd.Connection = con
cmd.CommandType = CommandType.Text
cmd.CommandText = "SELECT id,name,harga FROM barang WHERE nama_barang = #nama"
cmd.Parameters.AddWithValue("#nama", namaBarang)
reader = cmd.ExecuteReader
If (reader.Read()) Then
barang.id = reader.reader.GetString(0)
barang.nama = reader.GetString(1)
barang.harga = reader.GetDouble(2).ToString
End If
tutupKoneksi()
Return barang
End Function
End Class
You'll retrieve your barang object properties in one statement. All three properties are gathered from the DB at once. When you inevitably want to add another property to your DataGridView, you won't have to make any extra round-trips to your database, but rather only modify your SQL statement, and the .Rows.Add) call.
The database is now safe from SQL injection in this call.
the code is easier to read and understand for the next developer coming into read this code.
the database was previously using more resources due to being called 3x, and using a LIKE clause, where you really only needed an = name.