Connecting and Updating to Database through VB.NET "Select command property has not been ... " - database

I am using this guide to update a value in a database through VB.NET and really struggling to get it to work.
I am trying to add 1 to a particular cell in the database. The grid that I am using updates with the value, but the database itself doesn't change. Using different tutorials (and loads of other StackOverflow questions) I have changed the code from the original but nothing seems to work.
Each time I click the button that should trigger the addition, I get this error message.
"The select command property has not been initialized before calling fill"
If anyone could tell me what is going wrong or point me in the direction that I should go in, it would be much appreciated. Thanks.
Private Sub DataGridView1_CellContentClick(sender As Object, e As DataGridViewCellEventArgs) Handles DataGridView1.CellContentClick
Dim senderGrid = DirectCast(sender, DataGridView)
Dim OleDbDataAdapter As New OleDb.OleDbDataAdapter
If TypeOf senderGrid.Columns(e.ColumnIndex) Is DataGridViewButtonColumn AndAlso
e.RowIndex >= 0 Then
' If addition
If e.ColumnIndex = 3 Then
Try
'from microsoft tut
ProjectDatabaseDataSet.Tables(0).Rows(e.RowIndex).Item("ProductStock") = ProjectDatabaseDataSet.Tables(0).Rows(e.RowIndex).Item("ProductStock") + 1
OleDbDataAdapter.Fill(ProjectDatabaseDataSet.Product)
OleDbDataAdapter.Update(ProjectDatabaseDataSet.Product)
Catch ex As Exception
MsgBox(ex.Message)
End Try
End If

Related

Why is my vb code not updating the ms access database (only cached temporarly)

