how can i use parameters to avoid sql attacks - sql

I have a project without any parameters used in SQL queries.
Is there any solution so that i don't have to change the function and validate parameters from the Query string itself?
Query = "select * from tbl_Users where userName='"& textbox1.text &"' and password='"& textbox2.text &"' "
ds = obj.ExecuteQueryReturnDS(Query)
Function where query is passed:
Public Function ExecuteQueryReturnDS(ByVal stQuery As String) As DataSet
Try
Dim ds As New DataSet
Using sqlCon As New SqlConnection(connStr)
Dim sqlCmd As New SqlCommand(stQuery, sqlCon)
Dim sqlAda As New SqlDataAdapter(sqlCmd)
sqlCmd.CommandType = CommandType.Text
sqlAda.Fill(ds)
End Using
Return ds
Catch ex As Exception
End Try
End Function
I tried passing parameters into the function but the function is used in for other queries as well hence i cannot define the parameters inside the function .
Is there any work around

I think the only solution is create a new function and gradually migrate to it.
Public Function ExecuteQueryReturnDS(ByVal cmdQuery As SqlCommand) As DataSet
Try
Dim ds As New DataSet
Using sqlCon As New SqlConnection(connStr)
cmdQuery.Connection = sqlCon
Dim sqlAda As New SqlDataAdapter(cmdQuery)
sqlAda.Fill(ds)
End Using
Return ds
Catch ex As Exception
End Try
End Function
cmdQuery is intended to be an SqlCommand to which you already added all the parameters you need.

As an intermediate step before switching to a full parameterized application you could change your actual method to be able to receive an optional argument.
This optional argument will be your SqlParameter array defined in the point where you call this query
Public Function ExecuteQueryReturnDS(ByVal stQuery As String, Optional ByVal prms As SqlParameter() = Nothing) As DataSet
Try
Dim ds As New DataSet
Using sqlCon As New SqlConnection(connStr)
Dim sqlCmd As New SqlCommand(stQuery, sqlCon)
if Not prms Is Nothing Then
sqlCmd.Parameters.AddRange(prms)
End if
Dim sqlAda As New SqlDataAdapter(sqlCmd)
sqlCmd.CommandType = CommandType.Text
sqlAda.Fill(ds)
End Using
Return ds
Catch ex As Exception
End Try
End Function

Related

VB.NET How to correctly loop through a result set

