ExecuteNonQuery() for Insert - vb.net

Can you please tell me what's wrong with this code?
Do I need to use DataAdapter to insert into a table?
I know the connectionString is ok, because I tested it on the Server Explorer.
Dim mydao As New Connection
Dim connectionString As String = mydao.GetConnectionString()
Dim connection As New SqlConnection(connectionString)
Dim cmd As New SqlCommand
Public Function add(ByVal area As String, ByVal user As String) As Integer
cmd.CommandText = "INSERT into Area (Area, user) VALUES ('" + area + "','" + user + "')"
Try
connection.Open()
Dim cant As Integer = cmd.ExecuteNonQuery()'it throws exception here
connection.Close()
Return cant
Catch ex As Exception
Console.WriteLine(ex.Message)
Return 0
End Try
End Function
The above code fails just after ExecuteNonQuery() and canĀ“t figure why.
TARGET FIELDS (SQL Server 2008):
AREA varchar(100) NOT NULL ,
USER varchar(100) NOT NULL
The exception I receive is: Connection property has not initialized

There's a few issues with this code.
The most significant is that you aren't setting the Command's Connection property, so the command has no way of knowing how to connect to the database.
I would also strongly recommend utilizing using, and also parameterizing your query:
Finally, don't declare the connection and command outside of the function unless you need to. You should only keep the connection and command around for as long as you need them.
So your function would end up looking like:
Public Function add(ByVal area As String, ByVal user As String) As Integer
Dim mydao As New Connection
Using connection As New SqlConnection(mydao.ConnectionString())
Using command As New SqlCommand()
' Set the connection
command.Connection = connection
' Not necessary, but good practice
command.CommandType = CommandType.Text
' Example query using parameters
command.CommandText = "INSERT into Area (Area, user) VALUES (#area, #user)"
' Adding the parameters to the command
command.Parameters.AddWithValue("#area", area)
command.Parameters.AddWithValue("#user", user)
connection.Open()
Return command.ExecuteNonQuery()
End Using ' Dispose Command
End Using ' Dispose (and hence Close) Connection
End Function
Note that currently, you will be returning 0 all the time. Rather than having to check the value returned from the function, the above example will simply throw an exception. This makes for slightly cleaner code (as the caller would have to understand that 0 is an error condition), and, if you needed to handle the exception, simply wrap the call to this function in a Try-Catch block

Related

How to extract data from database access from a form and display on another form as receipt when button print is clicked in vb.net [duplicate]

