Check before dataser make changes - vb.net

I use datagridview and populate it using dataset as follows:
Private GetGeschaftDataSet As New DataSet
Public Function GetDataSet() As DataSet Implements IDAL.GetDataSet
Dim strcon = New AppSettingsReader().GetValue("ConnectionString", GetType(System.String)).ToString()
Using con As New SqlConnection(strcon)
Using cmd As New SqlCommand("SELECT * FROM T_Marke", con)
con.Open()
' Create a data adapter in the method and throw it away afterwards
Using GetProjectsDataAdapter = New SqlDataAdapter(cmd)
GetProjectsDataAdapter.Fill(GetGeschaftDataSet, "trial1")
End Using
End Using
End Using
Return GetGeschaftDataSet
End Function
when user ends work with that i save changes to db like this:
Public Sub MakeChangesDataSet() Implements IDAL.MakeChangesDataSet
If Not GetGeschaftDataSet.HasChanges Then
MessageBox.Show("No changes to save", "Informacja", MessageBoxButtons.OK, MessageBoxIcon.Warning)
Else
Dim i As Integer
Try
Using MyConnection = New SqlConnection(strcon)
Using cmd As New SqlCommand("SELECT * FROM T_Marke", MyConnection)
MyConnection.Open()
' Create a data adapter in the method and throw it away afterwards
Using GetProjectsDataAdapter = New SqlDataAdapter(cmd)
Dim cmdbuilder As New SqlCommandBuilder(GetProjectsDataAdapter)
i = GetProjectsDataAdapter.Update(GetGeschaftDataSet, "trial1")
End Using
End Using
End Using
MessageBox.Show("Updated" & i & " marke", "Informacja", MessageBoxButtons.OK, MessageBoxIcon.Information)
Catch ex As Exception
MsgBox(ex.Message)
End Try
End If
End Sub
However i would like to check whether to not duplicate data, in this case if my table contains columns: Id and Name - i don't want to update if user placed already existing Name. How to achieve that or should i do the check on datagrid level?

There some ways to do this.
Open a connection and select count all rows with that name and if is zero in the same connection execute the insert.
Using a stored procedure which will do all the above.
Create a unique index on Name column
I would use the third.

Related

"No value given for one or more required parameters" error using OleDbCommand

I am trying to update a record in MS Access using VB.net. This code will be under the "Delivered" button. When I try to run it, it shows the "No value given for one or more required parameters" error. Here is my code:
Private Const strConn As String = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=C:\Users\Traicy\Downloads\MWL(11-30-2021)\MadeWithLove\MadeWithLove\MadeWithLove.mdb;"
ReadOnly conn As OleDbConnection = New OleDbConnection(strConn)
Dim cmd As OleDbCommand
Public Sub DeliveredUpdate()
Const SQL As String = "UPDATE DELIVERY SET delivery_status = #status"
cmd = New OleDbCommand(SQL, conn)
' Update parameter
cmd.Parameters.AddWithValue("#status", "Delivered")
' Open connection, update, then close connection
Try
conn.Open()
If cmd.ExecuteNonQuery() > 0 Then
MsgBox("The delivery status was successfully updated.")
End If
conn.Close()
Catch ex As Exception
MsgBox(ex.Message)
conn.Close()
End Try
End Sub
Do not declare connections or commands outside of the method where they are used. These database objects use unmanaged resources. They release these resources in their Dispose methods. The language provides Using blocks to handle this.
As mentioned in comments by Andrew Morton, you should have a Where clause to tell the database which record to update. This would contain the primary key of the record. I guessed at a name for the field, OrderID. Check your database for the real field name.
Access does not use named parameters but you can use names for readability. Access will be able to recognize the parameters as long as they are added to the Parameters collection in the same order that they appear in the sql string. In some databases the Add method is superior to AddWithValue because it doesn't leave the datatype to chance.
It is a good idea to separate your database code from your user interface code. If you want to show a message box in your Catch put the Try blocks in the UI code. This way your function can be used in a web app or mobile app without rewriting.
Public Function DeliveredUpdate(ID As Integer) As Integer
Dim recordsUpdated As Integer
Dim SQL As String = "UPDATE DELIVERY SET delivery_status = #status Where OrderID = #Id;"
Using conn As New OleDbConnection(strConn),
cmd As New OleDbCommand(SQL, conn)
cmd.Parameters.Add("#status", OleDbType.VarChar).Value = "Delivered"
cmd.Parameters.Add("#Id", OleDbType.Integer).Value = ID
conn.Open()
recordsUpdated = cmd.ExecuteNonQuery
End Using 'closes and disposes the command and connection
Return recordsUpdated
End Function
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim retVal As Integer
Dim id As Integer = 1 'not sure where you are getting this value from
Try
retVal = DeliveredUpdate(id)
Catch ex As Exception
MsgBox(ex.Message)
End Try
If retVal > 0 Then
MsgBox("The delivery status was successfully updated.")
End If
End Sub

