I am attempting to Insert a Row on a Table, but I cannot seem to pass the TASK_ID as a value/variable/function into the SQL Statement. Throughout the application, to acquire TASK_ID, I use:
Dim taskID = ds.Tables("ReminderTable").Rows(Count).Field(Of Integer)("TASK_ID")
Some helpful context-code :
da.Fill(ds, "ReminderTable")
Count = ds.Tables("ReminderTable").Rows.Count - 1
Here is what I'm trying to do:
da.InsertCommand = New OleDbCommand("INSERT INTO ACTIVITY (TASK_ID, ACTIVITY_CDE, ACTIVITY_NOTE) VALUES (3827, 1, 'Reminder Sent: ' & Now)", Connection)
I keep receiving the error
No value given for one or more required parameters.
when I replace 3827 with a variable/function/reference.
The statement works as-is, but I need to replace "3827" in the above statement with the TASK_ID, but it won't accept my variable, a function or the location that I use elsewhere throughout the project to reference the same thing.
I'm working in Visual Studio 2019, using a Microsoft Access Database
Use String Interpolation
da.InsertCommand = New OleDbCommand(
$"INSERT INTO ACTIVITY (TASK_ID, ACTIVITY_CDE, ACTIVITY_NOTE)
VALUES ({taskID}, 1, 'Reminder Sent: ' & Now())", Connection)
If getting the date and time from .NET, you would be able to add that with string interpolation as well
da.InsertCommand = New OleDbCommand(
$"INSERT INTO ACTIVITY (TASK_ID, ACTIVITY_CDE, ACTIVITY_NOTE)
VALUES ({taskID}, 1, 'Reminder Sent: {DateTime.Now}')", Connection)
Try Adding the Actual Dimension to your First Line of code Like so:
Dim taskID as Integer = ds.Tables("ReminderTable").Rows(Count).Field(Of Integer)("TASK_ID")
You could also write like this:
Dim taskID as Integer = ds.Tables("ReminderTable").Rows(Count)("TASK_ID")
Hope this helps
Here is a really basic example. It is in C#, but you can easily translate it into VB. The important part is the cmd.Parameters.AddWithValue.
public int Create(EmployeeClassification classification)
{
if (classification == null)
throw new ArgumentNullException(nameof(classification), $"{nameof(classification)} is null.");
const string sql = #"INSERT INTO EmployeeClassification (EmployeeClassificationName) VALUES(#EmployeeClassificationName )";
using (var con = OpenConnection())
using (var cmd = new OleDbCommand(sql, con))
{
cmd.Parameters.AddWithValue("#EmployeeClassificationName", classification.EmployeeClassificationName);
return cmd.ExecuteNonQuery();
}
}
Source: https://grauenwolf.github.io/DotNet-ORM-Cookbook/SingleModelCrud.htm#ado.net
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
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
I've found this answer in C (Or C# maybe), but I don't really understand C. I'm a vb guy (and new to .net). Can anyone give me a hand in converting this to vb?
The concept appears to be exactly what I need:
I've got an Excel worksheet loaded to a dataset table.
I have an identical table in the SQLite db (column for column).
I want to save the imported Worksheet data in the dataset table to the empty SQLite table.
Here is the code from the link:
SQLiteConnection m_dbConnection;
void createDbAndTable()
{
SQLiteConnection.CreateFile("MyDatabase.sqlite");
m_dbConnection = new SQLiteConnection("Data Source=MyDatabase.sqlite; Version=3;");
m_dbConnection.Open();
string sql = "create table myValues (name varchar(20), highScore int)";
SQLiteCommand command = new SQLiteCommand(sql, m_dbConnection);
command.ExecuteNonQuery();
}
void fillTable(DataSet ds)
{
var dt = ds.Tables[0];
foreach (DataRow dr in dt.Rows)
{
var name = dr["name"].ToString();
var score = Convert.ToInt32(dr["value"].ToString());
string sql = "insert into myValues (name, highScore) values ( '" + name + "'," + score + ")";
SQLiteCommand command = new SQLiteCommand(sql, m_dbConnection);
command.ExecuteNonQuery();
}
m_dbConnection.Close();
}
Any help would be greatly appreciated!
Good Day
I am using VB 2017 to create an application. i am using an Access Database.
When i an running my code i get an Insert Into Syntax error
my code is as follows.
Please help.
Public Shared Function AddLocation(location As Location) As Integer
Dim connection As OleDbConnection = AutoBeautyCareDB.GetConnection
Dim insertStatement As String = "Insert Into Location (CUST#,HOSP_ID,FLOOR,ROOM) VALUES(?,?,?,?)"
Dim insertCommand As OleDbCommand = New OleDbCommand(insertStatement, connection)
insertCommand.Parameters.AddWithValue("Cust#", location.CustNo.ToString)
insertCommand.Parameters.AddWithValue("HospId", location.HospId.ToString)
insertCommand.Parameters.AddWithValue("Floor", location.Floor.ToString)
insertCommand.Parameters.AddWithValue("Room", location.Room.ToString)
Try
connection.Open()
insertCommand.ExecuteNonQuery()
Dim selectStatement As String = "Select ##Identity"
Dim selectCommand As New OleDbCommand(selectStatement, connection)
insertCommand.CommandText = selectStatement
Dim locationId As Integer = insertCommand.ExecuteScalar
Return locationId
Catch ex As OleDbException
Throw ex
Finally
connection.Close()
End Try
End Function
When you use a special symbol like # you need to enclose the field name between square brackets, however it is best to change that name to something less problematic
Dim insertStatement As String = "Insert Into Location
([CUST#],HOSP_ID,FLOOR,ROOM)
VALUES(?,?,?,?)"
Also remember that AddWithValue, while it seems to be a useful shortcut, has many problems as explained in the following article
Can we stop using AddWithValue already?
A single line approach with better parameter handling is the following
insertCommand.Parameters.Add("Cust#", OleDbType.Integer).Value = location.CustNo
(Assuming Cust# is an integer type in your database table)
I am currently coding in VB.net using windows form applications and an sql server for my tables. I am creating an order form with two comboboxes, one for the type of material to be ordered and one for a name. There is also a submit button at the bottom to run the SQL "Insert Into" code. The material combobox is filled with a column of material types from an SQL table called "tbl.channel". Each material type under that column has a part number, ID, and bundle size associated with that row. I want ALL of the information associated with that material type to write into a new table that records all the orders, with the user only selecting the material type from a combobox. How can I use a "Select From" sql code to pull the associated information with that material type to be written into a new table that tracks all the material ordered?
Try
Dim connectionstring As String = Nothing
Dim connection As SqlConnection
Dim command As SqlCommand
Dim adapter As New SqlDataAdapter
Dim ds As New DataTable
Dim i As Integer = 0
Dim sql As String = Nothing
connectionstring = "DATA SOURCE = BNSigma\Core ; integrated security = true"
sql = "Select Channel, [Bundle Size], ID from Production.dbo.tblchannel"
connection = New SqlConnection(connstring)
connection.Open()
command = New SqlCommand(sql, connection)
adapter.SelectCommand = command
adapter.Fill(ds)
Catch ex As Exception
MsgBox(ex.ToString)
End Try
Try
Using conn1 As New SqlConnection(connstring)
conn1.Open()
Using comm1 As SqlCommand = New SqlCommand("INSERT INTO Production.dbo.tbl (Channel, OrderedBy, Date1, BundleSize, ID) Values (#Channel, #Orderedby, #getdate(), #BundleSize, #ID)", conn1)
With comm1.Parameters
.AddWithValue("#Channel", CBChannel.SelectedValue)
.AddWithValue("#OrderedBy", CBName.SelectedValue)
.AddWithValue("#BundleSize", CBChannel.SelectedValue)
.AddWithValue("#ID", CBChannel.SelectedValue)
End With
End Using
End Using
Catch ex As Exception
MsgBox("Unable to make SQL connection")
MsgBox(ex.ToString)
End Try
I'm not certain I understand your goal, but are you looking to just do this?
INSERT Production.dbo.tbl (Channel, OrderedBy, Date1, BundleSize, ID)
SELECT Channel, #Orderedby, getdate(), BundleSize, ID
FROM tbl.channel
WHERE ID = #ID
I am pretty well convinced though that you should only be writing the channel.ID to the orders table -- do you have a compelling reason to write the extra values?
SQL lets you use a SELECT query to populate the values for an INSERT statement. Something like this:
Public Sub PopulateOrder(ByVal MaterialID As Integer, ByVal SalesName As String)
Dim sql As String = "INSERT INTO Production.dbo.tbl (Channel, OrderedBy, Date1, BundleSize, ID) SELECT Channel, #SalesName, current_timestamp, [Bundle Size], ID from Production.dbo.tblchannel WHERE ID = #MaterialID"
Using cn As New SqlConnection("connection string here"), _
cmd As New SqlCommand(sql, cn)
'Better to declare a specific database type than let .AddWithValue() try to infer one for you
cmd.Parameters.Add("#SalesName", SqlDbType.NVarChar, 50).Value = SalesName
cmd.Parameters.Add("#MaterialID", SqlDbtype.Int).Value = MaterialID
cn.Open()
cmd.ExecuteNonQuery()
End Using
End Sub
Call it like with code similar to this:
PopulateOrder(CBChannel.SelectedValue, CBName.SelectedValue)
But for what it's worth, it's almost always a bad idea to duplicate this information across tables.