Can someone please explain to me when the "size" column wont work for comparison but I can replace the ID column in my code and it works perfectly fine. Perhaps my formatting of the access database column for Size isnt correct?
I am basically just trying to see if the key and value in my dictionary match the conditions in the access database and if so to write one text, if not write another. The error I keep getting when I have size in my code is:
An unhandled exception of type 'System.Data.OleDb.OleDbException' occurred in System.Data.dll IErrorInfo.GetDescription failed with E_FAIL(0x80004005).
For Each KeyPair In dict
Dim key As String
Dim value As Integer
key = KeyPair.Key
value = KeyPair.Value
Dim sqlQry As String
sqlQry = "SELECT Item, Size FROM [Table] WHERE Item = '" & key & "'AND Size>" & value & " "
Console.WriteLine(sqlQry)
Dim topDecision As String
Dim cmd As OleDbCommand
cmd = New OleDbCommand(sqlQry, myconnection)
Dim myreader As OleDbDataReader
myreader = cmd.ExecuteReader()
If myreader.Read() Then
topDecision = "Order"
Else
topDecision = "Dont"
End If
myreader.Close()
Next
Connections and some other database objects provided by ADO.net use unmanaged code internally. They provide a .Dispose method where they release these resources. It is up to the coder to call the .Dispose method. Fortunately, .net provides Using...End Using blocks that handle this for us. Connections, commands and readers should be declared in the method where they are used so they can be properly closed and disposed.
Don't concatenate strings to build sql queries. Use parameters to avoid sql injection. We only need a single command and a single ParametersCollection. Only the values of the parameters change inside the loop.
A special note for OleDb parameters. The names of the parameters are ignored. The position of the parameter in the sql query should match the order that they are added to the ParametersCollection.
Declare KeyPair As KeyValuePair so you can access .Key and .Value properties.
I used a StringBuilder to collect the messages from your code. A StringBuilder is mutable (changeable) whereas a String is not. If I used a String the compiler would have to throw away a string and create a new one on each iteration. The garbage collector would be kept busy.
I used an interpolated string indicated by the $ before the string. It allows us to insert variables directly into the string if they are surrounded by braces { }.
If you follow this sample, be sure the text box at the end has Multiline = True.
Private Sub OPCode()
Dim sqlQry = "SELECT Item, Size FROM [Table] WHERE Item = #Key AND Size > #Value;"
Dim sb As New StringBuilder
Using cn As New OleDbConnection("Your connection string"),
cmd As New OleDbCommand(sqlQry, cn)
cmd.Parameters.Add("#Key", OleDbType.VarChar, 100)
cmd.Parameters.Add("#Value", OleDbType.Integer)
cn.Open()
For Each KeyPair As KeyValuePair(Of String, Integer) In dict
cmd.Parameters("#Key").Value = KeyPair.Key
cmd.Parameters("#Value").Value = KeyPair.Value
Dim topDecision As String
Using myreader = cmd.ExecuteReader()
If myreader.Read() Then
topDecision = "Order"
Else
topDecision = "Dont Order"
End If
End Using
sb.AppendLine($"{KeyPair.Key} - {topDecision}")
Next
End Using
TextBox1.Text = sb.ToString
End Sub
Related
Trying to update an old VB6 app to VB.Net. I am having trouble with syntax, I think. In any case it is a simple matter of inserting a new record to the autolog table. (code below).
I would like to ask something else that is often not documented too. It seems that I have to use command builders and so on - is there no way I can simply use an SQL statement and execute it against the background table? The tables are in Access while I am developing but will be scaled up on the final release of the software.
I have altered my code to the following by making use of the error suggestions at the foot of mygui.
It now looks like this and the only thing is that it is throwing a logic error at me which is that every end function must have a preceding "function". Perhaps I am being a little bit dim
Function MAutolog(ByVal Action As String) As Boolean
Dim SQL = "Insert Into Autolog (Action) Values (#Action)"
Using con As New OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=C:\Users\PC User\Documents\Freightmaster\resources\freightmaster.accdb"),
cmd As New OleDb.OleDbCommand(SQL, con)
cmd.Parameters.Add("#Action", OleDb.OleDbType.VarChar).Value = Action
con.Open()
cmd.ExecuteNonQuery()
End Using
MAutolog = True
End Function
I would like to thank you for your help in advance. I can not tell you how much I will appreciate it.
Code
Module ModFunctions
Function MAutolog(ByVal UserID As Long, ByVal Action As String) As Boolean
Dim dbprovider As String
Dim dbsource As String
Dim mydocumentsfolder As String
Dim fulldatabasepath As String
Dim TheDatabase As String
Dim SQL As String
Dim DS As New DataSet
Dim da As OleDb.OleDbDataAdapter
Dim con As New OleDb.OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=C:\Users\PC User\Documents\Freightmaster\resources\freightmaster.accdb")
con.Open()
'----------------------------
SQL = "Select * from Autolog"
da = New OleDb.OleDbDataAdapter(SQL, con)
da.Fill(DS, "Log")
con.Close()
Dim CB As New OleDb.OleDbCommandBuilder(da)
Dim DSNEWROW As DataRow
DSNEWROW = DS.Tables("Log").NewRow()
DSNEWROW.Item("UserID") = UserID
DSNEWROW.Item("Action") = Action
DS.Tables("log").Rows.Add(DSNEWROW)
da.Update(DS, "log")
MAutolog = True
End function
Database objects like Connection and Command use unmanaged code and need their Dispose methods to release these resources. Either call this method on these objects or use Using...End Using blocks which will do this for you even if there is an error. In this code, both the Connection and Command are included in the Using block by separating them be a comma.
By Val is the default so is not necessary.
Always use parameters to avoid sql injection. Using values directly from user input can allow malicious code to be executed on your database. The value of a parameter is not considered as executable code by the database.
OleDb does not care about parameter names. You could just as easily use ? in the sql statement. I use names for readability. You do need some sort of name to add the parameter. OleDb considers the position of the parameter in the sql statement. The position must match the order that the parameters are added to the parameters collection.
This is the code for the Insert if UserID in an auto-number field. You do not provide a value for auto-number fields. The database will handle that.
Function MAutolog(Action As String) As Boolean
Dim SQL = "Insert Into Autolog (Action) Values (#Action)"
Using con As New OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=C:\Users\PC User\Documents\Freightmaster\resources\freightmaster.accdb"),
cmd As New OleDbCommand(SQL, con)
cmd.Parameters.Add("#Action", OleDbType.VarChar).Value = Action
con.Open()
cmd.ExecuteNonQuery()
End Using
MAutolog = True
End Function
If UserID is not auto-number
Function MAutolog(UserID As Long, Action As String) As Boolean
Dim SQL = "Insert Into Autolog (UserID, Action) Values (#UserID, #Action)"
Using con As New OleDb.OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=C:\Users\PC User\Documents\Freightmaster\resources\freightmaster.accdb"),
cmd As New OleDbCommand(SQL, con)
cmd.Parameters.Add("#UserID", OleDbType.Integer).Value = UserID
cmd.Parameters.Add("#Action", OleDbType.VarChar).Value = Action
con.Open()
cmd.ExecuteNonQuery()
End Using
MAutolog = True
End Function
As the title says I have a MS Access database from where I need to find a specific dataset determined by a String Value. the reason for having to do this is so I can find the value of a single cell in this datase which has to be used as a path to find a certain file. my approach so far is the following:
Dim conn As New OleDb.OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=|DataDirectory|\ExaptLokal.accdb")
Dim cmd As New OleDb.OleDbCommand
Dim dt As New DataTable
Dim da As New OleDb.OleDbDataAdapter
Dim sql As String
sql = "SELECT NC_KEY FROM EXAPT_NC_KOPF_DATEN WHERE NC_PROGRAMM_NAME =" & ProgrammNr.Text.ToString
MsgBox(sql)
conn.Open()
cmd.Connection = conn
cmd.CommandText = sql
da.SelectCommand = cmd
da.Fill(dt)
fullpath = dt.ToString
at the end I would like to have the result from my SQL Query as the value of my "fullpath" variable but so far the da.Fill(dt) row is giving me a hard time saying there is a conflict with the datatype.
Is the datatable even needed in this case or might I be able to skip that step and get the result of the query directly in the fullpath variable?
Thanks to everyone in advance
Edit: Thanks for the help (though not the friendliest but who am I to judge) I finally got it to work with the Execute Scalar method. I would just wish newbies to this website would be greeted a little better lol
have a great day
I moved the connection string to a class level variable so you can use it in other methods.
I separated your data access code from your user interface code passing the value from the text box to a function that returns the path.
I changed your select statement to use parameters. Always use parameters to avoid sql injection and avoid errors.
Use Using...End Using blocks to ensure that your database objects are closed and disposed. This Using block covers both the command and the connection.
You can pass the .CommandText and the .Connection directly to the constructor of the command.
When adding parameters to the parameters collection you provide the parameter name, the datatype form the database, and the size of the field. I had to guess at the type and size so, check your database for the actual values.
Since you are expecting a single value you can use .ExecuteScalar.
Private ConStr As String = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=|DataDirectory|\ExaptLokal.accdb"
Private Function GetPath(ProgrammNr As String) As String
Dim fullpath As String
Dim sql = "SELECT NC_KEY FROM EXAPT_NC_KOPF_DATEN WHERE NC_PROGRAMM_NAME = #ProgrmmNr"
Using conn As New OleDb.OleDbConnection(ConStr),
cmd As New OleDb.OleDbCommand(sql, conn)
cmd.Parameters.Add("#ProgrmmmNr", OleDbType.VarChar, 100).Value = ProgrammNr
conn.Open()
fullpath = cmd.ExecuteScalar.ToString
End Using
Return fullpath
End Function
Usage...
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim fullpath = GetPath(ProgrammNr.Text)
End Sub
Unable to get the excepted result due to the following error
Conversion from string " to type 'integer' is not valid'
I have been able to load values from the products table, add selected ones to Selected Products and then search all the selected products against the Customers table to find out how many customers ordered these products.
Try
Dim ListOfDiag As StringBuilder = New StringBuilder()
For Each row As DataGridViewRow In SelectedDiagDGV.Rows
ListOfDiag.Append(row.Cells(0).Value.ToString & "", "" & Environment.NewLine)
Next
Dim query As String = String.Empty
Dim SegmentConnectionString As String = "Data Source=Test-PC;Initial Catalog=TestDB;Integrated Security=True"
query = "SELECT Customers, ProductName from Customers WHERE ProductName in (" & ListOfDiag.ToString & ")"
Dim dTable As DataTable = New DataTable()
Dim dAdapter As SqlDataAdapter
dAdapter = New SqlDataAdapter(query, SegmentConnectionString)
dAdapter.Fill(dTable)
DataGridView1.DataSource = dTable
'Next
Catch ex As System. Exception
MsgBox(ex.Message.ToString)
End Try
Unable to perform a for loop search. Some of the values contain special characters example: Soft ’Drink’; Computer (Desk).
Error: Conversion from string " to type 'Integer' is not valid.
ListOfDiag.Append(row.Cells(0).Value.ToString & "", "" & Environment.NewLine)
There is no overload of StringBuilder.Append that takes (String, String) as arguments. the first string is row.Cells(0).Value.ToString & "" and then there is a comma between parameters and the second string is "" & Environment.NewLine Remember that "" is an empty string, not escape characters. Not sure what your intention was but this will not work.
You had the right approach; to build a string for the In clause. I used a List(Of String) to get the data from the rows then after the loop I used a .Join with a comma separator to get the value for the In clause.
I passed the connection string directly to the constructor of the Connection and passed the Select statement and the connection to the constructor of the Command. For the Select statement I used and Interpolated String (the string preceded by the $) You could also use String.Format in older version of Visual Studio.
The Using...End Using blocks ensure that your database objects are closed and disposed even if there is an error.
I think the only special character that could mess things up would be the presence of a comma in a Product Name.
Private Sub OPCode()
Dim dTable As New DataTable
Dim ListOfDiag As New List(Of String)
For Each row As DataGridViewRow In SelectedDiagDGV.Rows
ListOfDiag.Add(row.Cells(0).Value.ToString)
Next
Dim InData = String.Join(",", ListOfDiag)
Using cn As New SqlConnection("Data Source=Test-PC;Initial Catalog=TestDB;Integrated Security=True")
Using cmd As New SqlCommand($"SELECT Customers, ProductName from Customers WHERE ProductName in ({InData})", cn)
cn.Open()
dTable.Load(cmd.ExecuteReader)
End Using
End Using
DataGridView1.DataSource = dTable
End Sub
I am trying to convert VBA code into VB.net and I have made it to a point but I can't convert resultset into vb.net. RS was 'dim as resultset' in VBA, thought i could just change it to dataset but am getting errors with the '.fields' and other options?
Function GetG(sDB As String, sServ As String, sJob As String) As String
'sDB = Database name, sServ = Server\Instance, path = job.path
Dim conString As String = ("driver={SQL Server};server = " &
TextBox1.Text & " ; uid = username;pwd=password:database = " &
TextBox2.Text)
Dim RS As DataSet
Dim conn As SqlConnection = New SqlConnection(conString)
Dim cmd As SqlCommand
conn.Open()
'This is where my problems are occuring
cmd = New SqlCommand("SELECT [ID],[Name] FROM dbo.PropertyTypes")
Do While Not RS.Tables(0).Rows.Count = 0
If RS.Fields(1).Value = sJob Then
GetG = RS.Fields(0).Value
GetG = Mid(GetG, 2, 36)
Exit Do
End If
DataSet.MoveNext
Loop
conn.Close
End Function
Based on my understanding and some guesswork, here is what I came up with for what I think you're wanting.
As I stated in my comment above, it appears you can just use a WHERE clause to get the exact record you want (assuming a single instance of sJob appears in the name column).
Build the connectionstring off the input arguments, not controls on your form. That is after all why you allow for arguments to be passed along. Also note that there is a SqlCommandBuilder object that may be of interest. But for now
Function GetG(sDB As String, sServ As String, sJob As String) As String
'we'll pretend your connectionstring is correct based off of the sDB and sServ arguments
Dim conStr As String = ("driver={SQL Server};server = " & sServ & " ; uid = username;pwd=password:database = " & sDB)
'Create a connection and pass it your conStr
Using con As New SqlConnection(conStr)
con.Open() 'open the connection
'create your sql statement and add the WHERE clause with a parameter for the input argument 'sJob'
Dim sql As String = "SELECT [ID], [Name] FROM dbo.PropertyTypes WHERE [Name] = #job"
'create the sqlCommand (cmd) and pass it your sql statement and connection
Using cmd As New SqlCommand(sql, con)
'add a parameter so the command knows what #job holds
cmd.Parameters.Add(New SqlParameter("#job", SqlDbType.VarChar)).Value = sJob
'Now that have the command built, we can pass it to a reader object
Using rdr As SqlDataReader = cmd.ExecuteReader
rdr.Read()
'i admin i'm a little confused here on what you are
'trying to achieve so ID may not be what you are
'really wanting to get a substring of.
Return rdr("ID").ToString.Substring(2, 36)
End Using
End Using
End Using
End Function
An example to see if this is working could be to call a messagebox do display the result. For this example, I'm going to pretend that TextBox3 holds the sJob you're wanting. With that knowledge, you could simply do:
MessageBox.Show(GetG(TextBox2.Text, TextBox1.Text, TextBox3.Text))
This should then produce the result in a messagebox.
It seems that you're not filling your DataSet. So, when you try to loop through it, it's uninitialized or empty.
Check this answer to see an example: Get Dataset from DataBase
I am trying to add the vote count after the selected user from the ddl.
I Don't see what I did wrong.
Dim str As String
str = "update [vote] SET [voteweight] = [voteweight]+1 where [userID] = 'DropDownList4.Selectedvalue'"
Dim Conn As New OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;; data source=" & Server.MapPath("App_Data/final.mdb"))
Dim cmd As New OleDbCommand(str, Conn)
Conn.Open()
cmd.ExecuteNonQuery()
Conn.Close()
The correct way to pass a value to an sql command is through a parameterized query. Otherwise the errors that could be embedded in your string are very numerous and subtle.
In your code, the single quotes around the control value, transform everything in a literal string and, of course, the UserID field is a numeric field that cannot be compared against a string
if DropDownList4.Selectedvalue = Nothing then
Return
End If
Dim str = "update [vote] SET [voteweight] = [voteweight]+1 " & _
"where [userID] = #userid"
Using Conn = New OleDbConnection(.....)
Using cmd As New OleDbCommand(str, Conn)
Conn.Open()
cmd.Parameters.Add("#userid", OleDbType.Int).Value = Convert.ToInt32(DropDownList4.Selectedvalue)
cmd.ExecuteNonQuery()
End Using
End Using
Other things to keep present are:
Using Statement around disposable object like the connection and the command (thus avoiding the possibility to leak resources if an exceptions triggers and you forget to dispose them)
A check around the SelectedValue for null is always a good measure. Sometimes your control could loose the Selection and you should be absolutely sure that there is no way to enter this code with a SelectedValue = Nothing.