DataReader.Read() skips first row of recordset - asp.net

I am trying to check the value of a field before decided what to input into a drop down list in ASP.net.
I am using datareader.Read() in order to read the recordset so I can do this. However this then skips the first row of data . . The drop down box is basically a list of sizes and colours . . . So currently I am missing the first size.
Here is the code:
Using cmd As New SqlCommand("doGetAllSizesForProduct", oConn)
cmd.CommandType = CommandType.StoredProcedure
cmd.Parameters.AddWithValue("#id", CType(Request.QueryString("id"), Integer))
oConn.Open()
Using dr As SqlDataReader = cmd.ExecuteReader()
If dr.HasRows() = True Then
dr.Read()
ddlSize.Visible = True
pnlSize.Visible = True
pnlNoStock.Visible = False
If dr("colour") = "None" Then
ddlSize.DataTextField = "size"
Else
ddlSize.DataTextField = "sizeColour"
End If
ddlSize.DataValueField = "mapperid"
ddlSize.DataSource = dr
ddlSize.DataBind()
Else
End If
dr.Close()
End Using
End Using
I guess either there must be another method other than Read or a way to stop it skipping the first record?

I've never seen a datareader being used as the datasource. What I think is happening is that your first call to dr.Read() is skipping to the first record as expected. However, when you assign the reader as the datasource, it's doing its own dr.Read() logic inside which starts at the next record. This could explain why you're not seeing the first item. Try modifying your code like this to use a DataTable instead (warning, didn't test this):
using dr as SqlDataReader = cmd.ExecuteReader()
if dr.HasRows() then
ddlSize.Visible = True
pnlSize.Visible = True
pnlNoStock.Visible = False
While dr.Read()
dim Value as string = dr("mapperid")
dim Text as string = if(dr("colour") = "None",dr("size"),dr("sizeColour"))
ddlSize.Items.Add(New ListItem(Text, Value))
End While
end if
dr.Close()
end using

How about changing the doGetAllSizesForProduct stored procedure to return two recordsets?
The first recordset can return a single row giving an indication of what type of data is contained in the second recordset, which contains the same content as previously.

you are not supposed to use the DataReader like this, executing a single Read to get the value in the first record then binding the UI control to it like this:
ddlSize.DataValueField = "mapperid"
ddlSize.DataSource = dr
ddlSize.DataBind()
I would personally use a DataTable for binding to a UI control, or try to remove the call to dr.Read() and see how it works.

Related

Read multiple items from list box to query information from a database

Ok so what I'm trying to do is allow the users of my web form in asp.net to select multiple assets from a list box. When they select these assets and press the select button it fires the following code which should run through the selected indices, query the DB for the description and asset tag's, and populate the Description and Asset tag boxes with those values. Working through this I've been able to get it to read the values from the database and populate the fields but it would only populate the value from the first selected item and it would just insert the same value equal to the number of times equal to the number of items selected in the inbox. I tried my current code to run through the indices a little better but it seems to be failing the If statement and I just can't figure out why. Any help is appreciated, thanks everyone!
Dim i As Integer
For i = 0 To lstAssets.GetSelectedIndices.Count
If lstAssets.Items(i).Selected = True Then
Dim str2 As String = lstAssets.Items(i).Value.ToString
Dim cs As New OracleConnection(System.Configuration.ConfigurationManager.ConnectionStrings("CSIWebUpd").ConnectionString)
Dim ds As String = cs.DataSource
Dim cn As OracleConnection = New OracleConnection("user id=webupd;password=webupd;data source=" + ds)
Dim sql As String = "SELECT description, tag FROM assets WHERE id = :id"
Dim cmd As New OracleCommand(sql, cn)
cmd.Parameters.Add(":id", OracleDbType.Varchar2).Value = str2
cmd.CommandType = CommandType.Text
cmd.BindByName = True
cn.Open()
Dim dr As OracleDataReader = cmd.ExecuteReader()
cmd.ExecuteNonQuery()
dr.Read()
desc = dr.GetString(0).ToString
ass = dr.GetString(1).ToString
If txtDescription.Text = Nothing Then
txtDescription.Text = dr.GetString(0).ToString
Else
txtDescription.Text = txtDescription.Text + Chr(13) + Chr(10) + dr.GetString(0).ToString
End If
If txtAsset.Text = Nothing Then
txtAsset.Text = dr.GetString(1).ToString
Else
txtAsset.Text = txtAsset.Text + Chr(13) + Chr(10) + dr.GetString(1).ToString
End If
cmd.Dispose()
cn.Close()
End If
Next i
UpdatePanel1.UpdateMode = UpdatePanelUpdateMode.Conditional
UpdatePanel1.Update()
I would work with the SelectedIndices values in this way
For Each i in lstAssets.GetSelectedIndices
Now i contains the index of the items selected not the offset of the GetSelectedIndices array
The GetSelectedIndices returns an array where every element of the array is the index in the Items collection where you have an item selected.
An example could explain better:
Supposing the the items selected are 1, 5 6 (Count=3), your code set the value of i to 0,1,2 and then uses that value to lookup the ID in the items collection, but the following check on lstAssets.Items(i).Selected found just the item at index 1 as selected. Instead using the for each approach you get the values 1,5,6.

