From the July 2002 issue of MSDN Magazine

MSDN Magazine

Viewing the Values of a DataSet in a Debug Window

Download the code for this article: AdvancedBasics0207.exe (61KB)

Q While reading your column on debugging in the April issue, I realized I need to view the values of a DataSet in the debug window. It's too time-consuming to navigate the tree to find the value of a particular cell. Is there an easier way for this to be done?
A** To demonstrate one approach to this problem, I took a program I created for the April column, did some tweaking, and added some debug code (see the Form_Load event in Figure 1).
      Suppose you create a DataSet with one or more tables, as the code in Figure 1 does. Then you test the application, but the DataGrid does not show any values. What do you do?
      There are several options. The easiest way I have found to inspect the DataSet and its values is by using either the Command Window or Debug.Write. You can also expand the DataSet object in the Autos or other debug windows to access an individual value. Take a look at the debug window option first.
      I placed a breakpoint in the Form_Load event shown in Figure 1 on the line that sets the DataSet. Then I ran the application and stepped over this line. At this point, I could access the DataSet object. This is where it got interesting. Because the DataSet is a rich object, it has a vast hierarchy of properties and collections. While it took some significant browsing to find exactly the right place in this hierarchy where the values I wanted were living, it is possible. As you can see, Figure 2 shows the results from the Autos window.
      The following code gives you a good idea of the depth of the hierarchy that I drilled through in order to find the data in each column. The first zero in the hierarchy specifies Table 0, and the last zero specifies the first row. You can see this row at the top of Figure 2 (+0).

  ds.Tables.Tables.List._items(0).list._items(0).ItemArray(0)
  

 

The nice thing about this view is that you can look at all the data and even browse through different rows by expanding and collapsing them. The problem is you may forget the hierarchy the next time you need to do this and you will not be able to quickly repeat the steps in the same order.
      Take a look at the Command Window for another approach. To use the Command Window, I can place a breakpoint on the line just after the DataSet is created or a new table is added to it. For instance, in the code in Figure 1, the breakpoint is set on this line:

  ds = RunSQLWithDataSet(sSQL,"Employees")
  

 

      The code for RunSQLWithDataSet is shown in Figure 3. When you run the app and the debugger stops on this line, you can press F10 to execute it. Then switch to the Command Window. Let's say you want to know how many records are in a table. You can use this command sequence in the Command Window:

  >? ds.tables(0).rows.count
  

 

Since the command returns a row count of 9, it's clear that there is data in the DataSet. Now, let's look at the individual cells (columns) that were in question. One way to handle this is to use the Command Window again and simply enter the following command into it:

  >? ds.tables(0).rows.item(2).item(0)
  

 

As you can see, this command shows the data in the first field in the third row. You should take a look at the following command sequence I entered to inspect several fields in the first row:

  ? ds.tables(0).rows.item(0).item(0)
1 {Integer}
[Integer]: 1 {Integer}
>? ds.tables(0).rows.item(2).item(2)
"Janet" {String}
String: "Janet"
>? ds.tables(0).rows.item(2).item(3)
"Sales Representative" {String}
String: "Sales Representative"
>

 

      These examples use the ordinal position of the columns and rows, but you can use the name of a field if you like:

  >? ds.tables(0).rows.item(2).item("Title")
  

 

      At this point you may be thinking that you have to remember the object hierarchy used in these examples, too. While this is true, you can at least make it easy. If you are debugging an application and frequently need to access properties of a DataSet, you can at least save the commands. For instance, I took some of these commands and pasted them into the app as comments. Now if I need one, I can copy it to the Command Window and execute it:

  'Dataset debug commands:
'? ds.tables(0).rows.count
'? ds.tables(0).rows.item(0).item(0)

 

      Would these commands be useful for many applications? Probably. To make them easier to use, I copied the commented commands to the ToolBox and renamed the item "DS Debug Cmds—Command Window." Now I can drag them into any application and pop them back into the Command Window. If the items are descriptively named in the ToolBox, it will be much easier to find and access them.
      Let's look at another way to solve your problem. You could create a routine to display the values in several columns (see the debug section in Figure 1). This code first labels the output with dotted lines and adds a tag for the DataSet as the header and footer. Then it writes five values out to the Output Window. With this code, you can simply run the application, and the values show up in the Output Window. This is easier than executing commands again in the Command Window, and the code stays in the application throughout the debug process.
      What about an approach that does this generically? For instance, it would be nice to have a single function that takes a DataSet and displays all the values in a particular table in the Output Window. I created the function in Figure 4 to do just that. You can place this function in a module within a project; then all you need to do to view the fields in a table is call it like this:

  'Show table in output window
ShowDSInOutputWindow(ds, "Employees")

 

The output for the first three rows is shown in Figure 5.
      As you can see, the data in each column is now easily discovered. I could even turn this data into a Microsoft® Word table or dump it into a spreadsheet for more analysis. Suppose you were performing calculations and needed to verify them. You could use the ShowDSInOutputWindow function to obtain the data, load it into Microsoft Excel, and analyze your calculations.
      In Figure 4, you can see how the code uses nested For Each loops to process the DataSet table as in the last example. The inner For Each loops process the columns in each row. The second For Each outputs the column names and the last For Each outputs the data.
      Also notice how the code outputs the table name before it starts the output of each column:

  Debug.WriteLine(vbTab & vbTab & "DS Table: " tbdebug.ToString())
  

 

      If you name your tables descriptively you can now debug them quite easily using this routine.

Wrap-up

      The answer to this month's question revealed several things. First, the debugging windows in Visual Studio® .NET can be used to explore the data in an object at run time. Second, the Command Window can be used to further explore things at run time and even reduce repetitive testing by saving commands. Finally, subroutines can be created to handle this automatically, allowing you to build a library of tools to improve application debugging.
      This functionality can also be extended quite easily. For instance, you may want to examine column values after the application is in production. You have several options. You can output the DataSet as XML. Or you can use the Trace class instead of the Debug class, which will allow you to capture the Trace output when the application is running. You can easily place a switch to turn tracing on or off in a file or other data store so you can automatically generate trace data at run time. Of course, you can customize this by outputting trace data to the event log or other listeners.
Send questions and comments for Ken to basics@microsoft.com.**

Ken Spencer works for 32X Tech (https://www.32X.com) which provides training, software development and consulting services on Microsoft technologies.