.NET OleDb parameterized query not working as expected - vb.net

I'm trying to pass this Update command to a database, it completes ok with no errors but it doesnt update the database and I can't understand why?
Dim Cmd As OleDbCommand
Dim SQL As String
Dim objCmd As New OleDbCommand
Dim Con = New OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & My.Settings.MasterPath)
Dim Item = "08/08/2015"
SQL = ("UPDATE [Hol Dates] SET [" & EmployeeList.Text & "]= #Htype WHERE [HDate]=#Hdate")
Cmd = New OleDbCommand(SQL, Con)
objCmd = New OleDbCommand(SQL, Con)
objCmd.Parameters.AddWithValue("#Hdate", item)
objCmd.Parameters.AddWithValue("#Htype", "None")
Con.Open()
Dim ans = MsgBox("Do you want to unbook this holiday?", MsgBoxStyle.YesNo)
If ans = MsgBoxResult.Yes Then
objCmd.ExecuteNonQuery()
Else
Exit Sub
End If
Con.Close()
Con.Dispose()

You need to reverse the order in which you add the parameters to the OleDbCommand object. OleDb allows us to assign names to parameters but it ignores the names and only pays attention to the order in which the parameters appear in the CommandText.
Therefore, since your SQL statement refers to Htype and then Hdate you need to add the parameters in that same order.

Related

How to update access db by editing datagridview cells?

Im trying to edit items after inserting to access by clicking the save button? How can i save the edits done in datagridview rows to access?
Already tried the update query
For each loop
vb.net
Private Sub BunifuFlatButton1_Click(sender As Object, e As EventArgs) Handles BunifuFlatButton1.Click
Dim constring As String = ("Provider=Microsoft.ACE.OLEDB.12.0;Data Source= C:\Users\PU-IMO\Desktop\BlueWavesIS - Copy\BlueWavesIS\BlueWavesIS.accdb")
Dim conn As New OleDbConnection(constring)
For Each row As DataGridViewRow In DataGridView1.Rows
Using con As New OleDbConnection(constring)
'nettxt.Text = (grosstxt.Text * perdistxt.Text / 100) - (dislctxt.Text + disusd.Text + distax.Text)
Dim cmd As New OleDbCommand("Update PurchaseInvoice set [Itemnum] = #ItemNum, [Itemname]= #ItemName, [Itemqty]= #ItemQty, [Itemprice] = #ItemPrice, [discount] =#discount, [subtotal] = #subtotal,[Preference] = " & preftxt.Text & ", [Suppnum] = " & pnumtxt.Text & ", [UniqueID] = " & pautotxt.Text & " Where [UniqueID] = " & pautotxt.Text & "", con)
cmd.Parameters.AddWithValue("#ItemID", row.Cells("ItemID").Value)
cmd.Parameters.AddWithValue("#ItemName", row.Cells("ItemName").Value)
cmd.Parameters.AddWithValue("#ItemQty", row.Cells("ItemQty").Value)
cmd.Parameters.AddWithValue("#ItemPrice", row.Cells("ItemPrice").Value)
cmd.Parameters.AddWithValue("#discount", row.Cells("discount").Value)
cmd.Parameters.AddWithValue("#subtotal", row.Cells("subtotal").Value)
cmd.Parameters.AddWithValue("#Ref", preftxt.Text.ToString)
cmd.Parameters.AddWithValue("#Suppnum", Convert.ToInt32(pnumtxt.Text))
cmd.Parameters.AddWithValue("#UniqueID", Convert.ToInt32(pautotxt.Text))
DataGridView1.AllowUserToAddRows = False
con.Open()
cmd.ExecuteNonQuery()
con.Close()
End Using
Next
'This the code i used to show the data in datagridview:
Private Sub NewPurchaseInvoice_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim con As New OleDbConnection
con.ConnectionString = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source= C:\Users\PU-IMO\Desktop\BlueWavesIS - Copy\BlueWavesIS\BlueWavesIS.accdb"
con.Open()
Dim sql As String = "Select [Itemnum],[Itemname],[Itemprice],[ItemQty],[discount],[subtotal] from PurchaseInvoice where [UniqueID] = " & pautotxt.Text & ""
Dim cmd10 As OleDbCommand = New OleDbCommand(Sql, con)
'Dim adap As New OleDbDataAdapter("Select [Itemnum],[Itemname],[Itemprice],[discount],[subtotal] from PurchaseInvoice where UniqueID = " & pautotxt.Text & "", con)
'Dim ds As New System.Data.DataSet
'adap.Fill(ds, "PurchaseInvoice")
Dim dr As OleDbDataReader = cmd10.ExecuteReader
Do While dr.Read()
DataGridView1.Rows.Add(dr("ItemNum"), dr("ItemName"), dr("ItemQty"), dr("ItemPrice"), dr("discount"), dr("subtotal"))
Loop
con.Close()
I expect that all the rows will be updated as each other, but the actual output is that each row has different qty name etc...
This code does not seem appropriate in the load event because pautotxt.Text will not have a value yet. Can you move it to a Button.Click?
I guessed that the datatype of ID is an Integer. You must first test if the the .Text property can be converted to an Integer. .TryParse does this. It returns a Boolean and fills IntID that was provided as the second parameter.
You can pass the connection string directly to the constructor of the connection. The Using...End Using blocks ensure that your database objects are closed and disposed even if there is an error. You can pass the Select statement and the connection directly to the constructor of the command.
ALWAYS use Parameters, never concatenate strings to avoid sql injection. Don't use .AddWithValue. See http://www.dbdelta.com/addwithvalue-is-evil/
and
https://blogs.msmvps.com/jcoehoorn/blog/2014/05/12/can-we-stop-using-addwithvalue-already/
and another one:
https://dba.stackexchange.com/questions/195937/addwithvalue-performance-and-plan-cache-implications
The DataAdapter will open the connection, fiil the DataTable and close the connection if it finds it closed; however if it is found open it will leave it open.
The last line binds the grid to the DataTable.
Private Sub FillGrid()
If Not Integer.TryParse(pautotxt.Text, IntID) Then
MessageBox.Show("Please enter a number")
Return
End If
dt = New DataTable()
Using con As New OleDbConnection(ConnString)
Using cmd As New OleDbCommand(Sql, con)
cmd.Parameters.Add("#ID", OleDbType.Integer).Value = IntID
Dim adap = New OleDbDataAdapter(cmd)
adap.Fill(dt)
End Using
End Using
DataGridView1.DataSource = dt
End Sub
DataTables are very clever. When bound to a DataGridView they keep track of changes and mark rows as update, insert, or delete. The DataAdapter uses this info to update the database. Once the database is updated we call .AcceptChanges on the DataTable to clear the marking on the rows.
Private Sub UpdateDatabase()
Using cn As New OleDbConnection(ConnString)
Using da As New OleDbDataAdapter(Sql, cn)
da.SelectCommand.Parameters.Add("#ID", OleDbType.Integer).Value = IntID
Using builder As New OleDbCommandBuilder(da)
da.Update(dt)
End Using
End Using
End Using
dt.AcceptChanges()
End Sub