I have looked at many different code snippets on this site looking that would show me how to do something that should be fairly simple once I have the knowledge.
I want to query a database table for an array of values and then populate a combobox with those results.
Here is what I have so far:
Public Sub getMachines()
Try
Dim SQL As String = "SELECT MachineName from machine"
Form1.machineName.DisplayMember = "Text"
Dim tb As New DataTable
tb.Columns.Add("Text", GetType(String))
Using cn As New MySqlConnection(ConnectionString)
Using cmd As New MySqlCommand(SQL, cn)
For Each cmd As String In cmd
'I want to add each value found in the database to "tb.Rows.Add"
'tb.Rows.Add(???)
Next
Form1.machineName.DataSource = tb
cn.Open()
cmd.ExecuteNonQuery()
End Using
cn.Close()
End Using
Catch ex As MySqlException
MsgBox(ex.Message)
End Try
End Sub
I proceeded much like you did. I used the Load method of the DataTable. It is not necessary to set the column name and type. The name of the column is taken from the Select statement and the datatype is inferred by ADO.net from the first few records.
Luckily a DataTable can be an Enumerable using the .AsEnumnerable method. Then we can use Linq to get all the values from the MachineName column. Calling .ToArray causes the Linq to execute. If you hold your cursor over names on this line you will see that the datatype is String(). Just what we need to fill a combo box.
Code for a class called DataAccess
Private ConnectionString As String = "Your Connection String"
Public Function GetMachineNames() As String()
Dim tb As New DataTable
Dim SQL As String = "SELECT MachineName from machine;"
Using cn As New MySqlConnection(ConnectionString)
Using cmd As New MySqlCommand(SQL, cn)
cn.Open()
dt.Load(cmd.ExecuteReader)
End Using
End Using
Dim names = dt.AsEnumerable().Select(Function(x) x.Field(Of String)("MachineName")).ToArray()
Return names
End Function
In the form load you combo box like this.
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim DatAcc As New DataAccess()
Dim arr = DatAcc.GetMachineNames()
machineName.DataSource = arr
End Sub
If you just want the MachineName to be displayed in the ComboBox, then just use that as the DisplayMember; don't bother creating another column called Text.
Public Sub getMachines()
Try
Dim cmd As String = "SELECT MachineName from machine"
Dim ds As New DataSet()
Using con As New MySqlConnection(ConnectionString)
Using da As New MySqlDataAdapter(cmd, con)
da.Fill(ds)
With Form1.machineName
.DisplayMember = "MachineName"
.ValueMember = "MachineName"
.DataSource = ds
End With
End Using
End Using
Catch ex As MySqlException
MsgBox(ex.Message)
End Try
End Sub
I'll show a few examples, including using parameters, since that is important.
First up, a quick translation to run the existing query and loop through the results:
Public Sub getMachines()
Try
Dim SQL As String = "SELECT MachineName from machine"
Using cn As New MySqlConnection(ConnectionString), _
cmd As New MySqlCommand(SQL, cn)
cn.Open()
Using rdr As MySqlDatareader = cmd.ExecuteReader
While rdr.Read()
Form1.machineName.Items.Add(rdr("MachineName"))
End While
End Using
End Using
Catch ex As MySqlException
MsgBox(ex.Message)
End Try
End Sub
But better practice for a method like this is to isolate data access for the UI. This method should return results to the caller, which can decide what do with them. So I'll show two methods: one to get the data, and the other to loop through it and set up the combobox:
Private Function GetMachines() As DataTable
'No try/catch needed here. Handle it in the UI level, instead
Dim SQL As String = "SELECT MachineName from machine"
Dim result As New DataTable
Using cn As New MySqlConnection(ConnectionString), _
cmd As New MySqlCommand(SQL, cn),
da As New MySqlDataAdapter(cmd)
da.Fill(result)
End Using
Return result
End Function
Public Sub LoadMachines()
Try
For Each item As DataRow in getMachines().Rows
Form1.machineName.Items.Add(item("MachineName"))
Next
Catch ex As MySqlException
MsgBox(ex.Message)
End Try
End Sub
Or, we can use DataBinding:
Private Function GetMachines() As DataTable
Dim SQL As String = "SELECT MachineName from machine"
Dim result As New DataTable
Using cn As New MySqlConnection(ConnectionString), _
cmd As New MySqlCommand(SQL, cn),
da As New MySqlDataAdapter(cmd)
da.Fill(result)
End Using
Return result
End Function
Public Sub LoadMachines()
Try
Form1.machineName.DisplayMember = "FirstName";
Form1.machineName.ValueMember = "City"
Form1.machineName.DataSource = GetMachines()
Catch ex As MySqlException
MsgBox(ex.Message)
End Try
End Sub
If you ever want to use a filter, you might do this (notice the overloading):
Private Function GetMachines(ByVal machineFilter As String) As DataTable
Dim SQL As String = "SELECT MachineName from machine WHERE MachineName LIKE #Filter"
Dim result As New DataTable
Using cn As New MySqlConnection(ConnectionString), _
cmd As New MySqlCommand(SQL, cn),
da As New MySqlDataAdapter(cmd)
'Match the MySqlDbType to your actual database column type and length
cmd.Parameters.Add("#Filter", MySqlDbType.VarString, 30).Value = machineFilter
da.Fill(result)
End Using
Return result
End Function
Private Function GetMachines(ByVal machineFilter As String) As DataTable
Return GetMachines("%")
End Function
Query parameters like that are very important, and if you were doing string concatenation to accomplish this kind of thing on your old platform, you were doing very bad things there, too.
Finally, let's get fancy. A lot of the time, you really don't want to load an entire result set into RAM, as is done with a DataTable. That can be bad. Instead, you'd like be able to stream results into memory and only work with one at a time, minimizing RAM use. In these cases, you get to play with a DataReader... but returning a DataReader object from within a Using block (which is important) doesn't work that well. To get around this, we can use functional programming concepts and advanced language features:
Private Iterator Function GetMachines(ByVal machineFilter As String) As IEnumerable(Of String)
Dim SQL As String = "SELECT MachineName from machine WHERE MachineName LIKE #Filter"
Using cn As New MySqlConnection(ConnectionString), _
cmd As New MySqlCommand(SQL, cn)
'Match the MySqlDbType to your actual database column type and length
cmd.Parameters.Add("#Filter", MySqlDbType.VarString, 30).Value = machineFilter
cn.Open()
Using rdr As MySqlDatareader = cmd.ExecuteReader
While rdr.Read()
Dim result As String = rdr("MachineName")
Yield Return result
End While
End Using
End Using
Return result
End Function
Private Function GetMachines() As IEnumerable(Of String)
Return GetMachines("%")
End Function