Insert text into MS access cells

i am begginer in visual basic and i want to insert text into cells in column in MS access but i doesn't find, how could i do that.
Here is code i tried:
Private Sub UpdateDataBase2()
provider = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source="
datafile = "F:\Test Database\Database.accdb"
conString = provider & datafile
myConnection.ConnectionString = conString
myConnection.Open()
Dim str As String
str = "Insert into TABLA([LABELS]) Values (?)"
Dim cmd As OleDbCommand = New OleDbCommand(str, myConnection)
cmd.Parameters.Add(New OleDbParameter("LABELS", CType(TextBox1.Text, String)))
Select Case panelCount
Case 1
' TextBox1.Text = cmd.Add("LABELS").Rows(0).Item(1).ToString()
Case 2
' str = "Insert into TABLA([LABELS]) Values (?)"
'cmd.Parameters.Add(New OleDbParameter("LABELS", CType(TextBox1.Text, String)))
End Select
Try
cmd.ExecuteNonQuery()
cmd.Dispose()
myConnection.Close()
Catch ex As Exception
MsgBox(ex.Message)
End Try
myConnection.Close()
End Sub
In this application, i made a dynamics panels and labels.(dynamics is for me generated in code)
panelcount is variable which saves to another MS acces database count of dynamics panels. I want to save text from labels to database systematically(it means: text from label 1 insert to cell 1.), but every code i tried was not function for me.
I know i have to use loop, but first i want to try if code works.
Sorry for my english.
Any solution?
Database objects like Connection and Command should be declared in the method where they are used so they can be disposed.
Use Using...End Using blocks to ensure that your database objects are closed and disposed even if there is an error.
Insert creates a new record. If you want to Update an existing record you will need the primary key value for the record you want to update.
Dim Str = Update TABLA Set [LABELS] = ? Where ID = ?
Then you will need a second parameter.
cmd.Parameters.Add("ID", OleDbType.Integer).Value = intID
You will need to declare and provide a value for intID.
Don't show a message box while a connection is open.
Private Sub OPCode()
Try
Dim Str = "Insert into TABLA([LABELS]) Values (?)"
Dim ConStr = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=F:\Test Database\Database.accdb"
Using myConnection As New OleDbConnection(ConStr),
cmd As OleDbCommand = New OleDbCommand(Str, myConnection)
cmd.Parameters.Add("LABELS", OleDbType.VarChar).Value = TextBox1.Text
myConnection.Open()
cmd.ExecuteNonQuery()
End Using 'Closes and disposes the connection and disposes the command
Catch ex As Exception
MsgBox(ex.Message)
End Try
End Sub

How to refresh GridView after Insert