Adding SQL Parameter fails

This is a new concept for me and I can't quite see what's causing the error
I'm attempting to populate a datagridview control from a single field in an Access database (from a combo and Text box source).
Using literals works, but not with the parameter.
Dim conn As New OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & Backend & ";Persist Security Info=False;")
Dim command As New OleDbCommand("Select Year from tblTest ", conn)
Dim criteria As New List(Of String)
If Not cboYear.Text = String.Empty Then
criteria.Add("Year = #Year")
command.Parameters.AddWithValue("#Year", cboYear.Text)
End If
If criteria.Count > 0 Then
command.CommandText &= " WHERE " & String.Join(" AND ", criteria)
End If
Dim UserQuery As New OleDbDataAdapter(command.CommandText, conn)
Dim UserRet As New DataTable
UserQuery.Fill(UserRet)
With frmDGV.DataGridView2
.DataSource = UserRet
End With
frmDGV.DataGridView2.Visible = True
frmDGV.Show()
Trying to Fill the datatable shows exception 'No value given for one or more required parameters.'
The value of command.CommandText at that point is "Select Year from tblTest WHERE Year = #Year"
Your instance of OleDbDataAdapter was created using two parameters query text and connection.
Dim UserQuery As New OleDbDataAdapter(command.CommandText, conn)
In this case DataAdapter doesn't know about parameters at all.
Instead use constructor with paremeter of type OleDbCommand.
Dim UserQuery As New OleDbDataAdapter(command)
In your code instance of OleDbCommand already associated with connection

Update Access database records by column, row known