Find nested gridview in user defined function

I'm having a problem populating a child gridview using a function I define. I keep getting the error "Object reference not set to an instance of an object". What am I doing wrong? Am I using the FindControl function incorrectly? It doesn't seem to find the child gridview.
Sub RecordsByZip()
Dim DBConn As New SqlConnection(Application("DBConn"))
Dim gv1 As GridView
gv1 = grdTotal
Dim gv2 As GridView
gv2 = DirectCast(gv1.FindControl("grdChild"), GridView)
Dim ZipCode = lbZip.SelectedItem
For Each ZipCode In lbZip.Items
If ZipCode.Selected = True Then
Dim cmdZip As SqlCommand = New SqlCommand("spPICAInsertTotals2", DBConn)
cmdZip.CommandType = CommandType.StoredProcedure
strZip = ZipCode.Text
strUser = Session("User")
Dim Zip As New SqlParameter("#Zip", SqlDbType.VarChar)
Zip.Value = strZip
cmdZip.Parameters.Add(Zip)
Dim UserID As New SqlParameter("#UserID", SqlDbType.Int)
UserID.Value = strUser
cmdZip.Parameters.Add(UserID)
DBConn.Open()
gv1.DataSource = cmdZip.ExecuteReader
gv1.DataBind()
gv1.Visible = True
DBConn.Close()
End If
Next
btnExport.Visible = True
lblmsg.Visible = False
' Dim DBConn = New SqlConnection(Application("DBConn"))
Dim cmdCounty As SqlCommand = New SqlCommand("spPICAInsertTotals", DBConn)
cmdCounty.CommandType = CommandType.StoredProcedure
'Dim gv As GridView = TryCast(e.Row.FindControl("grdChild"), GridView)
strUser = Session("User")
Dim UserID2 As New SqlParameter("#UserID", SqlDbType.Int)
UserID2.Value = strUser
cmdCounty.Parameters.Add(UserID2)
DBConn.Open()
gv2.DataSource = cmdCounty.ExecuteReader
gv2.DataBind()
gv2.Visible = True
DBConn.Close()
btnExport.Visible = True
lblmsg.Visible = False
lblInstructions.Visible = False
End Sub
First of all . . .
Just as a disclaimer, I normally use repeater controls instead of gridview controls.
But this may help you . . .
I can tell you that with repeater controls, if you want to find a nested repeater then you must look for them inside the item of the parent repeater to which they belong. Essentially, what you are trying to do with the code above, is find grdChild when there might actually be several grdChild (it is a nested gridview, after all). I'd be willing to bet this is where you're object reference error is occurring.
In other words, if you want to find the nested repeater with the ID nestedRepeater, and you know it is located in the first item of your main repeater (which in this case I've assigned to the myRepeater variable), you can do this:
Dim myItem as RepeaterItem = myRepeater.Items(0)
Dim Rep2 as Repeater = myItem.FindControl("nestedRepeater")
Using SqlDataAdapter and a DataSet (recommended)
Dim sa As New SqlDataAdapter(cmdCounty) 'Initialize the SqlDataAdapter and assign the SqlCommand object to it.
Dim ds As New DataSet() 'Initialize the DataSet (we will bind this to the gridview)
Try 'The Try/Catch statements help you to handle errors.
cmdCounty.Connection.Open() 'Open the connection to the database.
sa.Fill(ds) 'This statement uses the SqlDataAdapter to easily execute the SqlCommand (using the query specified in the SqlCommand object) and . . .
'. . .use that data to fill our dataset.
cmdCounty.Connection.Close() 'These statement close the connection and dispose of the SqlCommand object. Note: You may only need the dispose command.
cmdCounty.Dispose()
Catch ex As Exception
'Catch your error here.
cmdCounty.Connection.Close()
cmdCounty.Dispose()
End Try
gv2.DataSource = ds 'Set the datasource for your GridView control.
gv2.DataBind() 'Bind the data.
Using SqlDataReader and a DataTable
According to your comment, you're code is breaking when you assign the gridview to the cmd.ExecuteReader.
You will need to access your data using a method like this:
Dim rdr as SqlDataReader = cmdCounty.ExecuteReader() 'Declare the SqlDataReader and set it to handle your SqlCommand.
Dim dt as New DataTable 'Initialize a new DataTable. This is where we will place the information we read using the SqlDataReader.
'Make sure you add the columns...
dt.Columns.Add("firstColumnName") 'Create a column for each field you will be retrieving data from.
dt.Columns.Add("secondColumnName")
Dim r as DataRow 'Declare the variable r as a DataRow.
'You may want to insert the line "If rdr.HasRows Then" to check if any data was pulled before attempting to read it.
While rdr.Read() 'Loop through each row in the reader.
r = dt.NewRow() 'Set r to equal a new DataTable in the DataTable we created. Note: This does not actually add the row to the table.
r("firstColumnName") = rdr("firstColumnName") 'Set the values of each column in the current DataRow to equal their corresponding data read from SQL.
r("secondColumnName") = rdr("secondColumnName")
dt.Rows.Add(r) 'Add the DataRow r to the DataTable.
End While 'Loop back until there are no more rows.
gv2.DataSource = dt
gv2.DataBind()
Both of these examples assume you have already created your SqlCommand object and have assigned a working SQL query string and Connection object to it.

Asp.net parametized SQL Query with DataAdapter/Dataset

What I want to do is to look for the text that is entered on textbox4 assigned to Valor and display if found, the below code is working but I want to use parametized queries (security reasons) and I don't know how to modify my existing code to get it done. (eg, 123-A)
I will look for "123-A" but in the current code I got an error of "invalid column A", the SQL column that I will be looking "123-A" is "ID_LALTest"
Try
' *--------search by Unique ID-------*
Dim CON As New SqlConnection
Dim DA As New SqlDataAdapter
Dim DS As New DataSet
Dim SQL As String
Dim Valor As String
Valor = TextBox4_SearchData_LALTest.Text
CON.ConnectionString = "not displayed"
CON.Open()
SQL = "SELECT ID_LALTest, LALTest_SeqRef_CH, LALTest_SeqRef_Year FROM LALTest WHERE ID_LALTest=#Valor"
DA = New SqlDataAdapter(SQL, CON)
DA.SelectCommand.Parameters.AddWithValue("#Valor", Valor)
DA.SelectCommand.ExecuteNonQuery()
DA.Fill(DS, 0)
If DS.Tables(0).Rows.Count > 0 Then
' *--------Found, Display Data Grid-------*
Label2_SearchData_LALTest.Visible = False
GridView2_SearchData_LALTest.Visible = True
GridView3_SearchData_LALTest.Visible = True
GridView1_SearchData_LALTest.Visible = False
Else
Label2_SearchData_LALTest.Text = "Record Not Found"
Label2_SearchData_LALTest.Visible = True
GridView2_SearchData_LALTest.Visible = False
GridView3_SearchData_LALTest.Visible = False
GridView1_SearchData_LALTest.Visible = False
End If
con.dispose()
Catch ex As Exception
MsgBox(Err.Description)
End Try
Yup, you are wide-open for sql-injection. Depending on different databases (you just have SQL, but is that SQL-Server, MySQL, an Access/SQL database, etc).
Anyhow, it's not too far off from what you have. Not specializing in VB, I'll give you some pseudo-code for it...
Change your query, and put in a "place-holder" for the "variable" you want to apply from the input... Ex:
"select (your fields) from (yourtable) where ID_LALTest = #parmValor"
Then, add the parameters to your sql command created for your data adapter... something like...
DA.SelectCommand.Parameters.Add( "#parmValor", theInputFromYourVariable );
Now, if you have multiple conditions you want to apply, just keep adding "#someParm" value and add the parameters IN THE SAME ORDER as they exist in the query... I've heard from others in the past, that just the ordinal sequence of parameters not matching the query can/does cause problems... such as data type expectations too.

If I select a row

I am going to do my best to explain this, though admittedly I have not attempted much in months so I'm not only rusty, I wasn't good to being with.
I am using visual web developers and asp, vb is the code behind with a sql db.
If I select a columns from a table, for example:
sqlCmd.CommandText = "SELECT Username, W1, W2, W3, W4 FROM tablename"
Say there are multiple rows in this table with data in these columns.
When I do a datareader, or how I have been shown, I declare dr like:
Dim dr As Data.SqlClient.SqlDataReader
I can work with the selected items such as:
dr.item(0)
dr.item(1)
etc.
But the only items I can work with are the first row of items selected. How do I select all of the rows in the table. Or how can I work with the data from multiple rows using dr.item or by somehow telling it to move onto the next row so that dr.item(0) becomes the username for the second row in the table.
I hope that made sense and I'm sorry if this is a stupid question. I appreciate the time and help in advance. Thanks guys.
SqlDataReader.Read advances the reader to the next record and returns true when there is at least one other row:
Using conn = New SqlClient.SqlConnection(connString)
Using cmd = New SqlClient.SqlCommand("SELECT Username, W1, W2, W3, W4 FROM tablename", conn)
conn.Open()
Using dr = cmd.ExecuteReader()
While dr.Read()
Dim UserName As String = dr.GetString(0)
' ... '
End While
End Using
End Using
End Using
Use Using to dispose anything that implements IDisposable as soon as possible. It will also close connections implicitely.
Edit: using a DataTable
How do I select all of the rows in the table
The DataReader approach above works well, but if you want to select all rows anyway and it's ok to load all into memory, you can use a DataTable instead. Then you could also access each row via indexer like an array or list:
Dim tblUsers = New DataTable()
Using conn = New SqlClient.SqlConnection(connString)
Using da = New SqlClient.SqlDataAdapter("SELECT Username, W1, W2, W3, W4 FROM tablename", conn)
da.Fill(tblUsers)
End Using
End Using
' access a row via index: '
Dim row10 As DataRow = tblUsers.Rows(9)
Dim user10 = row10.Field(Of String)("Username")
' of course you can also iterate all rows: '
For Each row As DataRow In tblUsers.Rows
Dim userName = row.Field(Of String)("Username")
Next
To iterate through the rows of Data Table you need to use the following method:
while dr.read()
Dim col1=dr.item(0)
Dim col2=dr.item(1)
End while
So that You can each Attribute of ALL Rows.

SQLDataReader find value of each row

I used to use datasets instead of sqldatareaders and I used to be able to do something like this
If dataset.tables(0).Rows(0)(1).ToString()) = "N" Then
lbl.Text = dataset.tables(0).Rows(0)(2).ToString())
Else
'Do Nothing
End If
This obviously doesn't work with sqldatareaders.
I have code to see if the SQLDatareader has any rows but was wondering if there was a way to get the value of each row
I'm guessing this is possible and i've had a look around but can't seem to find anything
Dim conn As SqlConnection = New SqlConnection("server='h'; user id='w'; password='w'; database='w'; pooling='false'")
conn.Open()
Dim query As New SqlCommand("DECLARE #investor varchar(10), #sql varchar(1000) Select #investor = 69836 select #sql = 'SELECT * FROM OPENQUERY(db,''SELECT * FROM table WHERE investor = ''''' + #investor + ''''''')' EXEC(#sql)", conn)
Dim oDR As SqlDataReader = query.ExecuteReader()
If oDR.HasRows or dataset.tables(0).Rows(0)(1).ToString()) = "N" Then
lbl.Text = dataset.tables(0).Rows(0)(2).ToString())
Else
'Do Nothing
End If
That is the code I have at the moment which obviously doesn't work
Any ideas?
Thanks
When you use the data reader you have to step through each row yourself. Using HasRows is a good start, for it will tell you if the returned result set is empty.
To iterate through the result set you should use the Read() method. It will return true if you are at a row and false when you have moved past the last row.
My Vb is poor so I will give you an example in C# instead:
if (oDR.HasRows && oDR.Read())
{
if (oDR.GetString(0) == "N")
{
lbl.Text = oDr.GetString(1);
}
}
Here I first check that we have a result set with data and then try to move to the first row. If this succeeds I then read the string value of the first column and compare it to "N". If the value is equal to "N" I set the Text property of the lbl variable to the string value of the second column.
This should be equivalent to your algorithm with the dataset. I recommend that you read the MSDN documentation for the SqlDataReader. It is quite good and the example code is useful.

Resources