Oledb Connection Hangs VB.Net

I am using an OledbConnection to an AS400 computer. When I have a SQL statement that will return nothing, it just hangs on the adapter command Fill.
Function ExecuteOLEDBQuery(ByVal cmdtext As String) As DataTable
Try
Dim connString As String = "Provider=IBMDA400;Persist Security Info=True;User ID=##USERID;Password=##PASSWORD;Data Source=##SYSTEM"
Dim as400 As New OleDb.OleDbConnection(connString)
Dim cmd As New OleDb.OleDbCommand(cmdtext, as400)
Dim adapter As New OleDb.OleDbDataAdapter(cmd)
cmd.CommandTimeout = 60 'Doesn't work. It never times out.
Dim dt As New DataTable
as400.Open()
adapter.Fill(dt) 'This is where it hangs
as400.Close()
adapter.Dispose()
cmd.Dispose()
Return dt
Catch ex As Exception
Return Nothing
End Try
End Function
Any ideas?
It may be the connection to the AS400 itself. Try this version which disposes of the object in a slightly different order:
Function ExecuteOLEDBQuery(cmdtext As String) As DataTable
Using cn = New OleDbConnection("Provider=IBMDA400;Persist Security Info=True;User ID=##USERID;Password=##PASSWORD;Data Source=##SYSTEM")
cn.Open()
Using da = New OleDbDataAdapter(cmdtext, cn)
Dim dt = New DataTable
da.Fill(dt)
Return dt
End Using
End Using
End Function

Retrieving data from Access 2010 view with VB.NET 2008 throws error