I've heard that "everyone" is using parameterized SQL queries to protect against SQL injection attacks without having to vailidate every piece of user input.
How do you do this? Do you get this automatically when using stored procedures?
So my understanding this is non-parameterized:
cmdText = String.Format("SELECT foo FROM bar WHERE baz = '{0}'", fuz)
Would this be parameterized?
cmdText = String.Format("EXEC foo_from_baz '{0}'", fuz)
Or do I need to do somethng more extensive like this in order to protect myself from SQL injection?
With command
.Parameters.Count = 1
.Parameters.Item(0).ParameterName = "#baz"
.Parameters.Item(0).Value = fuz
End With
Are there other advantages to using parameterized queries besides the security considerations?
Update: This great article was linked in one of the questions references by Grotok.
http://www.sommarskog.se/dynamic_sql.html
The EXEC example in the question would NOT be parameterized. You need parameterized queries (prepared statements in some circles) to prevent input like this from causing damage:
';DROP TABLE bar;--
Try putting that in your fuz variable (or don't, if you value the bar table). More subtle and damaging queries are possible as well.
Here's an example of how you do parameters with Sql Server:
Public Function GetBarFooByBaz(ByVal Baz As String) As String
Dim sql As String = "SELECT foo FROM bar WHERE baz= #Baz"
Using cn As New SqlConnection("Your connection string here"), _
cmd As New SqlCommand(sql, cn)
cmd.Parameters.Add("#Baz", SqlDbType.VarChar, 50).Value = Baz
Return cmd.ExecuteScalar().ToString()
End Using
End Function
Stored procedures are sometimes credited with preventing SQL injection. However, most of the time you still have to call them using query parameters or they don't help. If you use stored procedures exclusively, then you can turn off permissions for SELECT, UPDATE, ALTER, CREATE, DELETE, etc (just about everything but EXEC) for the application user account and get some protection that way.
Definitely the last one, i.e.
Or do I need to do somethng more extensive ...? (Yes, cmd.Parameters.Add())
Parametrized queries have two main advantages:
Security: It is a good way to avoid SQL Injection vulnerabilities
Performance: If you regularly invoke the same query just with different parameters a parametrized query might allow the database to cache your queries which is a considerable source of performance gain.
Extra: You won't have to worry about date and time formatting issues in your database code. Similarly, if your code will ever run on machines with a non-English locale, you will not have problems with decimal points / decimal commas.
You want to go with your last example as this is the only one that is truly parametrized. Besides security concerns (which are much more prevalent then you might think) it is best to let ADO.NET handle the parametrization as you cannot be sure if the value you are passing in requires single quotes around it or not without inspecting the Type of each parameter.
[Edit] Here is an example:
SqlCommand command = new SqlCommand(
"select foo from bar where baz = #baz",
yourSqlConnection
);
SqlParameter parameter = new SqlParameter();
parameter.ParameterName = "#baz";
parameter.Value = "xyz";
command.Parameters.Add(parameter);
Most people would do this through a server side programming language library, like PHP's PDO or Perl DBI.
For instance, in PDO:
$dbh=pdo_connect(); //you need a connection function, returns a pdo db connection
$sql='insert into squip values(null,?,?)';
$statement=$dbh->prepare($sql);
$data=array('my user supplied data','more stuff');
$statement->execute($data);
if($statement->rowCount()==1){/*it worked*/}
This takes care of escaping your data for database insertion.
One advantage is that you can repeat an insert many times with one prepared statement, gaining a speed advantage.
For instance, in the above query I could prepare the statement once, and then loop over creating the data array from a bunch of data and repeat the ->execute as many times as needed.
Your command text need to be like:
cmdText = "SELECT foo FROM bar WHERE baz = ?"
cmdText = "EXEC foo_from_baz ?"
Then add parameter values. This way ensures that the value con only end up being used as a value, whereas with the other method if variable fuz is set to
"x'; delete from foo where 'a' = 'a"
can you see what might happen?
Here's a short class to start with SQL and you can build from there and add to the class.
MySQL
Public Class mysql
'Connection string for mysql
Public SQLSource As String = "Server=123.456.789.123;userid=someuser;password=somesecurepassword;database=somedefaultdatabase;"
'database connection classes
Private DBcon As New MySqlConnection
Private SQLcmd As MySqlCommand
Public DBDA As New MySqlDataAdapter
Public DBDT As New DataTable
Public BindSource As New BindingSource
' parameters
Public Params As New List(Of MySqlParameter)
' some stats
Public RecordCount As Integer
Public Exception As String
Function ExecScalar(SQLQuery As String) As Long
Dim theID As Long
DBcon.ConnectionString = SQLSource
Try
DBcon.Open()
SQLcmd = New MySqlCommand(SQLQuery, DBcon)
'loads params into the query
Params.ForEach(Sub(p) SQLcmd.Parameters.AddWithValue(p.ParameterName, p.Value))
'or like this is also good
'For Each p As MySqlParameter In Params
' SQLcmd.Parameters.AddWithValue(p.ParameterName, p.Value)
' Next
' clears params
Params.Clear()
'return the Id of the last insert or result of other query
theID = Convert.ToInt32(SQLcmd.ExecuteScalar())
DBcon.Close()
Catch ex As MySqlException
Exception = ex.Message
theID = -1
Finally
DBcon.Dispose()
End Try
ExecScalar = theID
End Function
Sub ExecQuery(SQLQuery As String)
DBcon.ConnectionString = SQLSource
Try
DBcon.Open()
SQLcmd = New MySqlCommand(SQLQuery, DBcon)
'loads params into the query
Params.ForEach(Sub(p) SQLcmd.Parameters.AddWithValue(p.ParameterName, p.Value))
'or like this is also good
'For Each p As MySqlParameter In Params
' SQLcmd.Parameters.AddWithValue(p.ParameterName, p.Value)
' Next
' clears params
Params.Clear()
DBDA.SelectCommand = SQLcmd
DBDA.Update(DBDT)
DBDA.Fill(DBDT)
BindSource.DataSource = DBDT ' DBDT will contain your database table with your records
DBcon.Close()
Catch ex As MySqlException
Exception = ex.Message
Finally
DBcon.Dispose()
End Try
End Sub
' add parameters to the list
Public Sub AddParam(Name As String, Value As Object)
Dim NewParam As New MySqlParameter(Name, Value)
Params.Add(NewParam)
End Sub
End Class
MS SQL/Express
Public Class MSSQLDB
' CREATE YOUR DB CONNECTION
'Change the datasource
Public SQLSource As String = "Data Source=someserver\sqlexpress;Integrated Security=True"
Private DBCon As New SqlConnection(SQLSource)
' PREPARE DB COMMAND
Private DBCmd As SqlCommand
' DB DATA
Public DBDA As SqlDataAdapter
Public DBDT As DataTable
' QUERY PARAMETERS
Public Params As New List(Of SqlParameter)
' QUERY STATISTICS
Public RecordCount As Integer
Public Exception As String
Public Sub ExecQuery(Query As String, Optional ByVal RunScalar As Boolean = False, Optional ByRef NewID As Long = -1)
' RESET QUERY STATS
RecordCount = 0
Exception = ""
Dim RunScalar As Boolean = False
Try
' OPEN A CONNECTION
DBCon.Open()
' CREATE DB COMMAND
DBCmd = New SqlCommand(Query, DBCon)
' LOAD PARAMS INTO DB COMMAND
Params.ForEach(Sub(p) DBCmd.Parameters.Add(p))
' CLEAR PARAMS LIST
Params.Clear()
' EXECUTE COMMAND & FILL DATATABLE
If RunScalar = True Then
NewID = DBCmd.ExecuteScalar()
End If
DBDT = New DataTable
DBDA = New SqlDataAdapter(DBCmd)
RecordCount = DBDA.Fill(DBDT)
Catch ex As Exception
Exception = ex.Message
End Try
' CLOSE YOUR CONNECTION
If DBCon.State = ConnectionState.Open Then DBCon.Close()
End Sub
' INCLUDE QUERY & COMMAND PARAMETERS
Public Sub AddParam(Name As String, Value As Object)
Dim NewParam As New SqlParameter(Name, Value)
Params.Add(NewParam)
End Sub
End Class

Does a pool of connections really improve the overall perfomance?

I have this code in Visual Basic, every time I have a new insert:
Private _conn As SqlConnection
Public Function Include(ByVal pSql As String, Optional timeout As Integer = 120) As Boolean
Try
Dim SQL_Str = "my string of conection... with database. not put on this example"
_conn = New SqlConnection(SQL_Str)
_conn.Open()
_adapter = New SqlDataAdapter
cmd.CommandTimeout = timeout
cmd.Connection = _conn
cmd.CommandText = pSql
cmd.CommandType = CommandType.Text
_adapter.InsertCommand = cmd
_adapter.InsertCommand.ExecuteNonQuery()
Catch ex As Exception
InputBox("New Error on Sql cmd: ", , pSql)
End Try
_conn.Close()
_conn.Dispose()
_conn = Nothing
_adapter.Dispose()
_adapter = Nothing
End Function
Ok this is a straightforward way to update the database.
But supose I have 1000 connections at the same time, do the application would support this kind of approach?
Do this method support simultaneous threads acessing the _conn object?
Is it really necessary to create a pool of connections to handle data?
Do a pool of connections will really improve something?
E.g. with this I'm overloading the application instead of the database?
If so, how would I do it on VbNet/Visual Basic?
Yes, pooled connections really are faster. They keep you from needing to continually re-negotiate login and protocol information. Even better, this is already built into the SqlConnection type, and it's done in a reasonably thread-safe way (where the existing code is not).
The short version is you really do want to create a brand new connection object for most queries, and you do not want to try to share the same connection variable throughout an application or session.
Given that, I see several big problems in that code:
Treating a class-level _conn variable as if it were local, making it impossible to share instances of this class safely across threads.
No mechanism to clean up the connection if an exception is thrown (needs a Finally or Using block. Just closing after the Catch isn't good enough.
No way to pass separate query parameters in the function signature. This will force you to write horribly insecure code elsewhere that's crazy-vulnerable to sql injection attacks. It's the kind of thing where you wake up one morning to find out you were hacked over a year ago, and IMO borders on professional malpractice.
Mixing UI code with utility code.
You want something more like this:
Private cnString As String = "my string of conection... with database. not put on this example"
Public Sub Include(pSql As String, parameters() As SqlParamter, Optional timeout As Integer = 120)
Using conn As New SqlConnectioN(cnString), _
cmd As New SqlCommand(pSql, conn)
If parameters IsNot Nothing AndAlso parameters.Length > 0 Then
cmd.Parameters.AddRange(parameters)
End If
conn.Open()
cmd.ExecuteNonQuery()
End Using
End Sub
And you might call it like this (assuming type or instance name is DB):
Dim pSql As String = "INSERT INTO [ExampleTable] (FirstName, LastName, CreationDate) VALUES (#FirstName, #LastName, #CreationDate)"
Dim parameters = {
New SqlParameter("#FirstName", SqlDbType.NVarChar, 20),
New SqlParameter("#LastName", SqlDbType.NVarChar, 20),
New SqlParameter("#CreationDate", SqlDbType.DateTime)
}
parameters(0).Value = "John"
parameters(1).Value = "Smith"
parameters(2).Value = DateTime.Now
Try
DB.Include(pSql, parameters)
Catch ex As Exception
MessageBox.Show(String.Format("New Error on Sql cmd:{0}{1}{0}{0}Message:{2}",vbCrLf, pSql, ex.Message)
End Try

Insert Into Syntax error vb.net

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)

DataAdapter is disposed before reaching "End Using"

I know that I should always dispose DataAdapter instances. In most cases I'm disposing it immediately after closing the connection, but in cases like when user will be modifying DataTable items (displayed in ListBox or DataGridView) I create the DataAdapter, use it to fill the DataTable, but don't dispose it until the user clickes Save which calls DataAdapter.Update(DataTable)... not my main question but is this the right approach?
Back to the main question, I have these two functions:
Public Function LoadCompaniesDT(ByRef dtCompanies As DataTable) As Boolean
Using daCompanies As MySqlDataAdapter = Nothing
Return LoadCompaniesDT(daCompanies, dtCompanies)
End Using
End Function
Public Function LoadCompaniesDT(ByRef daCompanies As MySqlDataAdapter, ByRef dtCompanies As DataTable) As Boolean
Dim sql As String = "SELECT * FROM companies"
Return LoadDT(daCompanies, dtCompanies, sql, Res.CompaniesFailedMsgBody)
End Function
They're used to call LoadDT which fills the DataTable so I have the choice to pass a DataAdapter or not.
Now I'm confused about something: When using the first LoadCompaniesDT function, daCompanies is disposed before reaching End Using.. like this:
Public Function LoadCompaniesDT(ByRef dtCompanies As DataTable) As Boolean
Using daCompanies As MySqlDataAdapter = Nothing
Dim tmp As Boolean = LoadCompaniesDT(daCompanies, dtCompanies)
Console.WriteLine(daCompanies Is Nothing) ' ==> True!!
Return tmp
End Using
End Function
Note: if I use Dim daCompanies instead of Using daCompanies then daCompanies Is Nothing will return False.
LoadDT function code:
Private Function LoadDT(ByRef da As MySqlDataAdapter, ByRef dt As DataTable,
ByVal sqlQuery As String,
ByVal errorText As String) As Boolean
Dim connStr As String = String.Format("server={0}; port={1}; user id={2}; password={3}; database={4}",
DbServer, DbServerPort, DbUserName, DbPassword, DatabaseName)
Dim conn As MySqlConnection = New MySqlConnection(connStr)
Dim cmd As MySqlCommand = New MySqlCommand
Try
conn.Open()
cmd.CommandType = CommandType.Text
cmd.CommandText = sqlQuery
cmd.Connection = conn
da = New MySqlDataAdapter(cmd)
dt = New DataTable
da.Fill(dt)
Return True
Catch ex As Exception
MessageBox.Show(errorText, Res.ServerError, MessageBoxButtons.OK, MessageBoxIcon.Error)
Return False
Finally
cmd.Dispose()
cmd = Nothing
conn.Close()
conn.Dispose()
End Try
End Function
Update: you're right, you don't get an initialized MySqlDataAdapter back from the methods if the ByRef passed instance is used in a Using-statement. Those variables are readonly. In C# you get this meaningful compiler error:
Error CS1657 Cannot pass 'daCompanies' as a ref or out argument
because it is a 'using variable'
It's documented here:
Compiler Error CS1657
Cannot pass 'parameter' as a ref or out argument because 'reason''
This error occurs when a variable is passed as a ref or out argument
in a context in which that variable is readonly. Readonly contexts
include foreach iteration variables, using variables, and fixed
variables.
In VB.NET you can do that(so the compiler ignores it which is almost a bug) but the variable is not initialized afterwards. But as mentioned below, you should not use this approach anyway.
According to the the other question:
If you look at the sample on MSDN you'll see that microsoft also doesn't dispose the dataadapter. So it not really necessary. Having said that, it's always best practise to use the Using statement on anything that implements IDisposable.
A DataAdapter is not an expensive object and it does not hold unmanaged resources(like the connection). So it doesn't hurt to create a new instance from it whereever you need one. And you don't need to dispose it, but that's an implementation detail that might change in future or in a different implementation of DbDataAdapter, so it's still best practise to dispose it, best by using the Using-statement.
I wouldn't use your approach because you are passing the sql-string to the method which often leads to a sql injection vulnerabiliy. Instead use sql parameters.
For example:
Private Function LoadDT() As DataTable
Dim tbl As New DataTable()
'Load connection string from app.config or web.config
Dim sql As String = "SELECT * FROM companies" ' don't use * but list all columns explicitely
Using conn As New MySqlConnection(My.Settings.MySqlConnection)
Using da = New MySqlDataAdapter(sql, conn)
da.Fill(tbl)
End Using
End Using
Return tbl
End Function
Instead of passing an errorText ByRef i'd use a logging framework like log4net.

Custom TableAdapter Delete Method Override

I'm attempting to overload the "Delete" method of a TableAdapter (approach). How can I execute an SQL statement from 'here' to handle the delete?
I've got:
Namespace AFL_BackendDataSetTableAdapters
Partial Class Log_entry_unitTableAdapter
Public Overloads Function Delete(ByVal LogEntryID As Integer) As Integer
Dim SQL As String
SQL = "DELETE FROM log_entry_unit WHERE log_entry_unit_id=" & LogEntryID
'?????.Execute SQL
Return 0
End Function
End Class
End Namespace
The overload is working fine, but I don't know how to do the hard part and actually manipulate the data from here. Previously, I've just gone into the Dataset Designer and manually updated the generated methods to work like I want them, but whenever I use the wizard to regenerate the dataset, that (as expected) gets overwritten.
I've previously only ever manipulated Data using the generated methods, and now I'm stuck.
EDIT w/ Final Answer
Based on William's help below here's the final working solution (Note I just had to use OleDb instead of SQL since my Dataset is Access:
Imports System.Data.OleDb
Namespace AFL_BackendDataSetTableAdapters
Partial Class Log_entry_unitTableAdapter
Public Overloads Function Delete(ByVal LogEntryID As Integer) As Integer
Dim queryString As String = "DELETE FROM log_entry_unit WHERE log_entry_unit_id=" & LogEntryID
Dim command As New OleDbCommand(queryString, Connection)
Dim r As Integer
Try
Connection.Open()
r = command.ExecuteNonQuery()
Catch ex As Exception
MsgBox(ex.Message)
r = 0
Finally
Connection.Close()
End Try
Return r
End Function
End Class
End Namespace
I hardcoded a connection string for reference only. This should be in a config file. As an example:
Dim connectionString As String = _
"Data Source=(local);Initial Catalog=YourDatabase;" _
& "Integrated Security=true"
Dim queryString As String = "DELETE FROM log_entry_unit WHERE log_entry_unit_id=" & LogEntryID
' Create and open the connection in a using block. This
' ensures that all resources will be closed and disposed
' when the code exits.
Using connection As New SqlConnection(connectionString)
' Create the Command
Dim command As New SqlCommand(queryString, connection)
' Open the connection in a try/catch block.
Try
connection.Open()
command.ExecuteNonQuery()
Catch ex As Exception
' handle exception here
End Try
End Using
EDIT
I probably should of mentioned you will probably want to fill your adapter again after the delete.