Hi folks. I am trying to update an app to VB dot net from vb6 and have enouctered a really basic problem. I will add the code of course in a sec. I - vb.net

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

Related

Two Connections types in one method (SQL and OLEDB)

I have two connections types to be used in my app. One is SQL Server, using the SqlClient.Connection type. The other one uses the OleDb.Connection type.
The question is, when using a DataAdapter, DataReader, and Command, how can I use one reference for each with both connections?
As an example, I want to use a reader like this:
Dim MyReader As OleDb.OleDbDataReader
to check my data with an OleDbConnection, and then use same reader to check data from the second SqlClient connection. That is, I want to do something like this (pseudocode):
Dim con
Dim MyReader
con = oledb.connection
MyReader = mymethod(con)
con = sql.sqlclient.conection
MyReader = mymethod2(con)
How can I do this in real code?
I need help in how to declare data components for two different connection types inside the same method or function.
You should declare multiple variables. It's really not a big deal to do so.
But, if you really want to do this (again: not the best idea) one thing you can keep in mind is these objects all inherit from a common set of types in the System.Data.Common namespace. So it possible to write code like this:
Dim con As System.Data.Common.DbConnection = New OleDbConnection("connection string here")
Dim cmd As System.Data.Common.DbCommand = New OleDbCommand("SELECT * ... ", con)
con.Open()
Dim rdr As System.Data.Common.DbDataReader = con.ExecuteReader()
While rdr.Read()
' ...
End While
con.Close()
con = New SqlClient.SqlConnection("connection string here")
cmd = New SqlClient.SqlCommand("SELECT * ...", con)
con.Open()
rdr = cmd.ExecuteReader()
While rdr.Read()
' ...
End While
But again: you really are better off using separate variables.

How can I add the value of a single Cell from Access database to a string variable

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

How can I get my datatable to update - vb.net?

