"No data exists for the row/column" when connecting to SQL database from VB.net - sql

I'm trying to create a program which has a datagridview, when the user clicks on a cell in the view, it then looks in a SQL database, grabs information from other fields in the same record, and automatically fills corresponding text boxes (done by manipulating the name of the field) in the form.
For some reason however, I'm getting an error message saying:
"InvalidOperationException was unhandled"
"No Data exists for the row / column"
Here is the code relevant to this part of the program:
Private Sub DataGridView1_CellMouseClick(sender As Object, e As DataGridViewCellMouseEventArgs) Handles dgvResults.CellMouseClick
' Set values in the text boxes to both corresponding to the film.
Dim strFields() As String = {"ID", "fName", "fGenre", "fSynopsis", "fAgeRating", "fActorsActresses", "fWebRating", "fNoReservations", "fWeeksRunning"}
Dim Con = New OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=ApplicationData.accdb;Persist Security Info=False;")
Con.Open() 'Open the connection
Dim Cmd As New OleDbCommand(StringBuilderCommand("*", "Films", dgvResults.CurrentCell.Value, "fName"), Con) 'Create a string by calling the StringBuilderCommand to combine the parameters together with quotes.
Cmd.CommandType = CommandType.Text
Dim Rdr As OleDbDataReader = Cmd.ExecuteReader()
Dim intCount As Integer = 4 ' Create a loop variable.
Do While Rdr.Read() Or intCount < 6 ' While this statement is 'TRUE', e.g. there is a valid record.
strResult = "txt" & strFields(intCount).Replace("f", "") 'Remove any instances of 'f', e.g. the prefix of the string.
txtActorsActresses.Text = StringBuilderCommand("*", "Films", dgvResults.CurrentCell.Value, "fName")
Me.Controls(strResult).Text = Rdr.Item(strFields(intCount)) ' Suspect the error lies here.
'Set the text-box to the correct value from the database.
'This will allow me to go through several text boxes, and grab their corresponding values from the database.
intCount = intCount + 1
'Current error is because it cannot find any data beyond the first field taken.
'I have no idea why this is. But if I change the starting intCount value, it will successfully take a different value.
Loop
Rdr.Close() 'Cleaning up.
Cmd.Dispose()
Con.Close()
WebBrowser1.Navigate(dgvResults.CurrentCell.Value.Replace(" ", ".") & ".movie.poster.new.jpg.to") 'Grab the movie poster off the internet corresponding to the films name.
End Sub
Private Function StringBuilderCommand(Field, Table, CurrentCellValue, SearchParameter)
'Creates a suitable SQL string.
Dim MyStringBuilder As New StringBuilder("SELECT ")
MyStringBuilder.Append("*") ' Append the parameter 'Field'.
MyStringBuilder.Append(" FROM ") ' Append the SQL command 'FROM'.
MyStringBuilder.Append(Table) ' Append the parameter 'Table'.
MyStringBuilder.Append(" WHERE ") ' Append the SQL command 'WHERE'.
MyStringBuilder.Append(SearchParameter) ' Append the parameter 'SearchParameter'.
MyStringBuilder.Append("=""")
MyStringBuilder.Append(CurrentCellValue) ' Append the parameter 'CurrentCellValue', representing the cell selected.
MyStringBuilder.Append("""") 'Append a quotation mark.
Return MyStringBuilder.ToString() ' Return it to the main program.
End Function
Database table being connected to:
A view of the error as it looks in Visual Studio 2012 Express:
The value of 'dgvResults.CurrentCell.Value' is the name of a film taken from the database (e.g. "12 Years a Slave").
What am I doing wrong?
Thanks,
C.

The problem is caused by the value of strFields(intCount) you are passing to the reader. It is not a valid column index.

You probably want to loop on the fields before looping again on DataReader(), like:
Do While Rdr.Read()
For intCount as Integer = 4 to 6
strResult = "txt" & strFields(intCount).Replace("f", "")
txtActorsActresses.Text = StringBuilderCommand("*", "Films", dgvResults.CurrentCell.Value, "fName")
Me.Controls(strResult).Text = Rdr.Item(strFields(intCount))
Next
Loop
I removed the Dim intCount As Integer = 4 because it is no longer needed because of the for next loop.

Related

Is there a way to run a for loop query based on datagridview selected values in vb.net?

Unable to get the excepted result due to the following error
Conversion from string " to type 'integer' is not valid'
I have been able to load values from the products table, add selected ones to Selected Products and then search all the selected products against the Customers table to find out how many customers ordered these products.
Try
Dim ListOfDiag As StringBuilder = New StringBuilder()
For Each row As DataGridViewRow In SelectedDiagDGV.Rows
ListOfDiag.Append(row.Cells(0).Value.ToString & "", "" & Environment.NewLine)
Next
Dim query As String = String.Empty
Dim SegmentConnectionString As String = "Data Source=Test-PC;Initial Catalog=TestDB;Integrated Security=True"
query = "SELECT Customers, ProductName from Customers WHERE ProductName in (" & ListOfDiag.ToString & ")"
Dim dTable As DataTable = New DataTable()
Dim dAdapter As SqlDataAdapter
dAdapter = New SqlDataAdapter(query, SegmentConnectionString)
dAdapter.Fill(dTable)
DataGridView1.DataSource = dTable
'Next
Catch ex As System. Exception
MsgBox(ex.Message.ToString)
End Try
Unable to perform a for loop search. Some of the values contain special characters example: Soft ’Drink’; Computer (Desk).
Error: Conversion from string " to type 'Integer' is not valid.
ListOfDiag.Append(row.Cells(0).Value.ToString & "", "" & Environment.NewLine)
There is no overload of StringBuilder.Append that takes (String, String) as arguments. the first string is row.Cells(0).Value.ToString & "" and then there is a comma between parameters and the second string is "" & Environment.NewLine Remember that "" is an empty string, not escape characters. Not sure what your intention was but this will not work.
You had the right approach; to build a string for the In clause. I used a List(Of String) to get the data from the rows then after the loop I used a .Join with a comma separator to get the value for the In clause.
I passed the connection string directly to the constructor of the Connection and passed the Select statement and the connection to the constructor of the Command. For the Select statement I used and Interpolated String (the string preceded by the $) You could also use String.Format in older version of Visual Studio.
The Using...End Using blocks ensure that your database objects are closed and disposed even if there is an error.
I think the only special character that could mess things up would be the presence of a comma in a Product Name.
Private Sub OPCode()
Dim dTable As New DataTable
Dim ListOfDiag As New List(Of String)
For Each row As DataGridViewRow In SelectedDiagDGV.Rows
ListOfDiag.Add(row.Cells(0).Value.ToString)
Next
Dim InData = String.Join(",", ListOfDiag)
Using cn As New SqlConnection("Data Source=Test-PC;Initial Catalog=TestDB;Integrated Security=True")
Using cmd As New SqlCommand($"SELECT Customers, ProductName from Customers WHERE ProductName in ({InData})", cn)
cn.Open()
dTable.Load(cmd.ExecuteReader)
End Using
End Using
DataGridView1.DataSource = dTable
End Sub

sql query returning whole column of data

I have a VB.net program that parses a set of variables into a sub that I use to communicate with an Access Database.
The query string reads:
SELECT ID FROM cbRooms WHERE #condition
#condition is given a value with the code:
.Parameters.AddWithValue("#condition", dbCondition)
Where dbCondition is, in this case:
H_Block=True
When I run this query within Access directly, I get the expected data back, which is a set of numbers, that have a ticked box in Access
However, when I run that same code in Visual Studio, it just returns the whole column, regardless of whether the tick box is true or not
It's been a while since i've written something in VB, so i'm quite rusty, and therefore the problem is likely a simple oversight on my part
Here is the code for the sql access function:
Public Function sqlSelect(ByVal dbCol As String, ByVal dbTable As String, ByVal dbCondition As String)
'Creating the sqlCmd string to sent as an SQL request to the Database
Dim sqlCmd As String
'Creating a new connection to the database
Dim conn As New OleDb.OleDbConnection
'Setting the value for the sqlCmd string, with several "#" parameters
sqlCmd = "SELECT " & dbCol & " FROM " & [dbTable] & " WHERE #condition;"
'Running the Connect Sub routine to interact with the database
Connect(conn)
Using conn
Using dbEvent As New OleDb.OleDbCommand
With dbEvent
'sets the connection used by the current instance of OleDB usng the conn string
.Connection = conn
'Sets how the .CommandType is interpreted
.CommandType = CommandType.Text
'Sets the sqlCmd string that will be sent
.CommandText = sqlCmd
'Setting the "#" parameters in the sqlCmd string using values parsed into the sub routine
.Parameters.AddWithValue("#condition", dbCondition)
End With
Try
'Opening connection to the database
conn.Open()
'Creating the _sqlRead Reader used to read the data coming from the database
Dim sqlReader As OleDb.OleDbDataReader
sqlReader = dbEvent.ExecuteReader()
'Creates a list that will store the values returned to the Search.vb class
Dim returnVals As New List(Of Integer)
Do While sqlReader.Read = True
returnVals.Add(sqlReader(dbCol))
Loop
Return returnVals
Catch ex As Exception
'Opens a message box showing the current error
MessageBox.Show(ex.Message.ToString(), "Error Message")
End Try
End Using
End Using
End Function
And Here is the code for the combo box that calls the AccessSQL.vb:
Public Sub cbBlock_SelectedIndexChanged(sender As Object, e As EventArgs) Handles cbBlock.SelectedIndexChanged
Select Case cbBlock.Text
Case "H Block"
cbRoom.Items.Clear()
Dim listOfRoomsInBlock As New List(Of Integer)
listOfRoomsInBlock = sqlSelect("ID", "cbRooms", "H_Block=True")
For x = 0 To listOfRoomsInBlock.Count - 1
cbRoom.Items.Add(listOfRoomsInBlock(x))
Next
Case "Tech Block"
cbRoom.Items.Clear()
Dim listOfRoomsInBlock As New List(Of Integer)
listOfRoomsInBlock = sqlSelect("ID", "cbRooms", "T_Block=True")
For x = 0 To listOfRoomsInBlock.Count - 1
cbRoom.Items.Add(listOfRoomsInBlock(x))
Next
End Select
End Sub
Thanks for any help!
EDIT:
The SQL, when run in VB returns This into the combo box I'm trying to populate
The 1 - 13 values at the bottom is what I'm expecting, whereas the 14 - 30 is not. My thinking is that my parameterisation is never actually using the condition I set above.
However, when I run that same, expected query, inside Access, I get the right values returned

Simple way to convert dbNull to a string in VB.NET

I'm looking for a simpler way to check if a value is dbNull and to convert it to an empty string if so.
An example of a situation where I need this would be:
Dim dt As New DataTable
Dim conn As New OleDbConnection(someConnStr)
Dim adap As New OleDbDataAdapter(qryCSSInfo, cssConn)
adap.Fill(dt)
Dim someStr As String = "The first column of the first row returned: " & dt.rows(0).item(0)
Msgbox(someStr)
The problem is that if dt.rows(0).item(0) is null in the database it will be returned as a dbNull value, which can apparently not be appended to a string.
My solution to this problem has been using if statements to replace the value with blank strings:
Dim dt As New DataTable
Dim conn As New OleDbConnection(someConnStr)
Dim adap As New OleDbDataAdapter(qryCSSInfo, cssConn)
adap.Fill(dt)
If Not isDBNull(dt.rows(0).item(0)) then
Dim someStr As String = "The first column of the first row returned: " & dt.rows(0).item(0)
Else
Dim someStr As String = "The first column of the first row returned: " & ""
End If
Msgbox(someStr)
This works fine for my purposes, but it gets overwhelming if I have to make this check for every column I need to use in the table. Say I had 10 columns from the table that I wanted to display with this string. I'd have to make this check on each one to ensure they weren't null. Is there an easier or simpler way of doing so?
For string types you can directly use it this way dt.rows(0).item(0).ToString(), without the If condition
adap.Fill(dt)
Dim someStr As String = "The first column of the first row returned: " & dt.rows(0).item(0).ToString()
MsgBox(somestr)
i.e. you can completely omit the if statement. As per MSDN any DBNull value will be converted to EmptyString with .ToString()
Also check this SO post Conversion from type 'DBNull' to type 'String'
However, for non-string database column types such as integers, doubles you must apply checks using IsDBNull to avoid any exceptions.
You can leverage the If Operator to reduce a few lines of code:
Dim someStr As String = "The first column of the first row returned: " & _
If(dt.rows(0).item(0) Is DbNull.Value, String.Empty, dt.rows(0).item(0))
You should be able to concatenate a null field with a string - it should convert to an empty string. That said row.IsNull(index) is a good test to use.
SQL = "Select top 10 Region, CompanyName FROM Suppliers"
Dim dt As DataTable = Gen.GetDataTable(SQL, scon)
For Each row As DataRow In dt.Rows
MsgBox(row("companyName") & " region: " & row("Region")) ' null allowed
If row.IsNull("region") Then ' .Net test for Null
MsgBox(row("companyName") & " region is null")
Else
'continue
End If
Next
You can also resolve this in the query - covert nulls to useful (or empty) strings. The example query is from SQL Server, I don't know if your DB supports COALESCE.
MsgBox("COALESCE") ' SQL Server - may not be the same in ODBC databases
SQL = "Select top 10 COALESCE(Region,'na') Region, CompanyName FROM Suppliers"
dt = Gen.GetDataTable(SQL, scon)
For Each row As DataRow In dt.Rows
MsgBox(row("companyName") & " region: " & row("Region"))
Next
Some coding notes:
Dim dt As New DataTable
Dim conn As New OleDbConnection(someConnStr)
Dim adap As New OleDbDataAdapter(qryCSSInfo, cssConn)
adap.Fill(dt)
If Not IsDBNull(dt.Rows(0).Item(0)) Then ' in OP
'...
End If
' save some typing if you know there will be only one record
' will throw exception is no rows are returned, check for expected count
Dim row As DataRow = dt.Rows(0)
If Not IsDBNull(row(0)) Then
'...
End If
' or
If Not row.IsNull(0) Then
'...
End If
' note the fields can be accessed by name so you can avoid hard coding field position
If Not row.IsNull("FieldName") Then
'...
End If
The simplest way to do it is just add a "" after the field or string.
Eg.:
dim EmptyString as string = Nullfield() & ""
if EmptyString = ""
' in the sample, it should.
end if
So, in your code you can use:
If dt.rows(0).item(0) & "" = "" then
' it should be...
end if
I got some null data into cells of a datagrid; to correctly retrieve that data
I concatenate the "" string to the cell value:
Dim readVal As String = "" & row.Cells(2).Value

OleDbDataAdapter - read tab delimited file

(I don't need alternatives to OleDbDataAdapter.)
The code below finds and reads the file OK but the DGV has four columns (as expected) but all the data rows just have text in the first column.
Dim sDir As String = "c:\temp\"
Dim sConn = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" & sDir & ";Extended Properties='text;HDR=Yes;FMT=TabDelimited';"
Dim dt As New DataTable()
Using adapt As New OleDbDataAdapter(String.Format("SELECT TOP 100 * FROM robo.txt"), sConn)
adapt.Fill(dt)
End Using
DataGridView1.DataSource = dt
I would think the Extended Properties would be the only requirement. I've tried add a Schema.ini to no avail - I don't think it is even being read as the column headers never match the schema.
The header row in the most successful pass used commas as separator - this resulted in four columns with the proper names but the tab separated data all in Col1. If I use tabs in the header row I get some system assign columns (3) which sort of corresponds to a data row with two commas.
What am I doing wrong?
Here are the first few rows with the tab character being replaced by <tab> . I since noticed that I have an extra column in the data. The fix to the header row below did not fix the problem - all data is dumped into the first field.
Use a tab separator in the header, instead of commas, results in all header text and the data being dumped into the first field.
col1,state,col3,size,path
<tab> same<tab><tab> 102912<tab>\\APCD04T\Data\Thumbs.db
<tab> same<tab><tab> 22016<tab>\\APCD04T\Data\APCD Topical Info\APCD_Boards&Committees_List.doc
<tab> same<tab><tab> 4.3 m<tab>\\APCD04T\Data\APCD Topical Info\LOSSAN-LAtoSLORailCorridorStrategicPlan.pdf
Learned several things while trying to load a RoboCopy log into a DataTable using OLEDB.
log file needs to have a .txt or .csv (or ?) extension, .log fails.
Schema.ini seems to be needed for tab delimited robocopy log, good for column definition anyway.
Datagridview takes a long time to display 30MB of data so I used
filters
I borrowed code from the net to create a Schema.ini as noted below
(SO bug: code will not paste from Visual Studio anymore. Code tool flips to other web page for Java.)
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Try
Cursor = Cursors.WaitCursor
'http://ss64.com/nt/robocopy.html can suppress header and summary
Dim sFile As String = "c:\temp\robo.txt" ' seems to need a .txt or .csv, .log didn't work
CreateRoboLogSchema(sFile) ' recreates each pass, no needed once things work
Dim sConn = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" & IO.Path.GetDirectoryName(sFile) & ";Extended Properties='text';"
' use Schema.ini for: HDR=Yes;FMT=TabDelimited' and column definitions
Dim dt As New DataTable()
Dim SQL As String = "SELECT * FROM " & IO.Path.GetFileName(sFile)
'SQL &= " WHERE State <> 'Same'"
Using adapt As New OleDbDataAdapter(SQL, sConn)
adapt.Fill(dt)
End Using
Debug.Print("|" & dt.Rows(0)(1) & "|") ' show import trimmed leading spaces (trims trailing too)
' DGV slow to load large files, use filter to display target rows
Dim dv As New DataView(dt)
dv.RowFilter = "State <> 'Same'" ' not case sensitive
DataGridView1.DataSource = dv
DataGridView1.Columns(0).Visible = False
DataGridView1.AutoResizeColumns()
Catch ex As Exception
MsgBox(ex.Message)
Finally
'Cursor=Cursors.Default
End Try
End Sub
Private Function CreateRoboLogSchema(ByVal strFileName As String) As Boolean
' edit http://www.vb-tips.com/CSVDataSet.aspx
Dim ascii As System.Text.Encoding = System.Text.Encoding.ASCII
Dim swSchema As System.IO.StreamWriter = Nothing
Dim blnReturn As Boolean
Dim strSchemaPath As String = System.IO.Path.GetFileName(strFileName)
Try
strSchemaPath = IO.Path.GetDirectoryName(strFileName) & "\Schema.ini"
swSchema = My.Computer.FileSystem.OpenTextFileWriter(strSchemaPath, False, ascii)
Dim strFile As String = System.IO.Path.GetFileName(strFileName)
swSchema.WriteLine("[" & IO.Path.GetFileName(strFileName) & "]")
swSchema.WriteLine("ColNameHeader=False")
swSchema.WriteLine("Format=TabDelimited")
swSchema.WriteLine("Col1=Value1 Text") ' file specific
swSchema.WriteLine("Col2=State Text")
swSchema.WriteLine("Col3=DirChanges Text")
swSchema.WriteLine("Col4=Size Text")
swSchema.WriteLine("Col5=Filepath Text")
'Continue for all fields
blnReturn = True
Catch ex As Exception
blnReturn = False
Finally
If swSchema IsNot Nothing Then
swSchema.Close()
End If
End Try
Return blnReturn
End Function

Unwanted data truncating from Excel

When I try to do an import from an Excel document the comments get truncated. I have checked the usually issue that the Table would be limited but is set as:
Comments ... nvarchar(MAX)
Sample of the code, please note even running the code in Debug mode I can see the parameter is truncated before it even goes to the stored procedure.
Dim excelConnectionString As String = (Convert.ToString("Provider=Microsoft.ACE.OLEDB.12.0; Data Source=") & vFileNameFolder) + "; Extended Properties='Excel 12.0;HDR=YES;IMEX=1;';"
'#### Upload, Rename and save file
'#### Open Excel to Parse here
Dim ds As New DataSet
Dim oleda As New OleDbDataAdapter()
Dim cmdExcel As New OleDbCommand()
'#### End - Open Excel to Parse here
Dim vActionRef As String = ""
Try
Dim excelConnection As New OleDbConnection(excelConnectionString)
With cmdExcel
.CommandText = "Select * from [Portal$A1:BB9999]" 'Names we want to select and the name of the sheet
.CommandType = CommandType.Text
.Connection = excelConnection
End With
excelConnection.Open()
oleda = New OleDbDataAdapter(cmdExcel)
oleda.Fill(ds, "dataExcel")
If ds.Tables("dataExcel").Rows.Count > 0 Then
'#### Stored procedure details
Dim connection As SqlConnection
Dim commandSQL As New SqlCommand
Dim FRAUPRN As String = ""
Dim ConnectionString As String = System.Configuration.ConfigurationManager.ConnectionStrings("SQLLocal").ToString()
'########### End - Stored procedure details
'Set date once
Dim vDate As Date
vDate = DateTime.Now.AddDays(0)
connection = New SqlConnection(ConnectionString)
connection.Open()
'Dims for error handling and checking for invalid characters
Dim iImported As Integer
For j As Integer = 0 To ds.Tables("dataExcel").Rows.Count - 1 ' counted rows so loop through, ignores first row with names in
If (IsDBNull(ds.Tables("dataExcel").Rows(j)("UPRN"))) Then
'skip
Else
iImported = iImported + 1
'Bring the data across, the rows(i)("xxx") must match a name on the Excel sheet but DOES NOT have to be in order
With commandSQL
.Parameters.Clear()
.Connection = connection
.CommandText = "spAddCSVDataLine" 'Stored procedure here
If Trim(ds.Tables("dataExcel").Rows(j)("Comments")) = "0" Then
.Parameters.AddWithValue("Comments", " ")
Else
' .Parameters.AddWithValue("Comments", If(IsDBNull(ds.Tables("dataExcel").Rows(j)("Comments")), "", Trim(ds.Tables("dataExcel").Rows(j)("Comments"))))
Dim vComments As String
vComments = ds.Tables("dataExcel").Rows(j)("Comments")
.Parameters.AddWithValue("Comments", vComments)
Session.Item("Comments") = Session.Item("Comments").ToString & "//" & vComments
End If
I have looked at similar questions such as ADO is truncating Excel data which talks about numerical issues but am struggling to find the reason why I am losing data before I export the data. 'Common sense' says excel is not passing over more than 255 characters but then this is programming!
I've had all sorts of problems with the JET/Ace DB engine truncating and doing other sorry-ass guesses at data type. Check out this Microsoft article that talks a bit about how JET uses only the first 8 records to determine field length (http://support.microsoft.com/kb/189897/en-us). You can edit a registry setting to change how many records it will scan to determine field length, but the results still seem to be hit or miss for folks.
You might also find some luck in creating a dummy record at the top of the excel sheet that contains a comment with the maximum number of characters of any of your comments. Then just delete that one record after it comes through. Again... results seem to be mixed here.