-2

I am trying to take the vales (comma separated) from a text box and pass them to a query string in an ExecuteSQL statement. I am not sure how to do this correctly. The obvious issue is that it will break if there are more or less than the three exceptions. I need to use an array or list, but don't know how to read that back into the query string?

This is what I have so far:

Private Sub Button5_Click(sender As System.Object, e As System.EventArgs) Handles Button5.Click
        Dim db As New Database
        If txtEmpExceptions.Text.ToString() IsNot Nothing Then
            Dim EmpExcept1 As String = txtEmpExceptions.Text.ToString().Split(",")(0)
            Dim EmpExcept2 As String = txtEmpExceptions.Text.ToString().Split(",")(1)
            Dim EmpExcept3 As String = txtEmpExceptions.Text.ToString().Split(",")(2)
            db.ExecuteSQL("Delete dbo.Employee where employeeID <> '" & EmpExcept1 & "' and employeeID <> '" & EmpExcept2 & "' and employeeID <> " & EmpExcept3, prodString)
        Else
            db.ExecuteSQL("Delete dbo.Employee", prodString)
        End If
    End Sub
Kevin Schultz
  • 886
  • 3
  • 20
  • 42
  • 4
    Just stop a second and read this [Sql Injection Explained](http://stackoverflow.com/questions/332365/how-does-the-sql-injection-from-the-bobby-tables-xkcd-comic-work) – Steve Jul 09 '15 at 21:31
  • @Steve - This is a small desktop executable that is used by a few employees to populate a table in the DB. But thanks, SQL injection is a large concern. – Kevin Schultz Jul 09 '15 at 21:33
  • 2
    Most hacks are inside jobs. You still need injection protection on internal systems, even desktop apps. – Joel Coehoorn Jul 09 '15 at 21:38
  • 2
    Also, splitting on a comma is an _awful_ way to handle CSV data. In VB.Net, take a look at the TextFieldParser class. – Joel Coehoorn Jul 09 '15 at 21:39
  • @JoelCoehoorn I think that's a little overkill for a single line of CSV. – Crowcoder Jul 09 '15 at 21:39
  • @Crowcoder Only if you can trust the CSV data. If you're letting humans enter arbitrary text, you're better off using a dedicated parser. – Joel Coehoorn Jul 09 '15 at 21:42
  • You know that CSV can contain commas in data, right? And that it can use characters other than commas as delimiters? And that there are [many](http://stackoverflow.com/questions/9642055/), [many](http://www.codeproject.com/Tips/823670/Csharp-Light-and-Fast-CSV-Parser) .NET CSV parsers that can handle this correctly? – Dour High Arch Jul 09 '15 at 21:42
  • The ONLY way that this is even remotely close to safe, is if you are the only user of the app...and even then, it's pretty bad practice. As others have stated, look into using something cleaner than this. Parameterize your queries, and please PLEASE name your buttons something other than the default names. Also your SQL is missing a from clause. – user2366842 Jul 09 '15 at 21:49

3 Answers3

2

To check if you have all your inputs you could split before and then test the count of elements in the array produced by string.Split

Private Sub Button5_Click(sender As System.Object, e As System.EventArgs) Handles Button5.Click
    Dim db As New Database
    Dim parts = txtEmpExceptions.Text.Split(",")
    If parts.Length <> 3 Then
       MessageBox.Show("Text must be splitted in 3 substrings")
       return
    End If
    Dim EmpExcept1 As String = parts(0)
    Dim EmpExcept2 As String = parts(1)
    Dim EmpExcept3 As String = parts(2)       
    ....

Note that a TextBox.Text is never Nothing. You could remove that check.
(And not strictly related to your problem, but as I have said in comment, you really should try to use a different approach to your database query to avoid string concatenation at all costs)

Joel Coehoorn
  • 399,467
  • 113
  • 570
  • 794
Steve
  • 213,761
  • 22
  • 232
  • 286
0

This should provide a good balance between your experience level and preventing SQL Injection:

Dim empIds As String = "bob, sally, fred" 'This would come from your textbox
Dim emps() As String = empIds.Split(",")

Dim qry As String = "DELETE FROM dbo.Employee WHERE "
Dim whereClause As String = " employeeID <> @param{0} AND "

For i As Integer = 0 To emps.Length - 1
    'Add "employeeID <> .. for as many ids as were parsed
    qry = String.Concat(qry, String.Format(whereClause, i))
    Dim paramName As String = String.Concat("@param", i)
    Dim par As new SqlParameter(paramName, SqlDbType.VarChar, 50) 'Assumes your employee ids are text type and max 50 length - adjust accordingly
    par.Value = emps(i)
    'Now add this parameter to your SqlCommand, I don't know how your db library does that

Next i

'Get rid of the last "AND"
qry = qry.Remove(qry.Length - 4)

'Now set the CommandText of  your SqlCommand to qry
'Then execute the command
Crowcoder
  • 11,250
  • 3
  • 36
  • 45
0

In sql, you can create a table type - here is an example from one of my databases. Modify as needed.

CREATE TYPE [dbo].[AddSample_Type] AS TABLE(
[JobID] [int] NOT NULL,
[SampleDescription] [nvarchar](1000) NOT NULL,
[Notes] [nvarchar](1000) NULL,
[SampleName] [nvarchar](200) NULL,
[StorageLocationID] [int] NULL,
[StorageDispositionID] [int] NULL,
[StorageNotes] [nvarchar](1000) NULL,
[EmployeeID] [int] NULL,
[CheckedOutToEmployee] [bit] NULL
)
GO

You can create a procedure which accepts this table as input.It's much easier and more efficient to work with rows and tables in sql, after all.

 CREATE PROCEDURE [dbo].[AddMultipleSamplesToJob]
@SamplesToAdd AddSample_Type Readonly
AS
BEGIN
select JobID,SampleDescription,Notes,SampleName,StorageLocationID,
StorageDispositionID,StorageNotes,EmployeeID,CheckedOutToEmployee, 0 as     SampleNumber
 from @SamplesToAdd
END

You can now send a table full of rows to your stored procedure from VB.

 Dim addSamplesTable As New EPI_JobBookDataSet.AddMultipleSamplesToJobDataTable
    Dim AddSamplesAdapter As New EPI_JobBookDataSetTableAdapters.AddMultipleSamplesToJobTableAdapter
'Datatable to be sent in the procedure
Dim TablesamplesToAdd As New EPI_JobBookDataSet.TableType_AddSampleTable
'Make a new row and load it with data
 Dim newSampleRow As EPI_JobBookDataSet.TableType_AddSampleRow =     TablesamplesToAdd.Rows.Add()
            newSampleRow.CheckedOutToEmployee = StorageActivityEntryTool1.WithEmployee
            newSampleRow.EmployeeID = StorageActivityEntryTool1.EmployeeID
            newSampleRow.JobID = JobID
            newSampleRow.Notes = r.Cells(ColumnNotes.Index).Value
            newSampleRow.SampleDescription = r.Cells(ColumnDescription.Index).Value
            newSampleRow.SampleName = r.Cells(ColumnSampleName.Index).Value
            newSampleRow.StorageDispositionID = StorageActivityEntryTool1.DispositionID
            newSampleRow.StorageLocationID = StorageActivityEntryTool1.LocationID
            newSampleRow.StorageNotes = "Some notes"
'Add more rows if desired
'
'Send the query
AddSamplesAdapter.Fill(addSamplesTable, TablesamplesToAdd)

I hope you find this useful. Please ignore the reference inside of my row as I copied it from my code and was too lazy to change those particular values for this example.

ThatGuy
  • 228
  • 1
  • 12
  • Personally, when I give my users the option to input multiple string values, they are provided with a datagridview which helps keep the information organized from their prospective. I then move the data into one ofe these type tables, row for row and push it to the procedure. – ThatGuy Jul 10 '15 at 02:04