VB is not a member of 'String' - vb.net

Private Sub TextBox1_TextChanged(sender As Object, e As EventArgs) Handles TextSearch.TextChanged
'attempting to allow search by last name
Dim con As New OleDb.OleDbConnection
Dim cmd As New OleDb.OleDbCommand
Dim Sql As String
Dim data_reader As String
con.ConnectionString = "PROVIDER = Microsoft.ACE.OLEDB.12.0;Data Source=C:\Me._DeCON_12___2015DataSet.DeCon"
con.Open()
Sql = "SELECT [First Name 2015], [Last Name 2015] FROM DeCon WHERE [Last Name 2015] = TextBoxLName.Text"
data_reader = Sql.ExecuteReader()
'ListBox1.Items.Clear()
If data_reader.HasRows = True Then
Do While data_reader.Read()
lbfirstname.item = data_reader.Item("firstname")
Loop
End If
data_reader.Close()
data_reader = Nothing
con.Close()
End Sub
Okay I'm a total newbie, and I'm trying to make it so a user can search by last name in a textbox, and then have first names (of people with that last name) show up in a listbox. I was given this code (except for data_reader As String) and I know I can make it work, but I don't know how.
Here are the errors I'm getting:
'ExecuteReader' is not a member of 'String'.
'Has Rows' is not a member of 'String'.
'Read' is not a member of 'String'.
'lbfirstname' is not declared. It may be inaccessible due to its protection level.
'Item is not a member of 'String'.
'Close' is not a member of 'String'.
Now...lbfirstname, I might be able to figure out. I assume I need to change the name to whatever makes sense. Is it referring to the textbox or the listbox?
The rest of it...I have no idea. I thought Close was a given, and assumed Read was. When I try to Dim * As String it's of no help, and I knew that wouldn't be right anyway. I'm connecting to an Access database, if that makes any difference.
I don't know enough VB to do so. I was voluntold to make it, and somehow managed most the other bits with little help. This is the last part I should have to do, and I need it done by tomorrow.

