ADO.NET record navigation - vb.net

I've done development in both VB6 and VB.NET, and I've used ADODB objects in VB6 to handle recordset navigation (i.e. the MoveFirst, MoveNext, etc. methods), and I have used ADO.NET to handle queries in a row-by-row nature (i.e For Each Row In Table.Rows ...)
But now I seem to have come to a dilemma. I am now building a program in VB.NET where I need to use the equivalent functionality of the Move commands of the old Recordset object. Does VB.NET have some sort of object that supports this functionality, or do I have to resort to using the old ADODB COM object?
Edit: Just for clarification, I want the user to be able to navigate through the query moving forwards or backwards. Looping through the rows is a simple task.

There is no need to go back to the bad old days. If you can give a pseudo code example, I can translate to vb.net for you.
This is kind of a generic way to do it.
Dim ds as DataSet
'populate your DataSet'
For each dr as DataRow in ds.Tables(<tableIndex>).Rows
'Do something with the row'
Next
Per Edit 1: The user will navigate the results, not the query. So what you want to do is either a) get the results and display only the current rowindex of ds.Tables.Row() to them, or b) execute a new query with each navigation (not a real well performing option.)
Per comment: No, they havent. But the user usually will not be working interactively with the database like this. You will need to get your dataset / table of the results, and use the buttons to retrieve the relevant row from the dataset/table.
The First Row is DataTable.Rows(0)
The Last Row is DataTable.Rows(DataTable.Rows.Count-1)
for any row in between (store the currently displayed rowindex in your app), then call
DataTable.Rows(currentRowIndex -1) for previous and
DataTable.Rows(currentRowIndex +1) for next.

It all depends on the usage:
If you need only to list the results of one or more queries you should use the datareader. Has DOK pointed out, it's read-only and foward-only so it's fast.
http://www.startvbdotnet.com/ado/sqlserver.aspx
If you need to navigate thou the records you should use a dataset.
http://www.c-sharpcorner.com/UploadFile/raghavnayak/DataSetsIn.NET12032005003647AM/DataSetsIn.NET.aspx
The dataset also has the advantage of working "disconnected", so you build all the logic, and only when you need the data you call the Fill method. The dataset is populated and then you can start working with the data, now disconnected from the DB.
Hope it helps,
Bruno Figueiredo
http://www.brunofigueiredo.com

Here's a quick example of using the datareader:
Dim cmd As New OleDb.OleDbCommand(sql, Conn) 'You can also use command parameter here
Dim dr As OleDb.OleDbDataReader
dr = cmd.ExecuteReader
While dr.Read
‘Do something with data
‘ access fields
dr("fieldname")
‘Check for null
IsDBNull(dr("fieldname"))
End While
dr.Close()

In .Net, there are many ways to do this. One that I like is to use a DataReader, which can return multiple recordsets. You can loop through its records using While DataReader.Read.
One of the advantages of using DataReader is that it is a forward-only, read-only object, so it's fast and light-weight.
To allow the user to navigate through all of the records, one at a time, you do not want to hold a DataReader open while the user navigates. you can read the DataReader records into objects. Or, you can retrieve the records into a DataSet, and display the DataRows from the DataTable one at a time.
I would suggest that, if possible, you retrieve all of the records at once if there are not too many. This will save repeated calls to the database.
On the other hand, if there are a lot of records, you could retrieve the first few (say, 10 or 20) and only retrieve the next set of records with a new database call if the user clicks beyond the initial set. This is lazy loading.

Related

VB.Net dataset rows count always returning 0