Dim strSQL as string = "select ScreenName, Status from ScreenCheckDuplicates where ScreenName='" & ScreenName & "'"
Dim aObj as new SqlDataAdapter(strSQL,conn)
dim dtObj as New DataTable
aObj.Fill(dtObj)
If dtObj.Rows.Count > 0 Then
dtObj.Rows(0)("Status") = Status
dtObj.AcceptChanges()
Else
Dim drNew as DataRow = dtObj.NewRow()
drNew("ScreenName") = ScreenName
drNew("Status") = Status
dtObj.Rows.Add(drNew)
dtObj.AcceptChanges()
End If
With Rows.Count > 0 (The ScreenName is in the Table), the Status will not update.
When I removed all rows from the DataTable such that the Else clause would run, No new row was added.
So... I must be missing how it is updating the table and need a bit of help. I'm betting it is pretty simple and I'm just missing it :(
Although you have created the SqlDataAdapter with SELECT command so it can fetch data, you have not told it how to UPDATE or INSERT data.
These need to be explicitly added to the SqlDataAdapter so that it understands how to perform these data updates.
I have mocked up an example of how to do this but it may be non-functional as the exact SQL syntax will depend upon your table definition:
Dim aObj As New SqlDataAdapter(strSQL, conn)
' Create the update command
aObj.UpdateCommand = New SqlCommand("UPDATE ScreenCheckDuplicates SET Status = ? WHERE ScreenName = ?")
aObj.UpdateCommand.Parameters.Add("Status", SqlDbType.VarChar)
aObj.UpdateCommand.Parameters.Add("ScreenName", SqlDbType.VarChar)
' Create the insert command
aObj.InsertCommand = New SqlCommand("INSERT INTO ScreenCheckDuplicates VALUES (?, ?)")
aObj.InsertCommand.Parameters.Add("Status", SqlDbType.VarChar)
aObj.InsertCommand.Parameters.Add("ScreenName", SqlDbType.VarChar)
This Microsoft article describes the precise method you can use to achieve your aim.
First of all, do not concatenate strings to create an sql command. This leads to Sql Injection attacks and to syntax errors if your string contains a single quote. Instead you should use parameters
Dim strSQL as string = "select ScreenName, Status
from ScreenCheckDuplicates
where ScreenName=#name"
Dim aObj as new SqlDataAdapter(strSQL,conn)
aObj.SelectCommand.Parameters.Add("#name", SqlDbType.NVarChar).Value = ScreenName
dim dtObj as New DataTable
aObj.Fill(dtObj)
Now a common error is to think that AcceptChanges updates the database table. This is wrong, AcceptChanges changes the RowState property for every row in your DataTable object from "DataRowState.Modified" (or other values) to "DataRowState.Unchanged" and after this call there is no way to know which rows have been changed and no simple way to update your database. So remove that line
If dtObj.Rows.Count > 0 Then
dtObj.Rows(0)("Status") = Status
Else
Dim drNew as DataRow = dtObj.NewRow()
drNew("ScreenName") = ScreenName
drNew("Status") = Status
dtObj.Rows.Add(drNew)
End If
At this point you are ready to commit your changes to the database. You can use the SqlCommandBuilder object to create the sql commands required to update your rows. But this will work only if you have retrieved the primary key of your database table.
So assuming that ScreenName is the primary key then you can write
Dim builder As SqlCommandBuilder = new SqlCommandBuilder(aObj)
aObj.Update(dtObj)
I am making the assumption that ScreenName is the primary key for the ScreenCheckDuplicates table. Methods to Update a table use the primary key.
Keep your database objects local so you can control if they are closed and disposed. Using...End Using blocks handle this for you even if there is an error.
Always used Parameters to avoid Sql injection. I had to guess at the SqlDbType and the field size. Check your database for the actual values and adjust the code accordingly.
When you use a DataAdapter you need to provide the commands that you need. A command builder will do this for you. Don't call .AcceptChanges on the DataTable until you have used the Update method on the DataAdapter.
Private Sub OpCode(ScreenName As String, Status As String)
Using conn As New SqlConnection("Your connection string"),
cmd As New SqlCommand("select ScreenName, Status from ScreenCheckDuplicates where ScreenName= #ScreenName", conn)
cmd.Parameters.Add("#ScreenName", SqlDbType.VarChar, 100).Value = ScreenName
Using aObj As New SqlDataAdapter(cmd)
Dim dtObj As New DataTable
aObj.Fill(dtObj)
Dim cb As New SqlCommandBuilder(aObj)
If dtObj.Rows.Count > 0 Then
dtObj.Rows(0)("Status") = Status
cb.GetUpdateCommand()
Else
Dim drNew As DataRow = dtObj.NewRow()
drNew("ScreenName") = ScreenName
drNew("Status") = Status
dtObj.Rows.Add(drNew)
cb.GetInsertCommand()
End If
aObj.Update(dtObj)
dtObj.AcceptChanges()
End Using
End Using
End Sub
The following alternative method is a bit better because it only requires a single hit on the database.
Private Sub BetterWay(ScreenName As String, Status As String)
Dim strSql = "If EXISTS(SELECT ScreenName, Status FROM ScreenCheckDuplicates where ScreenName= #ScreenName)
UPDATE ScreenCheckDuplicates SET Status = #Status WHERE ScreenName = #ScreenName
Else
INSERT INTO ScreenCheckDuplicates ScreenName, Status VALUES (#ScreenName, #Status)"
Using cn As New SqlConnection("Your connection string"),
cmd As New SqlCommand(strSql, cn)
cmd.Parameters.Add("#ScreenName", SqlDbType.VarChar, 100).Value = ScreenName
cmd.Parameters.Add("#Status", SqlDbType.VarChar, 50).Value = Status
cn.Open()
cmd.ExecuteNonQuery()
End Using
End Sub

AutocompleteExtender from a 65000 record in sql table

I'm trying to Autocomplete City names from a huge sql table. My code below work but its very slow and sometimes it freezes because I got a lot repeated suggestions. any way to filter all this repeated string to make it faster ? thanks
Public Function GetCompletionList(ByVal prefixText As String, ByVal count As Integer) As String()
Dim strCn As String = "Data Source=sqlserver\sqlexpress;Initial Catalog=zip;User ID=sa;Password=xxx"
cn.ConnectionString = strCn
Dim cmd As New SqlClient.SqlCommand
cmd.Connection = cn
cmd.CommandType = CommandType.Text
cmd.CommandText = "select * from zip_code Where City like #myParameter+'%'"
cmd.Parameters.AddWithValue("#myParameter", prefixText)
Try
cn.Open()
cmd.ExecuteNonQuery()
Dim da As New SqlDataAdapter(cmd)
Dim dt As New DataTable()
da.Fill(ds)
Catch ex As Exception
Finally
cn.Close()
End Try
dt = ds.Tables(0)
Dim txtItems As New List(Of String)()
Dim dbValues As String
For Each row As DataRow In dt.Rows
dbValues = row("City").ToString()
dbValues = dbValues.ToLower()
txtItems.Add(dbValues)
Next
Return txtItems.ToArray
You are executing your SQL twice, once during ExecuteNonQuery, second time during Fill.
You are not using IDisposable objects correctly.
You return all columns.
If you have a control for display, check if it will accept Rows (Dataset?) property directly. Don't copy.
UPDATE
Your connection, command and other objects implement an IDisposable interface. So they should be placed in a using block:
Using { resourcelist | resourceexpression }
[ statements ]
End Using
Then you don't have to worry about closing and disposing a database connection.
UPDATE 2
Your code has thread safety issues, the same connection must not be shared by two different threads. EVER. Two consecutive Fill request and one will close the database connection before the other can finish.

Trying to delete a record from my access database in visual basic studio 2010

Private Function CreatePlayerAdapter(ByVal playerDBconnection As OleDbConnection) As OleDbDataAdapter
// Initiating instances for the function
Dim dataAdapter As OleDbDataAdapter = New OleDbDataAdapter()
Dim myCommand As OleDbCommand
Dim parameter As OleDbParameter
// establishing the string to tell where to delete record from and how to find the record i want.
// PlayerIDTextBox.Text is a text on a form that is populated from the database after selecting a list of name (this works correctly) // connection is already open and is directed to correct place
Dim sql As String = "DELETE * FROM Players WHERE ID ='" & CInt(PlayerIDTextBox.Text) & "'"
myCommand = New OleDbCommand(sql, playerDBconnection)
parameter = myCommand.Parameters.Add("ID", OleDbType.Char, 3, "ID")
parameter.SourceVersion = DataRowVersion.Original
dataAdapter.DeleteCommand = myCommand
Return dataAdapter
End Function
// i call this function after executing a button click.
//ListPlayerComboBox.Text is populated with the names and needs it a name to fill PlayerIDTextBox.Text(works correctly)
Private Sub RemovePlayerButton_Click(sender As System.Object, e As System.EventArgs) Handles RemovePlayerButton.Click
If ListPlayerComboBox.Text = " " Then
MsgBox("Please Select a Player.")
Else
Me.CreatePlayerAdapter(playerDBConnection)
End If
End Sub
// no errors occur. However, nothing is done in the database. help please?
Notes:
1)Never leave your OleDbConnection Open. Only allow it to be opened when you actually need it. This will save you from a lot of headaches later on. The reasons why can be found on following stackoverflow question.
2) There is no reason to return an OleDbDataAdapter if you don't intend on using it.
3) Use your parameters correctly : see below example2
4) Keep in mind that there are some restricted keywords in Access. Luckely for you ID isn't one. The restrictedKeywords can be found here: Keywords
I'm probably missing some further points here. Anyone should be free to add em.
Why not adjust your Function CreatePlayerAdapter to the following:
1) Without parameters
Private Sub CreatePlayerAdapter(ByVal playerDBconnection As OleDbConnection)
Dim myCommand As OleDbCommand
Dim sql As String = "DELETE * FROM Players WHERE ID =" & CInt(PlayerIDTextBox.Text)
myCommand = New OleDbCommand(sql, playerDBconnection)
playerDBconnection.Open()
myCommand.ExecuteNonQuery()
playerDBconnection.Close()
End Sub
2) With parameters
Private Sub CreatePlayerAdapter(ByVal playerDBconnection As OleDbConnection)
Dim myCommand As OleDbCommand
Dim sql As String = "DELETE * FROM Players WHERE ID = #playerId"
myCommand = New OleDbCommand(sql, playerDBconnection)
Dim param As New OleDb.OleDbParameter(#playerId", CInt(PlayerIDTextBox.Text))
myCommand.Add(param)
playerDBconnection.Open()
myCommand.ExecuteNonQuery()
playerDBconnection.Close()
End Sub
The method ExecuteNonQuery executes the query passed to the command on the specified OleDbConnection and returns the number of rows affected. More info Here