This is what I've got so far :
Dim myCONN As OleDbConnection = New OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=w:\Baza.mdb")
Dim cmd1 = New OleDbCommand("SELECT ID FROM Baza WHERE NAZIV=#XXNAZIV")
cmd1.Parameters.AddWithValue("#XXNAZIV", TextBox2.Text)
cmd1.Connection = myCONN
myCONN.Open()
Dim result = cmd1.ExecuteReader()
While (result.Read())
Dim rowx As Integer = GetTextOrEmpty(result("ID"))
End While
I've found the row (rowx) in which I would like to change values in 20 corresponding columns (namesID : NAZIV, SIFRA,...). Data is already presented in textboxes (textbox1...), but I don't know how to finish this code with UPDATE and how to insert changed values back to Access.
Dim cmdText As String = "UPDATE Baza SET NAZIV=#XXNAZIV Where ID=SomeId"
Using con = new OleDbConnection("PROVIDER=Microsoft.ACE.OLEDB.12.0;Data Source = h:\Baza.mdb")
Using cmd = new OleDbCommand(cmdText, con)
con.Open()
cmd.Parameters.AddWithValue("#XXNAZIV",TextBox2.Text)
cmd.ExecuteNonQuery()
End Using
End Using
This should help you to solve your problem, of course you will have to pass ID parameter to query also.
Reference

Update MS Access via vb.net

the below code runs without error, but the MS Access table isn't updating. What am I missing?
Try
cnn = New OleDbConnection(ConfigurationManager.ConnectionStrings("accConnectionString").ToString())
cnn.Open()
Catch ex As Exception
Debug.Print("Oops - no connection to database")
Exit Sub
End Try
Dim sql As String
sql = "SELECT * FROM tblSend WHERE UploadedSuccessfullyOn is null ORDER BY QueuedOn;"
Dim da As New OleDbDataAdapter(sql, cnn)
Dim ds As New DataSet
da.Fill(ds, "dsQueuedToSend")
For Each dr As DataRow In ds.Tables("dsQueuedToSend").Rows
' Perform other unrelated tasks in this space, removed for brevity
' If those other tasks were successful, update Access.
If success = True Then
sql = "UPDATE [tblSend] SET [UploadedSuccessfullyOn] = #uploadedOn WHERE (([UploadID]) = #uploadId);"
Dim cmd As OleDbCommand
cmd = New OleDbCommand(sql, cnn)
cmd.Parameters.Add("#uploadedOn", OleDbType.Date).Value = Now
cmd.Parameters.Add("#uploadedId", OleDbType.Integer).Value = dr("UploadID")
cmd.ExecuteNonQuery()
cmd.Dispose()
Console.WriteLine("Success: updated.")
Else
Console.WriteLine("Failed: Not updated.")
End If
Next
da.Dispose()
cnn.Close()
I've also tried with:
sql = "UPDATE [tblSend] SET [UploadedSuccessfullyOn] = ? WHERE (([UploadID]) = ?);"
Or simply add the values in the not-preferred/non-parameter approach:
sql = "UPDATE [tblSend] SET [UploadedSuccessfullyOn] = #" & Now & "# WHERE (([UploadID]) = " & dr("UploadID") & ");"
Dim cmd As OleDbCommand
cmd = New OleDbCommand(sql, cnn)
cmd.ExecuteNonQuery()
cmd.Dispose()
None of these approaches updates the MS Access table. Ideas? Thanks!
One thing I have been caught by before with Visual Studio is that when embedding an Access database in a WinForms project, when you run the project, the Access database is copied over from the original to the runtime directory, overwriting any changes you've made.
This can create the illusion that an update is not taking place, when in fact if you check at the right moment, it is.

Possible to have multiple SqlCommand?