I'm trying to learn VB.NET and I'm looking to retrieve data from an Access 2010 view, using Vb.NET 2008, problem is I'm getting the following error. System.ArgumentNullException was unhandled Message=Value cannot be null
I cannot work out how to solve the error, so hopefully someone with more experience can help.
Public Function GetMyoleDataAdapterStudentQuestionRepeat(ByRef mydataSet As DataSet, ByVal topicId As String, ByVal groupId As String) As OleDbDataAdapter
Try
Dim strAccessConn As String = _appConfigDbConn
Dim cn As OleDbConnection = New OleDbConnection(strAccessConn)
Dim ds As New DataSet
Dim da As New OleDbDataAdapter("qryStudentNameRandomBasedOnScore", cn)
da.SelectCommand.CommandType = CommandType.StoredProcedure
da.SelectCommand.Parameters.AddWithValue("#GroupID", groupId)
da.Fill(ds, "Student")
Return da
Catch ex As Exception
Throw New ApplicationException(ex.InnerException.Message.ToString())
End Try
End Function
Within button click
Dim myoleDataAdapter As OleDbDataAdapter = GroupData.GetMyoleDataAdapterStudentQuestionRepeat(mydataSet, topicId, groupId)
myoleDataAdapter.Fill(mydataSet)
txtStudentName.DataBindings.Add("Text", mydataSet.Tables(0), "studentname")
This line throws error: myoleDataAdapter.Fill(mydataSet)
And in case it helps, my view is
SELECT TOP 1 tblStudentNameAndScore.studentname
FROM tblStudentNameAndScore
WHERE (((tblStudentNameAndScore.[QuizCount]) Between 2 And 10)) AND tblStudentNameAndScore.GroupID = ?
ORDER BY Rnd(QuizCount);
Thanks for any hep
In this line
myoleDataAdapter.Fill(mydataSet)
you try to fill the dataset but this dataset is never initialized.
Following your actual pattern, you should change your code that initialize the dataadapter in this way
Public Function GetMyoleDataAdapterStudentQuestionRepeat(ByVal topicId As String, ByVal groupId As String) As OleDbDataAdapter
Try
Dim strAccessConn As String = _appConfigDbConn
Dim cn As OleDbConnection = New OleDbConnection(strAccessConn)
Dim ds As New DataSet
Dim da As New OleDbDataAdapter("qryStudentNameRandomBasedOnScore", cn)
da.SelectCommand.CommandType = CommandType.StoredProcedure
da.SelectCommand.Parameters.AddWithValue("#GroupID", groupId)
Return da
Catch ex As Exception
Throw New ApplicationException(ex.InnerException.Message.ToString())
End Try
End Function
and then in the caller code write
Dim myoleDataAdapter As OleDbDataAdapter = GroupData.GetMyoleDataAdapterStudentQuestionRepeat( topicId, groupId)
mydataSet = new DataSet()
myoleDataAdapter.Fill(mydataSet)
....
In the method that returns the adapter you don't need to pass the dataset because you don't use it in any way and there is no need to create and fill another dataset. Just return the adapter and work on the caller code
Of course you could also change the method and return a dataset filled with your data instead of the DataAdapter. Probably this solution could be considered more encapsulated
Public Function GetStudentQuestionRepeat(ByVal topicId As String, ByVal groupId As String) _
As Dataset
....
Dim da As New OleDbDataAdapter("qryStudentNameRandomBasedOnScore", cn)
da.SelectCommand.CommandType = CommandType.StoredProcedure
da.SelectCommand.Parameters.AddWithValue("#GroupID", groupId)
Dim ds = new Dataset()
da.Fill(ds, "Student")
Return ds
....
End Function
By the way, what is the purpose of topidID variable passed and never used?
Why not return a Dataset instead of DataAdapter, like:
Public Function GetMyoleDataAdapterStudentQuestionRepeat(ByVal topicId As String, ByVal groupId As String) As DataSet
Try
Dim strAccessConn As String = _appConfigDbConn
Dim cn As OleDbConnection = New OleDbConnection(strAccessConn)
Dim ds As New DataSet
Dim da As New OleDbDataAdapter("qryStudentNameRandomBasedOnScore", cn)
da.SelectCommand.CommandType = CommandType.StoredProcedure
da.SelectCommand.Parameters.AddWithValue("#GroupID", groupId)
da.Fill(ds, "Student")
Return ds
Catch ex As Exception
Throw New ApplicationException(ex.InnerException.Message.ToString())
End Try
End Function
And then when you call it would be shorter:
Dim myDataSet As DataSet = GroupData.GetMyoleDataAdapterStudentQuestionRepeat(topicId, groupId)
txtStudentName.DataBindings.Add("Text", myDataSet.Tables(0), "studentname")

Why can't I return a Dataset from a function inside a VB.net class?