I created a table adapter for a stored procedure I have, which returns a row from a table. After adding it to the dataset, I try to loop through the row count of this table to retrieve the data but it keeps returning no rows. If I try to preview the data in the dataset designer, i get the row normally but when i try it in code i get nothing
For intI As Integer = 0 To Me.Ds1.SP_Get_Data_Communication_Parameters.Rows.Count - 1
Dim IP As String = Ds1.SP_Get_Data_Communication_Parameters.Rows(intI)("Remote_IP_address")
Next
A tableadapter is a device that moves data in either direction between a database and a datatable.
A datatable is part of a dataset (a dataset is a collection of datatable)., and is a client side representation of (some or all) of a database table.
To work with database data you use a tableadapter to transfer it from the database table to the datatable. You work with it, maybe edit it and maybe save it back to the database
From what you've described it sounds like you're not actually using a tableadapter to fill the datatable before inspecting it for data. The dataset designer is just a visual representation of the tableadapter and associated datatable classes; it doesn't mean that the database data is automatically available in your program
You'll need to have a code like:
Dim ta As New YourDatasetNameTableAdapters.SP_Get_Data_Communication_ParametersTableAdapter()
ta.Fill(Me.Ds1.SP_Get_Data_Communication_Parameters, any, parameters, the, sproc, needs, here)
Then you can look through the datatable and see the data the TA downloaded
Edit footnote:
If you make changes to the rows, like
For Each ro in Ds1.SP_Get_Data_Communication_Parameters
ro.FirstName = "John"
Next ro
Then you can send the changes back to the db using the Update method of the table adapter
at.Update(Ds1.SP_Get_Data_Communication_Parameters)
Update will run all different kinds of query, not just UPDATE. Newly added rows will be INSERT. Deleted rows will be DELETEd. Microsoft should really have called it SaveChanges

How to get info from a listbox populated by an SQL query?

The title says the vague question, but, I will be more specific. I'm more of a hardware oriented person with some experience in VB, Java, and C++. I've been asked to modify a form in Access which uses SQL queries to take information from a database. The edit I am trying to make is to a form that has selections to narrow down the data it queries from to appear in a listbox in the center of the form (so, like radio buttons to specify a certain height or weight of an object in the database). The selections, as they are selected, concatenate additional specifications to an string that is then run as an SQL query. What I am trying to accomplish is to take a column of the narrowed-down data and find the maximum number in the column and the average of the numbers in the column. It seems really simple and know how to do that with regular lists and arrays, but I'm at a loss with my limited knowledge. I've considered making another SQL string, but in another part of the form, it was done with two strings (so, one to put the data in the listbox from the database and one to pull the specific column of the narrowed down data). That being said, it doesn't seem logical to me to pull the same sets of data each time, so I'm wondering if I can just pull the info from the already pulled info. At the bottom of the code set is this:
ItemList.RowSource = vSQLStock
ItemList = Null
ItemList.Requery
QuoteList.RowSource = vSQLNonStock
QuoteList = Null
QuoteList.Requery
Both vSQLStock and vSQLNonStock have their own sets of SQL strings that are run by this function, or as far as I can tell (I'm still studying up on SQL) put the narrowed down items in the QuoteList listbox. What I'm asking is if there is a way with any function that can be used to go through this information? Specifically, take a column and do some calculations on the data. I don't want to run another SQL query and bog down the server, but rather manipulate the data on the user's computer.
An example of what happens is the user selects whether the item is stock/nonstock, searches by the name/code/ID, and then puts all the matches on the listbox on the form. The listbox shows lines of data with columns like Name, Cost, Size, Weight, etc. of which I am looking to do calculations on the Cost column. Hopefully this is enough information; I appreciate any advice.
It looks from your question like you are attempting to get the Maximum and the Average from a SQL query.
the syntax:
SELECT MAX(myColumn) as MyMax, AVG(myColumn) as MyAverage
FROM myTable
WHERE . . .
Will return the Maximum and Average Values within SQL Server or within MS Access
If you're really bound and determined to do this locally with the data you've already retrieved from the server, then you probably need to look into working with adodb.recordsets. You can kind of treat it like an array. Kind of.
I really recommend querying the server for the data you need though. It's a world simpler.
dim rs as ADODB.Recordset
Set rs = QuoteList.Recordset
rs.MoveFirst
Do Until rs.EOF
' loop through recordset doing something
rs.MoveNext
Loop

Using DataSets in vb.net

When looping through a DataSet why do you have to user the "(0)"??? Every time I dont use it I get an error, so I have learned use it but not really sure why? Im just trying to get a better understanding of why this is used. Here is an example below:
for each row as datarow in ds.tables(0).rows
next
'******Note: If i use this I get an error ********************
for each row as datarow in ds.tables.rows
next
And also another question comes to mind, with regards to this question is that locally through visual studio 2010 i use that for each statement and it works as expected, but if I use that for each on a production server, if there are no records i get an error, so what I have to do is do a record count, then if ok then go ahead with the for each. Is there anyway around that?
Because your ds.tables is an array of tables, and 0 is the first index of the tables array. It doesn't work in the second scenerio because you aren't defining an index, which is required to access a specific element within your array.
The issue with question one is that the dataset can hold many tables, so you need to either supply an index or table name to access the rows in that table. I am not sure why there is a difference between locally and production, it should act the same.
Wade

How update a SQL table from a modified datatable?

I used the DataSet Designer to create FTWDataSet which holds the AlarmText table from a SQLExpress database. This far my form contains ONLY Datagridview1. The code below successfully shows the contents of the AlarmText table plus the one added checkbox column (which I will populate with display-only data, and is not an issue here).
Dim ta As New FTWDataSetTableAdapters.AlarmTextTableAdapter
Dim dt As New FTWDataSet.AlarmTextDataTable
ta.Fill(dt)
DataGridView1.DataSource = dt
'create a new Bool column in the datatable
dt.Columns.Add("NewCol", (New Boolean).GetType)
What else do I need to do to use the DataGridView to edit and save values in the AlarmText table?
Here's a brief MSDN walkthrough on this topic.
Some notes:
You shouldn't need a binding source to persist changes back to the database.
To make the table adapter accessible to other procedures in your form, make it a form-scoped (a.k.a. member) variable instead of method-scoped, as in your example.
If you created your Dataset using the Dataset Designer, and you're fetching your original data from anything more than a simple table or view, then your adapter won't know how to update anything in the original database. You have to manually configure the UPDATE command in this situation. For reference, see the TableAdapter Update Commands section in the above link.
I should also mention that I avoid TableAdapters in ADO.Net like the plague. In theory they're very convenient and powerful. In practice, many of them (especially the one for the Oracle provider) are buggy, and if they don't work exactly right you're totally screwed.
If you're pulling from just one underlying table, the adapter should work fine (so ignore my nihilistic advice for the time being). It may be that adding an additional column in code will breaks the adapter (since there's no corresponding column in the database table).

