So I am writing a VB.Net that is using LINQ to access a database. There is one part of the program that I have been having trouble figuring out, how to access a column in a table when both values are passed as variables.
Right now I have a setup using SqlConnection strings and SqlDataAdapters to get the job done, but this is literally the only segment of my code that uses this syntax and it bothers the hell out of me. I'd like to figure out how to change the code to a LINQ format so that I have the code base feeling more cohesive.
This is the code segment I have right now:
Public Function GetColumnFromTable(col As String, tbl As String) As AutoCompleteStringCollection
Dim source As New AutoCompleteStringCollection
Dim conn As New SqlConnection("my connection string")
Try
conn.Open()
Catch ex As Exception
MessageBox.Show(ex.Message)
Return source
End Try
Dim query As New SqlCommand("SELECT " + col + " FROM " + tbl + "", conn)
Dim dataSource As New DataTable
Dim adapter As New SqlDataAdapter(query)
adapter.Fill(dataSource)
For i = 0 To dataSource.Rows.Count - 1
source.Add(dataSource.Rows(i)(field).ToString())
Next
conn.Close()
Return source
End Function
The code is meant to grab the rows from a specific column in a table and set them to an AutoCompleteStringCollection that is used as the autofill for text boxes on the GUI. I would like to be able to change it to something like this.
Public FUnction GetColumnFromTable(col As String, tbl As String) As AutoCompleteStringCollection
Dim source As New AutoCompleteStringCollection
Dim query = From tbl Select col
query.ToList().ForEach(Function(x) source.Add(x))
End Function
I'd like something like the above code that actually works, but I don't know how to do it.
Related
I'm trying to insert data into a database with an autonumber in MS Access as primary key. I get an error saying "Number of query values and destination fields are not the same. The data types in MS Access are Autonumber (I didn't include it in the INSERT statement), String (#OrderNo), String (#Product), Number (#Qty), and Date (#TDate). Here's the image:
Here's my code:
For Each row As DataGridViewRow In DataGridView1.Rows
Dim connString As String = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=Daily Inventory.accdb;"
Using conn As New OleDbConnection(connString)
Using cmd As New OleDbCommand("Insert into Table1 Values(#OrderNo, #Product, #Qty, #TDate)", conn)
cmd.Parameters.AddWithValue("#OrderNo", TxtOrder.Text.ToString)
cmd.Parameters.AddWithValue("#Product", row.Cells("Product").Value)
cmd.Parameters.AddWithValue("#Qty", row.Cells("Qty").Value)
cmd.Parameters.AddWithValue("#TDate", Date.Now.ToString("MM/dd/yyyy"))
If conn.State = ConnectionState.Open Then
conn.Close()
End If
conn.Open()
cmd.ExecuteNonQuery()
conn.Close()
End Using
End Using
Next
You need to change your sql to "Insert into Table1 (OrderNo,Product,Qty,TDate) Values(#OrderNo, #Product, #Qty, #TDate)".
The following code works for me.
DataGridView1.AllowUserToAddRows = False
For Each row As DataGridViewRow In DataGridView1.Rows
Dim connString As String = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=...;"
Using conn As New OleDbConnection(connString)
Using cmd As New OleDbCommand("Insert into Table1 (OrderNo,Product,Qty,TDate) Values(#OrderNo, #Product, #Qty, #TDate)", conn)
cmd.Parameters.AddWithValue("#OrderNo", TxtOrder.Text.ToString)
cmd.Parameters.AddWithValue("#Product", row.Cells("Product").Value)
cmd.Parameters.AddWithValue("#Qty", row.Cells("Qty").Value)
cmd.Parameters.AddWithValue("#TDate", Date.Now.ToString("MM/dd/yyyy"))
If conn.State = ConnectionState.Open Then
conn.Close()
End If
conn.Open()
cmd.ExecuteNonQuery()
conn.Close()
End Using
End Using
Next
Well, I think we dealing with a bit of a chicken and a egg problem here?
I mean, how did the grid get created?
How and where did you setup the columns in the grid?
lets drop a data grid view into a form. Drop in a button.
We have this code to load up the grid:
Private Sub HotelGridEdit_Load(sender As Object, e As EventArgs) Handles Me.Load
LoadGrid()
End Sub
Sub LoadGrid()
Using conn As New OleDbConnection(My.Settings.AccessDB)
Dim strSQL As String =
"SELECT ID, FirstName, LastName, City, HotelName, Description, Active FROM tblHotelsA
ORDER BY HotelName"
Using cmdSQL As New OleDbCommand(strSQL, conn)
conn.Open()
Dim rstData As New DataTable
rstData.Load(cmdSQL.ExecuteReader)
DataGridView1.DataSource = rstData
End Using
End Using
End Sub
So, now we have this:
Now, in a above, I can cursor around - make any edits I want.
I can also type in on the bottom blank row to add new rows.
I can click on the left side recordselector, and hit delete key.
Now, to save my edits, save my addtitions, and save my deletes?
I have the one button at the top, and this code works to do all updates, all addtitions, and all deletes.
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
' save all edits, and new rows, and deletes
Using conn As New OleDbConnection(My.Settings.AccessDB)
Dim strSQL As String =
"SELECT ID, FirstName, LastName, City, HotelName, Description, Active FROM tblHotelsA"
Using cmdSQL As New OleDbCommand(strSQL, conn)
conn.Open()
Dim da As New OleDbDataAdapter(cmdSQL)
Dim daC As New OleDbCommandBuilder(da)
da.Update(DataGridView1.DataSource)
End Using
End Using
End Sub
So, not a lot of code.
In fact, often using a data table is a lot less work, since you don't have to mess with all those parameters anyway - even if your data grid was somehow not feed from the database. But, if you feeding the "grid" from the database, then really, you can just add rows - and then write some code to send the changes back to the database with the above code. And if you want to start out with a blank grid - not show previous rows, then just add to your sql like this:
SELECT * from tblHotels WHERE ID = 0 Order by Hotelname
Use above to create a data table without any rows but STILL bind that to the grid, and thus once again, to add, or delete rows, you just edit and add them, and again the SAME code I had above will work to save edits, deletes and additions.
Here's my .mdf database file that has 5 columns
I want to add each of those values from my Id column in a list
Private Sub Read_Click(sender As Object, e As EventArgs) Handles Read.Click
Try
If con.State = ConnectionState.Open Then
con.Close()
End If
con.Open()
cmd = con.CreateCommand()
cmd.CommandType = CommandType.Text
cmd.CommandText = "SELECT Id FROM tablekongbago"
cmd.ExecuteNonQuery()
Dim dr As SqlClient.SqlDataReader
dr = cmd.ExecuteReader(CommandBehavior.CloseConnection)
While dr.Read
element = dr.GetInt32(0).ToString()
End While
Catch ex As Exception
End Try
MessageBox.Show(element)
End Sub
The problem is that I can only retrieve the last row of my Id column and not all of the values from my Id column using
element = dr.GetInt32(0).ToString()
If I try to iterate and turn it into
dr.GetInt32(1).ToString()
it displays nothing.
I want to create a collection of Id's to a List(Of Integer) I know how to create a list and a for loop but I don't know how can I retrieve all of my Id's from my Id column, what kind of code should I use if "dr.GetInt32(0)" is only for the last row of the Id column?, is there a way I can loop starting from the very first top row up to the last row of my Id column? I want something like "list[0] - referring to the first row and list[2] - referring to the last row, so that I can add it my List(Of Integer).
I cringe whenever I see If con.State = ConnectionState.Open Then. Connections should be declared in the method where they are used. You should never have to question the ConnectionState.
You have executed your command twice. A Select in not a NonQuery. NoQuery is Insert, Update and Delete.
Your While loop keeps overwriting the element varaiable on each iteration so you only get the value in the last record.
Never write an empty Catch block. It will just swallow errors and you may get unexpected results with no clue why.
It is a good idea to separate you database code from you user interface code.
Create your connection and command with a Using...End Using block so you know they are properly disposed. Likewise with the reader. I like to do as little as possible with a reader because it requires and open connection and connections should be open for as short a time as possible.
Private ConStr As String = "Your connection string"
Private Sub Read_Click(sender As Object, e As EventArgs) Handles Read.Click
Dim dt As DataTable
Try
dt = GetIds()
Catch ex As Exception
MessageBox.Show(ex.Message)
Return
End Try
Dim ListOfIDs = (From row As DataRow In dt.AsEnumerable
Select CInt(row(0))).ToList
ListBox1.DataSource = ListOfIDs
End Sub
Private Function GetIds() As DataTable
Dim dt As New DataTable
Using con As New SqlConnection(ConStr),
cmd As New SqlCommand("SELECT Id FROM tablekongbago;", con)
con.Open()
Using reader = cmd.ExecuteReader
dt.Load(reader)
End Using
End Using
Return dt
End Function
You can simply create a List of Integer and add the ids to your collection during each call to dr.Read()
Dim ids = New List(Of Integer)()
While dr.Read()
ids.Add(dr.GetInt32(0))
End While
You code looks a bit messed up. This should work:
Note that a sql command object is VERY nice.
It has a reader built in - you don't need to define one
It has the command text - you don't need to define one
it has a connection object - again no need to create one (but you look to have one)
And using a dataTable is nice, since you can use for/each, or use the MyTable.Rows(row num) to get a row.
And a datatable is nice, since you don't need a loop to READ the data - use the built in datareader in sqlcommand object.
Using cmdSQL As New SqlCommand("Select Id FROM tblekingbago", con)
cmdSQL.Connection.Open()
Dim MyTable As New DataTable
MyTable.Load(cmdSQL.ExecuteReader)
' table is now loaded with all "ID"
' you can see/use/display/play/have fun with ID like this:
For Each OneRow As DataRow In MyTable.Rows
Debug.Print(OneRow("Id"))
Next
' display the 5th row (it is zero based)
Debug.Print(MyTable.Rows(4).Item("Id"))
End Using
Here is my general understanding of database from what I read so far: Save / Update / Delete to pre-existing file made that binds to form thru SQL.
Here is what I am trying to do - I have a pre-made Data Table in Form with all columns defined. Once app is closed or certain functions ran, I need that data to be saved / updated in SQL (on local). Once app is open I need all that data to be preserved.
So far I have NOT found a single solution to it anywhere most refer to binding to an existing file. When I worked with Excel data transfer cells had to be defined and referenced in form for population.
My assumption is when a database from VB.NET is used, table with values can be created automatically saved/loaded/updated. However this is only my assumption since I never worked with SQL before. I am not sure if I need to manage an actual database file I created with all the values and then bind them to data table. For example DataTable cell XX to database column XX.
Here is what I done so far I have created database and added to my project. I tried few codes and I keep getting Dataset Empty even though there is Data in Table I tried to use DataTable as well but so far nothing has worked.
Please suggest on what I am doing wrong also additional information regards to databases will be great. As per previous I do know how binding works when actual file exist. But creating and managing is confusing to me since I keep thinking there should be a binding file.
Dim connetionString As String
Dim sqlCnn As SqlConnection
Dim sqlCmd As SqlCommand
Dim adapter As New SqlDataAdapter
Dim ds As New DataSet
Dim sql As String
connetionString = "Data Source=(LocalDB)\MSSQLLocalDB;AttachDbFilename=|DataDirectory|\Data_Ant.mdf;Integrated Security=True;Connect Timeout=30"
sql = "SELECT BN FROM DataTable" ' BN is my column name and DataTable is the name of my Table where data gets populated. This is also confusing to me How does it know which value is what? Is there are space/word/characters requirements?
' adapter.TableMappings.Add("DataTable", sql)
If ds.Tables.Count > 0 Then
sqlCnn = New SqlConnection(connetionString)
Try
sqlCnn.Open()
sqlCmd = New SqlCommand(sql, sqlCnn)
adapter.SelectCommand = sqlCmd
adapter.Update(ds)
adapter.Dispose()
sqlCmd.Dispose()
sqlCnn.Close()
Catch ex As Exception
MsgBox("Can not open connection !" & vbCrLf & Err.Description)
End Try
ElseIf ds.Tables.Count = 0 Then
MsgBox("Empty data")
End If
End Sub
Code I use to Create /Save Database. As per previous all columns/formats are pre-made, loaded.
Dim connetionString As String
Dim sqlCnn As SqlConnection
Dim sqlCmd As SqlCommand
Dim adapter As New SqlDataAdapter
Dim ds As New DataSet
Dim sql As String
connetionString = "Data Source=(LocalDB)\MSSQLLocalDB;AttachDbFilename=|DataDirectory|\Data_Ant.mdf;Integrated Security=True;Connect Timeout=30"
sql = "Select BN FROM DataTable"
adapter.TableMappings.Add("BN", sql)
If DataTable.RowCount > 0 Then
sqlCnn = New SqlConnection(connetionString)
Try
sqlCnn.Open()
sqlCmd = New SqlCommand(sql, sqlCnn)
adapter.SelectCommand = sqlCmd
adapter.Update(ds, "BN")
adapter.Dispose()
sqlCmd.Dispose()
sqlCnn.Close()
Catch ex As Exception
MsgBox("Can not open connection !" & vbCrLf & Err.Description)
End Try
ElseIf DataTable.RowCount = 0 Then
MsgBox("Empty data")
End If
End Sub
Please see more info below:
Data Table columns/format are structured for visual representation.
When User start the App Database can be empty/Can contain Values.
When users Runs certain function Closes App values are save and only values.
If I would you an MS Access I would structure same table/values and cross reference it with form values. Form Values come from outside source and Format/Qty is always known.
Hope this helps to have a cleaner look at my issue. Perhaps SQL is not a right choice for me? Does SQL needs to be build before value manipulation.
UPDATE: I Got rid of the Invalid Object error. Table had to be created 1st as I originally thought. However, My DataSet always comes up as EMPTY when I try to save... Cells do contain BN data as" 1,2, ....) Even if I to remove "If" logic Save and Load table comes out as empty. Something does load because when I try to ADD BN it tells me binding bla bla bla(different issue)
CODE:
Private Sub SaveData()
Dim connetionString As String = "Data Source=(LocalDB)\MSSQLLocalDB;AttachDbFilename=|DataDirectory|\Data_Ant.mdf;Integrated Security=True;Connect Timeout=30"
Dim sql As String = "SELECT BN FROM DataTable_d"
Dim sqlCnn As SqlConnection
Dim sqlCmd As SqlCommand
Dim adapter As New SqlDataAdapter
Dim ds As New DataSet()
adapter.TableMappings.Add("BN", sql)
If ds.Tables.Count > 0 Then
sqlCnn = New SqlConnection(connetionString)
Try
sqlCnn.Open()
sqlCmd = New SqlCommand(sql, sqlCnn)
adapter.SelectCommand = sqlCmd
adapter.Update(ds, "BN")
adapter.Dispose()
sqlCmd.Dispose()
sqlCnn.Close()
Catch ex As Exception
MsgBox("Can not open connection !" & vbCrLf & Err.Description)
End Try
ElseIf ds.Tables.Count = 0 Then
MsgBox("Empty data")
End If
End Sub
UPDATE: I got all the things working but I can't save multiple rows..... Could really use some help
In your SQL query remove WHERE DataTable ='. This statement is looking for a column name DataTable which I assume does not exist. The WHERE clause is used to help filter your query. You only use WHERE on column names in your table.
For instance:
SELECT BN FROM DataTable
will return all values from the BN column from DataTable.
Note that if you have multiple columns, the above query will still only return values from BN.
SELECT * FROM DataTable
will return every value in DataTable.
A helpful site to look at documentation for SQL is w3schools.
Let's start with just displaying some data. Add a DataGridView to a Form. You can call LoadData() from a button. I am not very sure of you connection string but give it a try.
Private dt As DataTable
Private sql As String = "Data Source=(LocalDB)\MSSQLLocalDB;AttachDbFilename=|DataDirectory|\Data_Ant.mdf;Integrated Security=True;Connect Timeout=30"
Private Sub LoadData()
'***EDIT*** Add instantiation line
dt = New DataTable()
'The Using...End Using blocks will close and dispose your database objects
'even if there is an error
Using cn As New SqlConnection(sql)
'You can pass the command text and the connection directly to the constructor
'In the select statement use the actual names of the field and table as they appear in the database.
Using cmd As New SqlCommand("Select BN From [Insert the name of the table in the database]", cn)
cn.Open()
dt.Load(cmd.ExecuteReader)
End Using
End Using
DataGridView1.DataSource = dt
End Sub
This is the simplest way I can think of to display data. We will proceed with changing the data once this works. If you get an error on cn.Open() We will have to work on the connection string.
****EDIT****
Private Sub TestConnection()
Dim sql As String = "Data Source=(LocalDB)\MSSQLLocalDB;AttachDbFilename=|DataDirectory|\Data_Ant.mdf;Integrated Security=True;Connect Timeout=30"
Using cn As New SqlConnection(sql)
cn.Open()
End Using
End Sub
I need a calculated column and to do achieve this I plan on using a query in TSQL from sql server 2014 the problem is I don't know how to use that query as data source for my dataGridView in vb.net. I'm using visual studio 2015. Can someone help point me in the right direction? I just need to display the results of my query in dataGridView. The calculated column will need data from multiple tables and the only way I can create the column is by using a TSQL query in SQL Server.
You cannot bind sql query to DataGridView.DataSource.
But you can with result of your query, for example you can get result as DataTable
Public Function GetData() As DataTable
Dim yourQuery As String = "...."
Using connection As New SqlConnection(yourConnectionString)
Using command As New SqlCommand(query, connection)
' Add parameters if you need
'command.Parameters.AddRange(arrayOfSqlParameters)
connection.Open()
Using adapter As New SqlDataAdapter(command)
Dim data As New DataTable()
adapter.Fill(data)
Return data
End Using
End Using
End Using
End Function
Then use result as DataSource
Dim data As DataTable = GetData()
myDataGridView.DataSource = data
If you don't like DataTable you can load data to the list of your classes which represent data and use it as datasource
Dim data As List<Customer> = GetCustomerData()
myDataGridView.DataSource = data
You need to create a stored procedure for your T-SQL code. And call the same stored procedure from VB.Net
Here is a good example explained for the same.
Using SQL Stored Procedures with VB.NET
Here's a variation of GetData() above. This one uses a DataReader instead which requires less overhead. There's some extra bells and whistles in there which aren't really needed - you really only need rdr = cmd.ExecuteReader()
Function GetDataTable(ByVal SQL As String, Optional ByVal ConnectString As String = "", Optional ByVal SingleRow As Boolean = False) As DataTable ' returns read only Datatable
Try
If ConnectString.Length = 0 Then ConnectString = g.OISConnectString()
Using con As New System.Data.SqlClient.SqlConnection(ConnectString)
Dim rdr As Data.SqlClient.SqlDataReader
con.Open()
Dim cmd As New SqlCommand(SQL, con)
If SingleRow Then
rdr = cmd.ExecuteReader(CommandBehavior.SingleRow Or CommandBehavior.CloseConnection)
Else
rdr = cmd.ExecuteReader(CommandBehavior.CloseConnection)
End If
Dim dt As New DataTable
dt.Load(rdr)
rdr.Close()
Return dt
End Using
Catch ex As Exception
MsgBox(ex.Message, , "GetDataTable")
Return Nothing
End Try
End Function
First question here, and forewarning I am very new to programming in general, just trying to figure the basics out.
Basically what I am trying to do is select all items in a specified list from my SQL server, add them to an array and then fill a combobox with that array.
The SQL server structure is as follows:
Just a representation
The user will select which product they are building when they log in, and then I would like a combobox to be filled with all of the parts that pertain to that item.
The current code sample
Public Class frmProduct
Private Sub frmProduct_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim mycon As SqlConnection = New SqlConnection('connection string')
mycon.Open()
Using mycon
Dim prodlist As String = frmMain.cmbProduct.SelectedItem
Dim cmds As String = "SELECT DISTINCT(Product) FROM [Products] where Product = #prodlist"
Dim cmde As New SqlCommand(sqlt, mycon)
cmde.Parameters.AddWithValue("#Product", prodlist)
Dim dr As SqlDataReader = scmd.ExecuteReader
If dr.HasRows() Then
cmbFailure.Items.Add(dr.GetString(0))
End If
scmd.ExecuteNonQuery()
End Using
End Sub
End Class
The user selects the Product on the login form, and the selected product is carried over to the parts list form.
The error I am currently recieving which is likely the first of many is "Invalid Column Name Product". I thought that the issue might be that the selected product is not carrying over, but I added a label which I am changing the text to be equal to "prodlist" and it accurately changes the label text.
Again I am fresh meat at all of this, so I apologize for any obvious blunders. Thank you for your help!
In the image you attached the "Product1, Product2, Product3" are fixed columns?
If are, you have to use these columns in your select statement. This is a bad design but will work.
I assume that's a study exercise, so later, can you enhance your project adding 2 tables, one for products and other for store parts of products.
For information about the code.
Private Sub frmProduct_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim mycon As SqlConnection = New SqlConnection('connection string')
Dim cmds As String = "SELECT DISTINCT(Product) FROM [Products] where Product = #prodlist"
Try
' Most of the DB will having a connection fail, so programmer must set a try catch for it '
mycon.Open()
Using mycon
Dim prodlist As String = [Form].[ComBoBox].SelectedItem
Dim cmde As New SqlCommand(sqlt, mycon)
cmde.Parameters.AddWithValue("#Product", prodlist)
Dim dr As SqlDataReader = scmd.ExecuteReader
If dr.HasRows() Then
cmbFailure.Items.Add(dr.GetString(0))
End If
scmd.ExecuteNonQuery()
' For more save for this sql will execute successfully, some of programmer will use Transaction with commit '
' Microsoft Link: https://msdn.microsoft.com/en-us/library/5ha4240h(v=vs.110).aspx '
End Using
' mycon.Close() '
Catch ex As Exception
MessageBox.Show(ex.Message) ' Exception Message '
' [Optional]Release object '
' mycon.Close() '
End Try
End Sub
And for your items with it may crash the result, due to scmd was not same as cmde.
Would you please explain [scmd] is stand for?