Dim conn As New SqlConnection("Database=Clinic_Management_System;Data Source=.\SQLExpress;Integrated Security=True;AttachDBFilename=|DataDirectory|Clinic Management System.mdf")
Dim cmd As SqlCommand
Dim dr As SqlDataReader
conn.Open()
cmd = New SqlCommand("INSERT INTO record([PatientID],[Prescription],[VisitDate]) Values ('" & PatientIDTextBox.Text & "','" & txtPrescription.Text & "',GetDate()) ", conn)
cmd.ExecuteNonQuery()
For cn As Integer = 0 To DataGridView1.RowCount - 1
cmd = New SqlCommand("INSERT INTO record_item([RecordID],[ItemID],[Amount]) Values ( (SELECT MAX(RecordID) FROM record)," & DataGridView1.Rows(cn).Cells(0).Value & "," & DataGridView1.Rows(cn).Cells(2).Value & ")", conn)
cmd.ExecuteNonQuery()
Next
conn.Close()
Is this possible to run 2 SqlCommand together??
Because after executed somehow the 2nd inside the loop did not execute or insert data.
You don't have 2 SqlCommands.
You have 1 SqlCommand, called cmd, which is executed multiple times.
It is fine to do something like this, however I would have 1 SqlCommand for your INSERT INTO record, and 1 for your INSERT INTO record_item. I think this makes it much easier to understand when looking back at the code at a later date.
Either way, using a SqlCommand like this should not prevent it from executing, therefore I believe there is another issue with your scripting.
I've adapted your code so that it is split into 2 seperate SqlCommand objects, and the queries have been parameterized to prevent SQL Injection:
Dim conn As New SqlConnection("Database=Clinic_Management_System;Data Source=.\SQLExpress;Integrated Security=True;AttachDBFilename=|DataDirectory|Clinic Management System.mdf")
Dim cmdRecord As New SqlCommand("INSERT INTO record ([PatientID],[Prescription],[VisitDate]) Values (#PatientID, #Prescription, GETDATE())", conn)
cmdRecord.Parameters.Add("#PatientID", SqlDbType.Int).Value = PatientIDTextBox.Text
cmdRecord.Parameters.Add("#Prescription", SqlDbType.NVarChar).Value = txtPrescription.Text
Dim cmdRecordItem As New SqlCommand("INSERT INTO record_item([RecordID],[ItemID],[Amount]) Values ( (SELECT MAX(RecordID) FROM record),#ItemID,#AmountID)", conn)
cmdRecordItem.Parameters.Add("#ItemID", SqlDbType.Int)
cmdRecordItem.Parameters.Add("#Amount", SqlDbType.Decimal)
Dim dr As SqlDataReader
conn.Open()
cmdRecord.ExecuteNonQuery()
For cn As Integer = 0 To DataGridView1.RowCount - 1
cmdRecordItem.Parameters("#ItemID").Value = DataGridView1.Rows(cn).Cells(0).Value
cmdRecordItem.Parameters("#Amount").Value = DataGridView1.Rows(cn).Cells(2).Value
cmdRecordItem.ExecuteNonQuery()
Next
conn.Close()
One command can only be used once.
If you want to use more than one command, declare a new cmd as
Dim cmd2 as SqlCommand
In order to ensure that either all statements complete successfully or that none do, you need to wrap your commands in a transaction.
Using parameters for the commands will also make the code more understandable and safer and you should use using statements where possible.
Also, performing the Select max recordid is a very bad idea (if you have multiple users), but I'll leave that for another time:
Using conn As New SqlConnection("Database=Clinic_Management_System;Data Source=.\SQLExpress;Integrated Security=True;AttachDBFilename=|DataDirectory|Clinic Management System.mdf")
Dim cmdRecord As SqlCommand
Dim cmdRecordItem As SqlCommand
Dim oTran As SqlTransaction = Nothing
conn.Open()
Try
cmdRecord = New SqlCommand("INSERT INTO record([PatientID],[Prescription],[VisitDate]) Values (#PatientID, #Prescription, GetDate())", conn)
cmdRecord.Parameters.AddWithValue("#PatientID", PatientIDTextBox.Text)
cmdRecord.Parameters.AddWithValue("#Prescription", txtPrescription.Text)
cmdRecordItem = New SqlCommand("INSERT INTO record_item([RecordID],[ItemID],[Amount]) SELECT ISNULL(MAX(RecordID), 0), #ItemID, #Amount FROM record", conn)
cmdRecordItem.Parameters.Add("#ItemId")
cmdRecordItem.Parameters.Add("#Amount")
oTran = conn.BeginTransaction
cmdRecord.ExecuteNonQuery()
For Each oRow As DataGridViewRow In DataGridView1
cmdRecordItem.Parameters("#ItemId").Value = oRow.Cells(0).Value
cmdRecordItem.Parameters("#Amount").Value = oRow.Cells(2).Value
cmdRecordItem.ExecuteNonQuery()
Next
oTran.Commit()
Catch
If oTran IsNot Nothing Then
oTran.Rollback()
End If
Throw
Finally
conn.Close()
End Try
End Using