The errors you are getting are because you're attempting to call properties and methods that are not part of the String class, as has been mentioned in the comments. Specifically:
ExecuteReader is a method of OleDbCommand.
HasRows is a property of OleDbDataReader (which is returned by calling OleDbCommand.ExecuteReader(). Read() and Close() are methods on the OleDbDataReader.
lbfirstname is not the ID assigned to the ListBox control in your application - you need to use the ID you gave the control.
Item should probably be Items (note the plural), referring to the collection of items in the ListBox.
You're vulnerable to SQL injection (in addition to the fact that your SQL string won't work anyway, because you use TextBoxLName.Text as a literal when you want the value of the TextBox instead. To avoid SQL injection, you should be using parmaterized queries.
lbfirstname.item = data_reader.Item("firstname") - Even if this line of code worked, you'd only wind up with the last firstname in the collection returned from your database. You need to add the first names to the ListBox via the Add method.
Putting this altogether, you would have something like this:
Using con As New OleDb.OleDbConnection("PROVIDER = Microsoft.ACE.OLEDB.12.0;Data Source=C:\Me._DeCON_12___2015DataSet.DeCon")
con.Open()
Dim Sql As String = "SELECT [First Name 2015], [Last Name 2015] FROM DeCon WHERE [Last Name 2015] = #LastName"
Dim cmd As New OleDb.OleDbCommand(Sql, con)
cmd.Parameters.Add("#LastName", OleDb.OleDbType.VarChar).Value = TextBoxLName.Text
Using data_reader As OleDb.OleDbDataReader = cmd.ExecuteReader()
If data_reader.HasRows Then
Do While data_reader.Read()
lbfirstname.Items.Add(data_reader.GetString(0))
Loop
End If
End Using
End Using
Explanation of the above code:
First, the connection is wrapped in a Using block - this will ensure the connection is properly closed and disposed of when the block is exited, even if an exception is thrown.
Next, the Sql string is parameterized by replacing the value in the WHERE clause with #LastName. The OleDbCommand is created and assigned the Sql string plus the OleDbConnection object.
The #LastName parameter is then added to the command's Parameters collection and assigned the value of the TextBoxLName.
ExecuteReader is then called on the OleDbCommand object, in a Using block again (to ensure the reader is properly closed and disposed of).
While the reader is looped through, the first name of each record returned from the query is added to the ListBox. I used the GetString method of the reader to return the value as a string. This method requires an ordinal (the index of the column), which based on your provided SQL string is the first column (0 since it's 0-indexed).
All of the above code assumes the column names in the query are correct and the control names are correct - you may need to adjust them to match what you have in your application.

Related

How to edit a record in an access database - visual basic

I want to edit a specific record in an access database but I keep on getting errors
this is the database I want to edit:
Access database
these are flashcards that the user has created and stored in an access database. What I want is that the user is able to edit the difficulty so it appears more/less often
This is the module:
Module Module1
Public Function runSQL(ByVal query As String) As DataTable
Dim connection As New OleDb.OleDbConnection("provider=microsoft.ACE.OLEDB.12.0;Data Source=flashcard login.accdb") 'Establishes connection to database
Dim dt As New DataTable 'Stores database in table called dt
Dim dataadapter As OleDb.OleDbDataAdapter
connection.Open() 'Opens connection
dataadapter = New OleDb.OleDbDataAdapter(query, connection)
dt.Clear() 'Clears datatable
dataadapter.Fill(dt) 'Fills datatable
connection.Close()
Return dt
End Function
End Module
And here is the button that the user can press to edit the database:
Private Sub btnSubmit_Click(sender As Object, e As EventArgs) Handles btnSubmit.Click
Dim sql As String
sql = "UPDATE flashcards set difficulty = '" & TxtDifficulty.Text
runSQL(sql)
End Sub
The difficulty column in the database should be able to be edited by the user through the value they entered in txtDifficulty.text
Good to hear I found the problem with the apostrophe.
I am going to need a where statement but the problem I have is that the user can create as much flashcards as they want so how would I write the where statement?
An INSERT statement does not have a WHERE clause but an UPDATE does and is usually by a primary key.
Look at how I add a new record ignoring mHasException and specifically using parameters. In this case a List is used but with little effort an array of DataRow can be passed instead.
Here is an example for updating a record with a DataRow.
To get other code samples for ms-access see the following repository.
In closing, in the above repository I don't get into all possibilities yet there should be enough there to get you going. And when reviewing code I flip between Add for adding a parameter and AddWithValue while Add is the recommend way but both are shown to see differences. see also Add vs AddWithValue.

CommandText Property Has Not Been Initialized, retrieving data

Private Sub ButtonSubmitID_Click(sender As Object, e As EventArgs) Handles ButtonSubmitID.Click
Dim comm As New SqlCommand
Dim conn As New SqlConnection
conn.ConnectionString = "Data Source = localhost\SQLEXPRESS; Initial Catalog = test2Forms; Integrated Security = SSPI;"
comm.Connection = conn
Dim ID = TextBoxID.Text
comm.Parameters.AddWithValue("#ID", ID)
Dim adapter As SqlDataAdapter = New SqlDataAdapter(comm.CommandText, comm.Connection)
comm.CommandText = "SELECT * FROM withActivityLog3 WHERE ID = #ID"
Dim records As DataSet = New DataSet
adapter.Fill(records)
DataGridView2.DataSource = records
End Sub
CommandText property has not been initialized is the error I am receiving. I am able to pull all the data from the database into the GridView on the Form Load but when I try to narrow it down to one ID using a WHERE clause on the button trigger, it comes up with the above error. I've used the debugger to trace through one step at a time and the command and connection strings look correct. I've also successfully duplicated the query on my database using the SQL Server command line. I'm searching on a primary key (ID) so the expected results would be one uniquely identified row from the database.
As for the problem you know you have:
' initialize DataAdapter with (EMPTY) commandtext
Dim adapter As SqlDataAdapter = New SqlDataAdapter(comm.CommandText, comm.Connection)
' initialize Command Text
comm.CommandText = "SELECT * FROM withActivityLog3 WHERE ID = #ID"
When you pass the CommandText to the DataAdapter, it is empty because you havent set it yet which results in the error.
There is a fair amount of inefficiency in your code though. Rewritten:
' form level conn string
Private TheConnString As String = "Data Source = localhost\..."
Private Sub ButtonSubmitID_Click(sender ...
Dim dt As New DataTable
Using dbcon As New MySqlConnection(TheConnString)
Using cmd As New MySqlCommand("select * from Sample where Id = #id", dbcon)
cmd.Parameters.Add("#id", MySqlDbType.Int32).Value = Convert.ToInt32(TextBox2.Text)
dbcon.Open()
dt.Load(cmd.ExecuteReader)
dgvA.DataSource = dt
End Using
End Using
End Sub
Note: this uses MySQL but the concepts are the same for Sqlite, Access, SQL Server etc
There is no need to type or paste the connection string and over everywhere it is used. One form level variable will allow DRY (Dont Repeat Yourself) code.
Anything which implements the Dispose() method should be disposed of. That includes nearly all the DB Provider objects. The Using statement allows you to declare and initialize an object and at the End Using it is disposed of. Failing to Dispose of things can cause leaks and even run out of connections or resources to create things like DB Command objects.
There is no need to create a local DbDataAdapter. These are very powerful and useful critters meant to do much more than fill a DataTable. If that is all you are doing, you can use ExecuteReader method on the DbCommand object.
Nor do you need a local DataSet. Contrary to the name, these do not hold data, but DataTables. Since there is only one and it is local (goes out of scope when the method ends), you dont need a DataSet to store it.
The Add method should be used rather than AddWithValue. The code above specifies the datatype for the parameter so there is no guesswork required of the compiler. Of course with that comes the need to convert the text to a number...
...Since this is user input, you should not trust the user, so Integer.Tryparse would be more appropriate: I like pie will not convert to an integer. Data Validation is something you should do before you commence the DB ops.
Dim ID = TextBoxID.Text as used is pointless code. You do not need to move the textbox text into a new variable in order to use it. However, ID might be used to store the integer value

Need to Extend the Scope of a DataTable

I'm converting old VB 7 code which used ODBC to connect to an SQL Anywhere DB to VB 2013 and an Access 2010 DB.
I declare several DataSets at the top of the module but when I get into procedures and functions, I have lost the scope of the DataTable.
I have the following declared:
Public con As New System.Data.OleDb.OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=|DataDirectory|\CLI_CRVM.accdb")
Public extractDA As New OleDbDataAdapter("SELECT * FROM [extract]", con)
Public extractCB = New OleDbCommandBuilder(extractDA)
Public extractDT As DataTable
Public extractDR As DataRow
Then, in a clicked event of a button, I call a procedure which loads the DataTable:
extractCB.quoteprefix = "["
extractCB.quotesuffix = "]"
extractDT = New DataTable
extractDA.Fill(extractDT)
When it returns to the clicked event code, it does a For Each loop:
For Each extractDR As System.Data.DataRow In extractDT.Rows
At this point, I can see values from the DataTable like this:
ls_plan_code = Trim(extractDR("plan_code"))
MsgBox("Plan Code: " & ls_plan_code)
But when I call a procedure or function where I need the values from the DataTable, they are no longer available. Ie. when this executes:
Sub accumulation(ByVal adec_premium As Decimal, ByVal ai_stage As Integer)
Dim ldec_mode As Decimal
ldec_mode = CDec(extractDR("pay_mode"))
End Sub
I get this error: "Object reference not set to an instance of an object."
I know a workaround is to pass the DataRow to the sub routine; however, there are several DataTables and many procedures and functions, some of which call other procedures and functions which rely on data from other DataTables. Additionally, some sub routines write values to an answer DataTable which then gets written to the Access DB.
So, while I know this may not be "proper" form, I've got to get this code up and running to test values and if there were a way - as it did in the VB 7 code - to get the scope of the DataTable to extend throughout the entire module, I think my problems will be solved.
Thanks in advance!
In the following line:
For Each extractDR As System.Data.DataRow In extractDT.Rows
By specifying extractDR As System.Data.DataRow, you are creating a new variable. This variable is being assigned the value instead of the variable with the same name in the higher scope.
Simply remove the As System.Data.DataRow:
For Each extractDR In extractDT.Rows

Fill Datatable using SQL 'select' WITHIN A TRANSACTION

I would like to fill a datatable with results from a SQL select statment but using a transaction. The reason that I am using a transaction is because I have a list of names (as a datatable), and I want to iterate through the list of names and select the database rows where the name = the name on the list. There are 500,000 names in the database and I only want to retreive the relevant rows. I have the code for the procedure as I think it should look like (untested) BUT I dont know HOW to place the data into a datatable .... so Im missing something where I declare the datatable and the 'fill' of that table , could someone help with this ? Or suggest how else I can get the information out of the batabase without looking up each name individually.
Using connection As New SQLite.SQLiteConnection(R2WconectionString)
connection.Open()
Dim sqliteTran As SQLite.SQLiteTransaction = connection.BeginTransaction()
Try
oMainQueryR = "SELECT NameID, Address, Ocupation FROM Employees Where Name= :Name"
Dim cmdSQLite As SQLite.SQLiteCommand = connection.CreateCommand()
With cmdSQLite
.CommandType = CommandType.Text
.CommandText = oMainQueryR
.Parameters.Add(":Name", SqlDbType.VarChar)
End With
'Prevent duplicate selects by using a dictionary
Dim NameInalready As New Dictionary(Of String, String)
For Each row As DataRow In TheLIST.Rows
If NameInalready.ContainsKey(row.Item("Name")) Then
Else
NameInalready.Add(row.Item("Name"), "")
cmdSQLite.Parameters(":Name").Value = row.Item("Name")
cmdSQLite.ExecuteNonQuery()
End If
Next
sqliteTran.Commit()
Catch ex As Exception
End Try
End Using
First, you don't need a transaction because you aren't updating the database.
Second, depending on the possible number of Names in TheLIST, it might be worthwhile for you to change the name selector to IN (i.e. SELECT * FROM Employees WHERE Name IN ('name1', 'name2'). However, if you expect more than about 10, this is probably not worth trouble.
Finally, you need to create a new DataTable to hold the results. Then you need to create a DataAdapter passing cmdSqlLite as the constructor parameter. And finally, replace your ExecuteNonQuery with DataAdapter.Fill(DataTable).
For example (after Dim cmdSQLite):
Dim oDataTable As New DataTable("Employees")
Dim oAdapter As New SqliteDataAdapter(cmdSQLite)
and replacing the ExecuteNonQuery line with:
oAdapter.Fill(oDataTable)
I will qualify this code by saying it may need some tweaks. I only work with class objects and collections, so my preference would have actually been to load a collection of Employee class instances.
I would have done that by replacing ExecuteNonQuery with ExecuteReader and then the loading the read data into a new class instance. This type of approach resolves various issues with serializing the data across service boundaries (i.e. Xml for web services) and also lets you embed business logic, if needed, into the classes.

Retrieving ##IDENTITY in AccessDB

I'm trying to retrieve the ##IDENTITY value from an access database after I add a row to it. However, instead of using hard coded connections, I'm using the DataSet wizard. Unfortunately, due to the Jet engine, I can't do more than one command at a time. I tried to select the ##IDENTITY in a separate command but unfortunately, I guess it counts as a different command as it returns a 0 each time.
My question is, is there a way I can use the GUI/IDE to retrieve the ##IDENTITY or do I have hard code the connection, command, query values and obtain the value that way.
Thanks.
You asked an interesting questions which I did not know the answer to. After some research I found this and it seems promising. However I have never used it and cannot substantiate if and how well it works.
I also don't know the DataSet wizard that well, but vaguely recalls that it generates an OleDbAdapter object, hopefully it exposes a RowUpdated event which you can hook this code to.
I have pasted the interesting MSDN code parts here:
(Link to full documentation)
' Create the INSERT command for the new category.
adapter.InsertCommand = New OleDbCommand( _
"INSERT INTO Categories (CategoryName) Values(?)", connection)
adapter.InsertCommand.CommandType = CommandType.Text
Then hook-up and event listener to RowUpdated
' Include an event to fill in the Autonumber value.
AddHandler adapter.RowUpdated, _
New OleDbRowUpdatedEventHandler(AddressOf OnRowUpdated)
Obtain the ##Identity with the same connection.
Private Shared Sub OnRowUpdated( _
ByVal sender As Object, ByVal e As OleDbRowUpdatedEventArgs)
' Conditionally execute this code block on inserts only.
If e.StatementType = StatementType.Insert Then
' Retrieve the Autonumber and store it in the CategoryID column.
Dim cmdNewID As New OleDbCommand("SELECT ##IDENTITY", _
connection)
e.Row("CategoryID") = CInt(cmdNewID.ExecuteScalar)
e.Status = UpdateStatus.SkipCurrentRow
End If
End Sub
Hopefully this helps you out or least someone will set me straight :)