I am using WinForms and trying to retrieve all the data from the database once on form Load so I won't have to go back and forth to the database a lot.
I created a class and wrote a function that returns a dataset but I'm not being able to refer to it from my forms.
This is the code:
Private Function FillKeywords() As DataSet
Dim ds1 As DataSet : Dim cmd as SqlCommand
Dim da As SqlDataAdapter
Try
cmd = New SqlCommand("Dbo.selectkeywords", cn)
cmd.CommandType = CommandType.StoredProcedure
da = New SqlDataAdapter(cmd)
ds1 = New DataSet
da.Fill(ds1, "Keywords")
Return ds1
Catch ex As SqlClient.SqlException
WriteExToFile(ex.ToString)
Catch ex As Exception
WriteExToFile(ex.ToString)
End Try
End Function
I'll take a wild guess and say you will need to lose the Private keyword if you want your function to be accessible from another class or module.

SQLXML Import/Export

I have a SQL DB from which I export data as XML using VB.Net code. The code is relatively simple, works quickly, and formats the XML beautifully. The code is:
Dim connetionString As String
Dim connection As SqlConnection
Dim adapter As SqlDataAdapter
Dim ds As New DataSet
Dim sql As String
connetionString = "**connectionstring**"
connection = New SqlConnection(connetionString)
sql = "select * from costdata"
Try
connection.Open()
adapter = New SqlDataAdapter(sql, connection)
adapter.Fill(ds)
connection.Close()
ds.WriteXml("**PATH**")
MsgBox("Done")
Catch ex As Exception
MsgBox(ex.ToString)
End Try
The problem I'm having is loading this data back in. It seems like it should be as easy as the above, but I can't seem to get a simple way to do it.
It's my understanding that I can use an XMLReader coupled with ADO.NET, but in that case I need to define the columns for the DataTable to insert the XML Data into before I import it all into the DB.
Is there any way to keep from having to hard-code column values in the DataTable, and have the exported XML data import in similar fashion to the above?
Though it's not automated by column name, I decided that hardcoding the mappings wasn't too big a hassle. I'm all ears for an automated way, however. My solution:
Dim connectionString As String = "Data Source=(localdb)\v11.0;Initial Catalog=localACETest;Integrated Security=True"
Try
Using sqlconn As New SqlConnection(connectionString)
Dim ds As New DataSet()
Dim sourcedata As New DataTable()
ds.ReadXml("C:\Users\coopere.COOPERE-PC\Desktop\Test.xml")
sourcedata = ds.Tables(0)
sqlconn.Open()
Using bulkcopy As New SqlBulkCopy(sqlconn)
bulkcopy.DestinationTableName = "ScheduleData"
bulkcopy.ColumnMappings.Add("Id", "Id")
bulkcopy.ColumnMappings.Add("Period", "Period")
...
bulkcopy.WriteToServer(sourcedata)
End Using
sqlconn.Close()
End Using
MsgBox("Done")
Catch ex As Exception
MsgBox(ex.ToString)
End Try
Here is a way to automate column mappings ... it assumes the table exists with the same structure in the target database. Cheers :-)
Public Shared Function BulkCopyXML( _
path_ As String, _
connection_string_ As String, _
messages_ As List(Of String), _
exceptions_ As List(Of Exception) _
) As Boolean
Dim result_ As Boolean = False
Try
Dim dataset_ As New DataSet()
dataset_.ReadXml(path_)
Dim datatable_ As DataTable = Nothing
Using connection_ As SqlClient.SqlConnection = New SqlClient.SqlConnection(connection_string_)
connection_.Open()
Using bulkCopy_ As SqlClient.SqlBulkCopy = New SqlClient.SqlBulkCopy(connection_)
For Each datatable_ In dataset_.Tables()
messages_.Add(datatable_.TableName)
bulkCopy_.DestinationTableName = datatable_.TableName
bulkCopy_.ColumnMappings.Clear()
For Each dataColumn_ As DataColumn In datatable_.Columns
bulkCopy_.ColumnMappings.Add(dataColumn_.ColumnName, dataColumn_.ColumnName)
Next
bulkCopy_.WriteToServer(datatable_)
Next
End Using
End Using
result_ = True
Catch exception_ As Exception
If exceptions_ Is Nothing Then
Throw exception_
Else
exceptions_.Add(exception_)
End If
Finally
End Try
Return result_
End Function