access report from dynamic crosstab query and vba to "manually" generate reports

I have come across the problem of generating complex access reports (by complex I mean with data processing, variable number of fields, among others).
Let me explain in deeper detail some of the things I need to implement:
Some fields should not show according to some values in a query
If a certain record does not exist, a nice colored (very noticeable) message should appear instead of the values that would be there (Suppose, for example, that a record with 03/04/2009 in the date field exists, a record with 03/06/2009 in the date field also exists but no record with 03/05/2009 exists. Before showing the data related to the last record, I should print something like "Didn't show up on 03/05/2009")
A bar chart that takes as data not the values in the records, but instead something else that is calculated over a set of records (like an average of all grades for a certain date). The number of series in this chart also varies according to values in the records, this chart would not be in the detail section, but instead in the page heading or some kind of group heading.
It should also be mentioned that the query is a TRANSFORM query (more precisely, an INNER JOIN of many TRANSFORM queries), and thus the number of columns returned by the query varies. While in the past I've been unable to bind this query as the recordsource for the report, somehow Access stopped complaining for now (can someone please clarify this? Is this normal, should I not worry about it and use it as a recordsource or should I avoid it?)
There are two options to achieve what I want (that I can see for now):
Create a report with no record source and lots of unbound fields, and through several events (Report_Open, Section_Format, etc.) and with the help of DAO, manually set the values of these fields. Changing the Data Series of the chart is also possible through VBA.
Set the record source to the query, and create some crazy and confusing VBA code to deal with the data and implement everything I need.
It seems to me that option 2 is going to be a huge headache and waste of time, and I recognize option 1 is pretty much like writing to an Excel file (since all the data is obtained with DAO), which would be much easier since I have much more control over almost everything there (but for many other reasons, we want everything in an access report)
While I'm biased and intend to go with option 1, I have found several problems with this option, for example:
I can't find a way to create new pages in the report with VBA, and thus I'm limited only to the first page.
Lack of some kind of free, online, decent and complete documentation on VBA and Access Reports
Also, if option 2 is more viable, I'm certainly willing to go with it, but I would also need some advice, and perhaps some tips to solving the problems I mentioned in this question.
So, the questions are:
Where can I find some decent and complete documentation on Access Reports and VBA?
How can I create pages in an access report, and choose which page I want to write to?
With the problem I have in my hands, will I reach any bottlenecks I should know about? Should I already be thinking of alternatives to Access Reports (writing to a spreadsheet, for example?)
Sounds like you want to dynamically create the report and avoid all the dummy text boxes.
In regard to:
I can't find a way to create new pages
in the report with VBA, and thus I'm
limited only to the first page.
Your solution #1 seems to assume an unbound report.
I think what I'd do is have the form the crosstab as the rowsource, so you'd have records to generate the pages, and then define your report's controls with no ControlSource (except for the controls that are bound to fields that are always present in the CrossTab). Then you could assign the ControlSources at runtime based on the particular columns. Here's the SQL for a crosstab grabbed from an app I'm working on now:
TRANSFORM First(impNoMatch.PersonID) AS FirstOfPersonID
SELECT impNoMatch.LastName, impNoMatch.FirstBame
FROM impNoMatch
GROUP BY impNoMatch.LastName, impNoMatch.FirstName
PIVOT impNoMatch.Status;
Now, you know that the fields in the SELECT clause will always be present, so if you opened a recordset on the SQL string you are using and count the number of fields in the recordset's Fields collection (you can't use the report's Recordset unless it's an ADO recordset, i.e., not bound to the Recordsource):
Dim strSQL As String
Dim rsFields As DAO.Recordset
Dim lngFieldCount As Long
strSQL = Me.Recordsource
Set rsFields = CurrentDB.OpenRecordset(strSQL)
lngFieldCount = rsFields.Fields.Count
From that, since you know the number of fields in the SELECT statement (i.e., the row headings), you can calculate the number of dynamic controls you want to assign, and you can use this recordset's fields collection to assign the ControlSources and unhide the controls.
You'd start out with all your controls that will display the dynamic fields set so their Visible property is FALSE. You'd also use a naming convention for those controls. In the code below, I've used txtNN, where NN is the numeric index in the Fields collection formatted as 2 digits. Here's the code (which adds lines to what's listed above, and is run in the OnOpen event):
Dim strSQL As String
Dim rsFields As DAO.Recordset
Dim lngFieldCount As Long
Dim l As Long
Dim strControlName As String
strSQL = Me.RecordSource
Set rsFields = CurrentDb.OpenRecordset(strSQL)
lngFieldCount = rsFields.Fields.Count
For l = 2 To lngFieldCount - 1
strControlName = "txt" & Format(l, "00")
Me(strControlName).ControlSource = rsFields.Fields(l).Name
Me(strControlName).Visible = True
Next l
rsFields.Close
Set rsFields = Nothing
Now, if you want to get fancy, you can reformat the controls, changing widths and horizontal/vertical position. If you do that, you have to do it in a different event, and it's a bit tricky to choose that. The only good place to put it is in a report group's header's OnFormat event. If you don't have any grouping, you can add one that doesn't do anything. In the case of my crosstab, a two-level sort on Lastname and Firstname and a header on the Firstname group with nothing in it is a good place to use the OnFormat event to change the appearance/layout of the controls on your report.
As to your question about how to learn how to do this, I recommend picking up an intermediate/advance Access programming book. The Access Developers Handbook is the gold standard on this, and includes tons of examples of programmatic control of reports.