I am doing a small system using Ms. Access (the database has more than 10 tables ) connecting to visual studio. I made a public class for opening the connection to the database so I can use it in every form. Everything is working and I can get the data from the database But any inserting or deleting data in forms, the database in ms access not getting the update. I can see the new records in forms but nothing in the database.
Imports System.Data.OleDb
Public Class dbconnectClass1
'create db connection
Private DBcon As New OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0; Data Source=dental_clinic.accdb;")
'prepare db command
Private dbcmd As OleDbCommand
' db data
Public DBDA As OleDbDataAdapter
Public DBDT As DataTable
'query parameters
Public params As New List(Of OleDbParameter)
'query statics
Public recordcount As Integer
Public Exception As String
Public Sub ExecQuery(query As String)
'reset query status
recordcount = 0
Exception = ""
Try
'open connection
DBcon.Open()
'create db command
dbcmd = New OleDbCommand(query, DBcon)
'load params into dbcommand
params.ForEach(Sub(p) dbcmd.Parameters.Add(p))
'clear params list
params.Clear()
'excute command and fill dataset
DBDT = New DataTable
DBDA = New OleDbDataAdapter(dbcmd)
recordcount = DBDA.Fill(DBDT)
Catch ex As Exception
Exception = ex.Message
End Try
'close the database connection
If DBcon.State = ConnectionState.Open Then DBcon.Close()
End Sub
'include query and command parameters
Public Sub addparam(name As String, value As Object)
Dim newparam As New OleDbParameter(name, value)
params.Add(newparam)
End Sub
End Class
This my code inside the forms:
Public Class NewExpense
Private access As New dbconnectClass1
' a varuble having the appointment Id to connect between 2 forms
Private appointmentNo As Integer
Private Function NoError(Optional report As Boolean = False) As Boolean
If Not String.IsNullOrEmpty(access.Exception) Then
If report = True Then MsgBox(access.Exception)
Return False
Else
Return True
End If
End Function
Private Sub Savebuttum_Click(sender As Object, e As EventArgs) Handles
Savebuttum.Click
Dim oDate As DateTime = Convert.ToDateTime(DateTimePicker1.Value)
access.addparam("#expensenme", expensenmtXT.Text)
access.addparam("#expensedetail", ExpenseDetailTXT.Text)
access.addparam("#expenseamount", ExpenseAmountTXT.Text)
access.addparam("#expensedate", oDate)
access.addparam("#expensepaidTo", paidtoTXT.Text)
access.ExecQuery("INSERT INTO Expense (Expenses_name, expense_details,
expenses_amount, ExpenseDate_Paid, ExpensePaid_To) Values (#expensenme,
#expensedetail, #expenseamount, #expensedate, #expensepaidTo);")
'report on errors
If Not String.IsNullOrEmpty(access.Exception) Then
MsgBox(access.Exception) : Exit Sub
'success
access.DBDA.Update(access.DBDT)
MsgBox("Expense Has been Added Successfully")
End Sub
End Class
Hum, you have this:
params.ForEach(Sub(p) dbcmd.Parameters.Add(p))
Great, we add the parmaters - looks good to go!!!
then, next line CLEARS all the work above!!! (the parameters are removed!!!!)
'clear params list
params.Clear()
Next up? Many will build a connection object, then a reader, and then a adaptor. But you ONLY need a data adaptor if you going to update a data table. if you just going to execute a command, then you don't need the data table, and you don't need a adaptor FOR that table. Adaptor = ability to modify a existing datatable (or dataset).
You are MUCH better to use the command object.
Why?
Because the command object has a connection object (don't need a separate one)
Because the command object has a data reader for you (no need for a whole data adaptor to JUST fill a table. And remember, you don't need a whole data adaptor UNLESS you are going to send/update a data table back to the database.
And because the command object has the command text, then you don't even need a variable for that!!!
And because all objects are in "one object", then really all you need is something to handy get you the connection.
So, for your insert example, we really don't gain by having that object, do we?
Ok, so here is your insert code without using those extra objects:
so in the following, I declare ONE variable, - the sql command object.
And do the insert
And as FYI? Your save button is not a save - but a insert button - every time you hit it, you will insert a new row. Lets deal with that issue in a bit.
So, here is our code:
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Using cmdSQL As New OleDbCommand("INSERT INTO Expense
(Expenses_name, expense_details,expenses_amount, ExpenseDate_Paid, ExpensePaid_To)
Values (#expensenme,#expensedetail, #expenseamount, #expensedate, #expensepaidTo)",
New OleDbConnection(My.Settings.TESTOLEDB))
With cmdSQL.Parameters
.Add("#expensenme", OleDbType.WChar).Value = expensenmtXT.Text
.Add("#expensedetail", OleDbType.WChar).Value = ExpenseDetailTXT.Text
.Add("#expenseamount", OleDbType.Currency).Value = ExpenseAmountTXT.Text
.Add("#expensedate", OleDbType.DBDate).Value = DateTimePicker1.Value
.Add("#expensepaidTo", OleDbType.WChar).Value = paidtoTXT.Text
End With
cmdSQL.Connection.Open()
cmdSQL.ExecuteNonQuery()
End Using
So things in above:
We have strong data typing and converstion.
Because of this, note how I did NOT have to create a separate date/time variable here.
Note the SAME for "money" or so called currency conversion - again strong data type by using parameters this way.
And is this a date only, or a date+ time value? If it is date, then
.Add("#expensedate", OleDbType.DBDate).Value = DateTimePicker1.Value
If it was/is a date + time, then this:
.Add("#expensedate", OleDbType.DBTimeStamp).Value = DateTimePicker1.Value
So, notice how all your object stuff REALLY did not help one bit, and in fact you did not really save code, and above actually had LESS variables defined to do the whole job.
Now, back to the insert issue/problem. (you save is doing a insert). But what about editing existing records?
So, I would suggest you work out the problem this way:
You create/get/have/assume a data row for the form.
The form takes the data row, fills the controls. You edit, and when you hit save, the data row is sent back to the datbase. So, once this works, then to add? Well, the add code will CREATE a new data row, save to database and THEN you send that new data row to the above eixsting form that can edit a data row, and can save a data row. So the form now is able to deal with both issues (adding vs editing existing). If the user dont' want the row, then you offer a delete button.
And REALLY nice is a data row means you don't deal with SQL, and don't deal with parmaters!!
So the code (desing pattern) I use is thus this:
dim da as oledbDataAdaptor
dim myTable as DataTable = MyRstEdit("SELECT * from tblHotels WHERE ID = " & lngID,da)
dim MyDataRow as DataRow = myTable.Rows(0)
' code to fill controls
txtHotelName.Text = MyDataRow("HotelName")
txtCity.Text = MyDataRow("City")
' etc. etc. etc.
Now to save? Well I put the values back into that DataRow like this:
MyDataRow("HotelName") = txtHotelName.Text
MyDataRow("City") = txtCity.Text
MyDateRow("BookingDate") = txtTimePick1.Value
da.Update(MyTable)
Notice how I don't have parameters, and even strong data type checking occurs for say the above Date/Time booking date column.
And the above is nice, since I don't have to deal with ANY parmaters to udpate a row of data.
The MyRstEdit routine looks like this and returns byREf a "da" (data adaptor).
Public Function MyrstEdit(strSQL As String, Optional strCon As String = "", Optional ByRef oReader As SqlDataAdapter = Nothing) As DataTable
' Myrstc.Rows(0)
' this also allows one to pass custom connection string - if not passed, then default
' same as MyRst, but allows one to "edit" the reocrdset, and add to reocrdset and then commit the update.
If strCon = "" Then
strCon = GetConstr()
End If
Dim mycon As New SqlConnection(strCon)
oReader = New SqlDataAdapter(strSQL, mycon)
Dim rstData As New DataTable
Dim cmdBuilder = New SqlCommandBuilder(oReader)
Try
oReader.Fill(rstData)
oReader.AcceptChangesDuringUpdate = True
Catch
End Try
Return rstData
End Function
So, now in vb.net, I actually find it is LESS code then even writing + using recordsets in MS-Access VBA code.
However, BEFORE you go down ANY of the above road?
Have you considered using the vb.net data binding features. Data-binding in vb.net means that you do NOT write ANY of the above code. it means that vb.net will do all of the dirty work, and write and setup ALL OF the code for you to edit data on a form. The end result is you don't write any code to update a table.
You do have to use + create a "data set". Once done, then you just drag controls onto the form, and you even get this. So you just drop in a dataset, table adaptor, Binding navagator, and you get this:
Note now the tool bar at the top (and you can place it on teh bottom if you wish). So you get this:
So that WHOLE form was created without having to write ONE line of code. And you can see we have navigation, edits and saves and even the ability to add. So, you can build up a editing form - and it thus becomes similar to say working in MS-Access and ZERO lines of code is required to build the above form.
However, if you ARE going to roll your own code? Then use a data row. That way you can shuffle data to/from the table, and NOT have to use parmaters and SQL update and insert statements - but ONLY have nice clean code in which you shove, or get values from that data row. .net will "write" all the update stuff for you.

Program sends data but database does not receive it

I have a conundrum.
In my VB.net program, in multiple places I communicate with my SQLserver database. I use Insert, Update and Select statements. My program is made up of multiple forms and my database has 4 tables.
All but one of my subs is working, and despite using break-points and walk through's of the code I can not figure out why.
The form and database tables in question are fine with the select and Insert statements are fully functioning, however the code of the update statement is not. When I run a walk through it behaves as if it has sent the data to the database, however the database never receives it. The connection data is identical to the functioning subs, and the code is in the same format as my update code in other forms within my program. So I can't see why its not working.
Here is the code:
Public selectedDeviceNumber As String = ""
Public selectedDeviceRowNumber As Integer = 0
'================================
'= Set up the DATASETS! =
'================================
Dim PCBconnectionstring As String = "Data Source=ServerName;Initial Catalog=Databasename;User Id=UserId;Password=password;Connect Timeout=30;User INstance=False"
Dim PCBsqlconnection As New SqlClient.SqlConnection(PCBconnectionstring)
Dim damyPCB As New SqlDataAdapter
Dim dsmyPCB As New DataSet
Dim ALcon As New SqlConnection
Dim ALcmd As New SqlCommand
Dim PCBcmd As New SqlCommand
Dim PCBcon As New SqlConnection
'================================
'= SAVE the data! =
'================================
Dim test As String = Me.selectedDeviceNumber
Private Sub SaveToDataBaseFunctionPCB()
'update the data entered to the database
Try
PCBsqlconnection.ConnectionString = "Data Source=ITWIN10-PC\SQL2010;Initial Catalog=SLE1000;User Id=UserId;Password=password;Connect Timeout=30;User Instance=False"
PCBsqlconnection.Open()
PCBcmd.Connection = PCBsqlconnection
PCBcmd.CommandText = "UPDATE PCBlist SET pcbSerial=#pcbSerial,pcbPart=#pcbPart,pcbVent=#pcbVent,pcbDesc=#pcbDesc," & _
"pcbTested=#pcbTested,pcbU1=#pcbU1,pcbU5=#pcbU5,pcbU7=#pcbU7,pcbU10=#pcbU10,pcbU11=#pcbU11,pcbVersion=#pcbVersion," & _
"pcbTestIni=#pcbTestIni,pcbApplyIni=#pcbApplyIni,pcbTestDate=#pcbTestDate,pcbApplyDate=#pcbApplyDate WHERE pcbID=#pcbID "
PCBcmd.Parameters.AddWithValue("#pcbSerial", txtSerialPCB1.Text)
PCBcmd.Parameters.AddWithValue("#pcbPart", cboPartPCB1.Text)
PCBcmd.Parameters.AddWithValue("#pcbVent", txtVentPCB1.Text)
PCBcmd.Parameters.AddWithValue("#pcbDesc", txtDescriptPCB1.Text)
PCBcmd.Parameters.AddWithValue("#pcbTested", chkTested1.Checked)
PCBcmd.Parameters.AddWithValue("#pcbU1", txtU11.Text)
PCBcmd.Parameters.AddWithValue("#pcbU5", txtU51.Text)
PCBcmd.Parameters.AddWithValue("#pcbU7", txtU71.Text)
PCBcmd.Parameters.AddWithValue("#pcbU10", txtU101.Text)
PCBcmd.Parameters.AddWithValue("#pcbU11", txtU111.Text)
PCBcmd.Parameters.AddWithValue("#pcbVersion", txtVersionPCB1.Text)
PCBcmd.Parameters.AddWithValue("#pcbTestIni", txtTestPCB1.Text)
PCBcmd.Parameters.AddWithValue("#pcbApplyIni", txtApplyPCB1.Text)
PCBcmd.Parameters.AddWithValue("#pcbTestDate", txtTestDatePCB1.Text)
PCBcmd.Parameters.AddWithValue("#pcbApplyDate", txtApplyDatePCB1.Text)
PCBcmd.Parameters.AddWithValue("#pcbID", Me.selectedDeviceNumber)
PCBcmd.ExecuteNonQuery()
PCBcmd.Parameters.Clear()
PCBsqlconnection.Close()
MsgBox("Data updated")
Catch ex As Exception
MessageBox.Show(ex.Message)
Finally
PCBsqlconnection.Close()
End Try
End Sub
`Private Sub btnAmend_Click(sender As System.Object, e As System.EventArgs) Handles btnAmend.Click
'Save the data to SQL Server
SaveToDataBaseFunctionPCB()
'record activity in Activity Log
FrmSLE1000.txtActivityLogRecorder.Text = ("Data Saved")
'Save the Activity log data to SQL Server
SaveToDataBaseFunction1()
'SLE1000SqlConnection = New SqlConnection(frmConnections.lblDBConnection.Text & frmConnections.lblDBConnection2.Text)
End Sub
If you have any ideas then please let me know. It could be that I've been looking at the code too long and its obvious, however I can't see the problem.
Many thanks
After extensive fiddling with my code I have fixed the problem. As it turns out the problem lay further back in the code. When the Select statement was called it loaded the data into a list view, which you then select a row which you want to edit. The catch ex as exception was being fired at this point, however the data was still being passed to the text boxes so that you could edit the data, so I mentally 'parked' that glitch for later. In solving that glitch the "amend" button and Update function works fine.
If you're interested the original glitch in the code was as follows
Private Sub lvPCB1_DoubleClick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lvPCB1.DoubleClick
Dim newmachine As String = FrmSLE1000.txtNo.Text
Try
If lvPCB1.SelectedItems.Count = 0 Then
MessageBox.Show("Ensure that you have selected a PCB Board. Try again.")
Exit Sub
Else
'Set selected units ID number in varable to allow access to all row data to populate tables
selectedDeviceNumber = lvPCB1.SelectedItems(0).Text
PopulateTablesFunctionPCB()
txtSerialPCB1.Text = lvPCB1.SelectedItems(1).Text
'record activity in Activity Log
End If
Catch ex As Exception
MessageBox.Show("Problem with selection")
End Try
End Sub
and the new working code is as follows
Private Sub lvPCB1_DoubleClick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lvPCB1.DoubleClick
Dim newmachine As String = FrmSLE1000.txtNo.Text
Try
If lvPCB1.SelectedItems.Count = 0 Then
MessageBox.Show("Ensure that you have selected a PCB Board. Try again.")
Exit Sub
Else
'Set selected units ID number in varable to allow access to all row data to populate tables
selectedDeviceNumber = lvPCB1.SelectedItems(0).Text
PopulateTablesFunctionPCB()
txtSerialPCB1.Text = lvPCB1.SelectedItems(0).Text
'record activity in Activity Log
End If
Catch ex As Exception
MessageBox.Show("Problem with selection")
End Try
End Sub
So it all came down to having a "1" instead of a "0" in the line "txtSerialPCB1.Text = lvPCB1.SelectedItems(0).Text"
And there you have it!

Calling selection from Combo box on form to be used in SQL query

I have been able to figure out how to populate my combo box with information pulled from a SQL query. What I need to do now is take the item selected from that combo box and run another query with that info using a button. This is what I have so far.
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim myconn As New SqlClient.SqlConnection("server=myserver;UID=User;PWD=Password;database=myDB")
Dim myTable As New DataTable()
Dim myCmd As New SqlCommand()
Dim myAdapter As New SqlDataAdapter(myCmd)
myCmd.Connection = myconn
myCmd.Connection.Open()
myCmd.CommandText = "UPDATE myDB.<*Selected list from combo box*> SET example = x WHERE example = y"
myCmd.ExecuteNonQuery()
myCmd.Connection.Close()
MsgBox("Done!")
End Sub
As you can see the issue is the portion I have named <Selected list from combo box> I am not asking for answers on how to do this, I am asking that you all point me in the right direction if possible.
If I understand your question, I believe all you need to do is build a string for your update statement to concatenate the table name in. Something like this would be functional:
myCmd.CommandText = "UPDATE myDB." & listbox1.SelectedItem.Value & " SET example = x WHERE example = y"
One important thing you want to keep in mind though is that a bad value in your list box could cause you to a security vulnerability called SQL Injection. So within your button procedure you might want to do some validation checks to be absolutely sure that the value in listbox1.SelectedItem.Value is strictly a valid table name that you allow to be updated before just passing it along as a SQL command.

VB.NET - .accdb database not saving changes

I can't figure out why my code won't save to my .accdb database.
I am fetching data from a .accdb database file and displaying it in a DataGridView, and then allowing changes to be made to it there. (This is a stock control system.) After making changes, the user is meant to be able to send the data back so it is saved in the .accdb file.
I have looked online everywhere and tried multiple different ways of doing this. This is the way I am currently using to solve the problem, but when running the code it does not save to the .accdb file. (However, it throws up no errors.)
Public Class Database
Dim datatable As DataTable
Dim adapter As OleDb.OleDbDataAdapter
Dim dbCon As New OleDb.OleDbConnection
Dim dbProvider As String = "PROVIDER=Microsoft.ACE.OLEDB.12.0;"
Dim dbRsrc As String = "Data Source =" & System.IO.Directory.GetCurrentDirectory & "/Resources/List.accdb"
Dim binding As BindingSource
Dim cmdBuilder As OleDb.OleDbCommandBuilder
Private Sub Database_Load(sender As Object, e As EventArgs) Handles MyBase.Load
dbCon.ConnectionString = dbProvider & dbRsrc
dbCon.Open()
adapter = New OleDb.OleDbDataAdapter("Select * FROM List", dbCon)
datatable = New DataTable
adapter.FillSchema(datatable, SchemaType.Source)
adapter.Fill(datatable)
binding = New BindingSource
binding.DataSource = datatable
dbCon.Close()
StockTable.DataSource = binding
End Sub
Private Sub SaveBtn_Click(sender As Object, e As EventArgs) Handles SaveBtn.Click
'insert validation here
Try
dbCon.ConnectionString = dbProvider & dbRsrc
dbCon.Open()
cmdBuilder = New OleDb.OleDbCommandBuilder(adapter)
adapter.AcceptChangesDuringUpdate = True
adapter.Update(datatable)
dbCon.Close()
Catch ex As Exception
MsgBox(ex.Message.ToString() & " Save Unsuccessful.")
End Try
End Sub
End Class
Not sure where I'm going wrong - when I hit the 'save' button, it should connect to the database, build a SQL query to update it and then update my datatable + .accdb database, right?
To test it, I've tried editing multiple columns and saving it, but when opening the file it still says the same values as it had before.
Any suggestions/pointers? I'm pretty newbie to VB.NET, learnt it about 3 months ago and only just starting to get fully into it.
Many thanks to the user "jmcilhinney" who helped me to reach this answer. I feel highly stupid at not realising that my code was working.
I used
Debug.WriteLine("Update value: " & adapter.Update(datatable))
Debug.WriteLine("Connection str: " & dbProvider & dbRsrc)
to find that my update command worked, and that in fact the output of my database file was in the /bin/ folder. I didn't realise that it used the /bin/ folder, and was looking in the root folder with the .VB files, etc.

VB.NET issue loading textboxes with data from SQL Server

I have some textboxes (using VS2010) I'm trying to populate with values from columns in a SQL Server database based on what item someone selects from a combobox. At first I was able to display the values for the first item in the combobox, but now nothing at all displays when I debug. Code:
Private Sub loadfields(sender As System.Object, e As System.EventArgs) Handles client_selection_combobox.SelectedIndexChanged
Using myconnection As New SqlConnection("connection string")
Dim loadfields As String = "SELECT company FROM ClientFileDatabase WHERE ClientFileDatabase.File_Name=#company;"
Dim loadfields_sqlcommand As New SqlCommand(loadfields, myconnection)
loadfields_sqlcommand.Parameters.Add("#company", SqlDbType.NVarChar)
loadfields_sqlcommand.Parameters("#company").Value = client_selection_combobox.SelectedIndex.ToString
Dim loadfields_dataadapter As New SqlDataAdapter
loadfields_dataadapter.SelectCommand = loadfields_sqlcommand
Dim loadfields_dataset As DataSet = New DataSet()
loadfields_dataadapter.Fill(loadfields_dataset, "ClientFileDatabase")
Dim loadfields_dataview = New DataView(loadfields_dataset.Tables("ClientFileDatabase"))
companyname_textbox.DataBindings.Clear()
companyname_textbox.DataBindings.Add("Text", loadfields_dataview, "Company")
taxid_textbox.DataBindings.Clear()
taxid_textbox.DataBindings.Add("Text", loadfields_dataview, "TaxIDNumber")
accountmanager_textbox.DataBindings.Clear()
accountmanager_textbox.DataBindings.Add("Text", loadfields_dataview, "AccountManager")
etc...
End Using
End Sub
I've also tried using the SelectedValueChanged and SelectionChangeCommitted event handlers to no avail. Also tried using a refresh after setting the databindings, didn't help. Any advice welcome, thanks!
I personally like to use datatables, I find them easier to work with. I'm sure you will have more code to check to make sure dt.rows.count > 0 before actually attempting to work with the data, but here is how I would do it.
Dim loadfields_dataadapter As New SqlDataAdapter
Dim dt As New DataTable
loadfields_dataadapter.Fill(dt)
companyname_textbox.text = dt.Rows(0).Item("Company")
taxid_textbox.text = dt.Rows(0).Item("TaxIDNumber")
accountmanager_textbox.text = dt.Rows(0).Item("AccountManager")
Also, keep in mind that NULL fields in the database can cause runtime errors, so you may have to check for those as well.

Resources