I'm fairly new to Vb, and my task is to create programmaly those whole process, creating SqlDataAdapter, DataTable, DataSet. However I still can't manage the right things to do and in what order. I can retrieve, Update, Delete and sometimes my Insert works but I can't get the fresh data from my DataBase straight after the insert. Also, I want to mention I have a GUID as UniqueIndentifier in my SQL Table which need to be populated with the rest of the data in the GridView. Thank you
I have tried to put SELECTCOMMANDE and call the method FILL after the Insert, also to clear the the DataSet, put the gridControl.Datasource = Nothing, lastly I tried to Update my SqlDataAdapter. None of those methods worked for me....:(
gridView.AddNewRow()
gridView.UpdateCurrentRow()
'sqlDa.SelectCommand = New SqlCommand(str_select_command, connection)
'sqlDa.Fill(dataS, "Representants")
connection.Close()
'gridControl.RefreshDataSource()
'dataS.Clear()
'dataS.Tables.Remove("Representant")
'sqlDa.Update(dataS.Tables(0))
''sqlDa.Fill(dataS, "Representants")
'gridControl.DataSource = Nothing
'gridControl.DataSource = dataS.Tables(0)
'gridView.PopulateColumns()
'gridView.RefreshData()
Try
connection.Open()
Dim Command = New SqlCommand("INSERT INTO TEST (prenom, nom, id_cie_12) " & "VALUES (#prenom, #nom, #id_cie_12)", connection)
Command.Parameters.Add("#prenom", SqlDbType.NChar, 20, "prenom")
Command.Parameters.Add("#nom", SqlDbType.NChar, 20, "nom")
Command.Parameters.Add("#id_cie_12", SqlDbType.NChar, 10, "id_cie_12")
sqlDa.InsertCommand = Command
gridView.RefreshData()
bbiButton.Enabled = False
Catch ex As Exception
MsgBox(ex.ToString)
Finally
connection.Close()
End Try
I'm expecting the data updated from the DataBase shown in the GridView.
Public Class Form1
Sub filterrecords(ByVal sender As String)
cmd.Connection = cn
cmd.CommandText = "SELECT * FROM Table1"
Dim da = New SqlDataAdapter(cmd)
Dim dt As New DataTable
da.Fill(dt)
DataGridView1.DataSource = dt
Dim imgc As New DataGridViewImageColumn
imgc = DataGridView1.Columns(5)
imgc.ImageLayout = DataGridViewImageCellLayout.Stretch
End Sub
then Paste in Form Load
filterrecords("")
cmd.ExecuteNonQuery()
cn.Close()
and paste Here
filterrecords("")

VB.NET How to correctly loop through a result set

I have looked at many different code snippets on this site looking that would show me how to do something that should be fairly simple once I have the knowledge.
I want to query a database table for an array of values and then populate a combobox with those results.
Here is what I have so far:
Public Sub getMachines()
Try
Dim SQL As String = "SELECT MachineName from machine"
Form1.machineName.DisplayMember = "Text"
Dim tb As New DataTable
tb.Columns.Add("Text", GetType(String))
Using cn As New MySqlConnection(ConnectionString)
Using cmd As New MySqlCommand(SQL, cn)
For Each cmd As String In cmd
'I want to add each value found in the database to "tb.Rows.Add"
'tb.Rows.Add(???)
Next
Form1.machineName.DataSource = tb
cn.Open()
cmd.ExecuteNonQuery()
End Using
cn.Close()
End Using
Catch ex As MySqlException
MsgBox(ex.Message)
End Try
End Sub
I proceeded much like you did. I used the Load method of the DataTable. It is not necessary to set the column name and type. The name of the column is taken from the Select statement and the datatype is inferred by ADO.net from the first few records.
Luckily a DataTable can be an Enumerable using the .AsEnumnerable method. Then we can use Linq to get all the values from the MachineName column. Calling .ToArray causes the Linq to execute. If you hold your cursor over names on this line you will see that the datatype is String(). Just what we need to fill a combo box.
Code for a class called DataAccess
Private ConnectionString As String = "Your Connection String"
Public Function GetMachineNames() As String()
Dim tb As New DataTable
Dim SQL As String = "SELECT MachineName from machine;"
Using cn As New MySqlConnection(ConnectionString)
Using cmd As New MySqlCommand(SQL, cn)
cn.Open()
dt.Load(cmd.ExecuteReader)
End Using
End Using
Dim names = dt.AsEnumerable().Select(Function(x) x.Field(Of String)("MachineName")).ToArray()
Return names
End Function
In the form load you combo box like this.
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim DatAcc As New DataAccess()
Dim arr = DatAcc.GetMachineNames()
machineName.DataSource = arr
End Sub
If you just want the MachineName to be displayed in the ComboBox, then just use that as the DisplayMember; don't bother creating another column called Text.
Public Sub getMachines()
Try
Dim cmd As String = "SELECT MachineName from machine"
Dim ds As New DataSet()
Using con As New MySqlConnection(ConnectionString)
Using da As New MySqlDataAdapter(cmd, con)
da.Fill(ds)
With Form1.machineName
.DisplayMember = "MachineName"
.ValueMember = "MachineName"
.DataSource = ds
End With
End Using
End Using
Catch ex As MySqlException
MsgBox(ex.Message)
End Try
End Sub
I'll show a few examples, including using parameters, since that is important.
First up, a quick translation to run the existing query and loop through the results:
Public Sub getMachines()
Try
Dim SQL As String = "SELECT MachineName from machine"
Using cn As New MySqlConnection(ConnectionString), _
cmd As New MySqlCommand(SQL, cn)
cn.Open()
Using rdr As MySqlDatareader = cmd.ExecuteReader
While rdr.Read()
Form1.machineName.Items.Add(rdr("MachineName"))
End While
End Using
End Using
Catch ex As MySqlException
MsgBox(ex.Message)
End Try
End Sub
But better practice for a method like this is to isolate data access for the UI. This method should return results to the caller, which can decide what do with them. So I'll show two methods: one to get the data, and the other to loop through it and set up the combobox:
Private Function GetMachines() As DataTable
'No try/catch needed here. Handle it in the UI level, instead
Dim SQL As String = "SELECT MachineName from machine"
Dim result As New DataTable
Using cn As New MySqlConnection(ConnectionString), _
cmd As New MySqlCommand(SQL, cn),
da As New MySqlDataAdapter(cmd)
da.Fill(result)
End Using
Return result
End Function
Public Sub LoadMachines()
Try
For Each item As DataRow in getMachines().Rows
Form1.machineName.Items.Add(item("MachineName"))
Next
Catch ex As MySqlException
MsgBox(ex.Message)
End Try
End Sub
Or, we can use DataBinding:
Private Function GetMachines() As DataTable
Dim SQL As String = "SELECT MachineName from machine"
Dim result As New DataTable
Using cn As New MySqlConnection(ConnectionString), _
cmd As New MySqlCommand(SQL, cn),
da As New MySqlDataAdapter(cmd)
da.Fill(result)
End Using
Return result
End Function
Public Sub LoadMachines()
Try
Form1.machineName.DisplayMember = "FirstName";
Form1.machineName.ValueMember = "City"
Form1.machineName.DataSource = GetMachines()
Catch ex As MySqlException
MsgBox(ex.Message)
End Try
End Sub
If you ever want to use a filter, you might do this (notice the overloading):
Private Function GetMachines(ByVal machineFilter As String) As DataTable
Dim SQL As String = "SELECT MachineName from machine WHERE MachineName LIKE #Filter"
Dim result As New DataTable
Using cn As New MySqlConnection(ConnectionString), _
cmd As New MySqlCommand(SQL, cn),
da As New MySqlDataAdapter(cmd)
'Match the MySqlDbType to your actual database column type and length
cmd.Parameters.Add("#Filter", MySqlDbType.VarString, 30).Value = machineFilter
da.Fill(result)
End Using
Return result
End Function
Private Function GetMachines(ByVal machineFilter As String) As DataTable
Return GetMachines("%")
End Function
Query parameters like that are very important, and if you were doing string concatenation to accomplish this kind of thing on your old platform, you were doing very bad things there, too.
Finally, let's get fancy. A lot of the time, you really don't want to load an entire result set into RAM, as is done with a DataTable. That can be bad. Instead, you'd like be able to stream results into memory and only work with one at a time, minimizing RAM use. In these cases, you get to play with a DataReader... but returning a DataReader object from within a Using block (which is important) doesn't work that well. To get around this, we can use functional programming concepts and advanced language features:
Private Iterator Function GetMachines(ByVal machineFilter As String) As IEnumerable(Of String)
Dim SQL As String = "SELECT MachineName from machine WHERE MachineName LIKE #Filter"
Using cn As New MySqlConnection(ConnectionString), _
cmd As New MySqlCommand(SQL, cn)
'Match the MySqlDbType to your actual database column type and length
cmd.Parameters.Add("#Filter", MySqlDbType.VarString, 30).Value = machineFilter
cn.Open()
Using rdr As MySqlDatareader = cmd.ExecuteReader
While rdr.Read()
Dim result As String = rdr("MachineName")
Yield Return result
End While
End Using
End Using
Return result
End Function
Private Function GetMachines() As IEnumerable(Of String)
Return GetMachines("%")
End Function

How do I create a dataset in VB.NET from this code?

I currently have the following code in my project which populates a DataGridView object with the results of an SQL query.
Sub PerformQuery(ByVal SQLText As String)
Dim DbConnection As New OleDb.OleDbConnection(createConnectionString)
Dim SQLQuery As String = SQLText
Dim Adapter As New OleDb.OleDbDataAdapter(SQLQuery, DbConnection)
Try
Using Table As New DataTable
Adapter.Fill(Table)
Table.Locale = Globalization.CultureInfo.InvariantCulture
DbConnection.Close()
DataGridView1.DataSource = Table
End Using
Catch ex As Exception
MsgBox(ex.Message)
End Try
End Sub
Elsewhere in my project I can create a DataSet object using the code
Dim ds As New DataSet
And then extract data from it using code like:
MaxRows = ds.Tables("Dataset_Users").Rows.Count
Rather than populating a DataGridView, how can I use the PerformQuery code to create a dataset?
Thank you in advance for your help.
I think you are after the following:
Try
Dim ds As New DataSet
Using Table As New DataTable
Adapter.Fill(Table)
Table.Locale = Globalization.CultureInfo.InvariantCulture
DbConnection.Close()
DataGridView1.DataSource = Table
ds.Table.Add(Table)
End Using
Catch ex As Exception
MsgBox(ex.Message)
End Try
Or as in your example you was after the Number of rows in the dataset you can do the same with the DataTable, For Example:
Try
Dim MaxRows As Integer
Using Table As New DataTable
Adapter.Fill(Table)
Table.Locale = Globalization.CultureInfo.InvariantCulture
DbConnection.Close()
DataGridView1.DataSource = Table
'' Getting the number of rows in the DataTable
MaxRows = Table.Rows.Count
End Using
Catch ex As Exception
MsgBox(ex.Message)
End Try
Think in a more functional style. Return the table instead of setting to grid. While we're here, let's update the method so you don't have to write queries anymore that leave you wide open to sql injection attacks:
Function PerformQuery(ByVal SQLText As String, ByVal ParamArray Parameters() As OleDbParameter) As DataTable
Dim result As New DataTable()
Using cn As New OleDb.OleDbConnection(createConnectionString), _
cmd As New OleDb.OleDbCommand(SQLText, cn), _
Adapter As New OleDb.OleDbDataAdapter(cmd, cn)
If Parameters IsNot Nothing AndAlso Parameters.Length > 0 Then
cmd.Parameters.AddRange(Parameters)
End If
Adapter.Fill(result)
End Using
Return Result
End Function
Note that I also removed the error handling and locale code. You still need to do that stuff, but when you want to just return a datatable instead of interact directly with the user interface in a method you have effectively moved your code to a lower level of abstraction. When you do that, you probably don't want to deal with the error handling at this lower level any more; instead let the exceptions bubble up where you can handle them closer to the user interface.
Now you call the updated method like this:
Dim sql As String = "SELECT * FROM Customers WHERE CustomerID = ?"
Dim CustID As New OleDb.OleDbParameter("CustomerId", OleDbType.Integer)
CustID.Value = 123456
Try
DataGridView1.DataSource = PerformQuery(sql, CustID)
Catch Ex As Excpetion
MsgBox(Ex.Message)
End Try