As promised , we will explain the difference between typed and untyped DataSets. Another way of saying typed and untyped is to say strongly typed and weakly typed, terminology I prefer because it more aptly describes what is going on. The terms are used interchangeably in Microsoft documentation.
Typed (strongly typed) means the programming language strictly enforces data types. Visual Basic is an example of a strongly typed language.
Untyped (weakly typed) means you can assign any variable to any other variable. C is an example of a weakly typed language.
In C you can pretty much assign any variable to any other variable without the compiler complaining, no matter the data types of the variables .
For example, C does not complain if you assign a char to an int. Visual Basic normally requires that the data types be compatible when they are assigned. If you try to assign the string variable to a numeric variable the compiler will usually complain. However, Visual Basic is normally not strictly typed. Strict typing means that you can only assign the string to a string and a numeric to another numeric of equal or greater scale. In Visual Basic you can turn on strict typing with an Option statement.
When we talk about a strongly typed DataSet, we are talking about something a little different. A strongly typed DataSet is a class that derives from a DataSet. It inherits all the properties, methods , and events of the DataSet class, plus it adds its own so that we can access its members by name instead of just by index.
Suppose a typed DataSet has a table named Customers. We can access the Customers table by a property called Customers instead of by the Tables collection. The advantage of this is that the Customers table already has all of the column objects and row objects defined. Check out this snippet of code:
Dim cust As DataSet1.CustomersDataTable Dim myrow As DataSet1.CustomersRow Dim str1, str2 As String cust = DataSet11.Customers() myrow = DataSet11.Customers.Rows(0) str1 = myrow.Address str2 = cust.AddressColumn.Caption
Notice how we're able to address the tables and rows by name. This is similar to early and late binding in COM. Since the Customers DataTable property is derived from the DataTable, we can assign it to a variable that is declared as a DataTable or as the more restrictive type of Customers DataTable.
When we create a strongly typed DataSet, the wizard generates a new class derived from the DataSet class and by default names it DataSet1. It then looks at the results of the queries in the Data Adapter and creates DataTable objects and classes. It then creates properties and methods for manipulating the classes directly. You can see the VB module it generates if you click the Show All Files button in the Project Explorer.
Underneath the XSD file for the DataSet will be the VB module. You can open this and look at the code, but do not modify it because this will confuse the system. If you have an XSD file from another source you can generate your VB module using a command-line utility. This utility parses the XSD file and creates the DataSet class for you. You can also do this programmatically using the emitter classes but this is beyond the scope of this book. This utility is appropriately called xsd.exe and the simple invocation is xsd <xsd file name>. You can use it to generate VB code or C# code via command-line switches (execute xsd.exe /? to view all of the available options).
Let's create a screen similar to the screen we had in the previous project. This screen should contain a grid control, three buttons , and the search text box. In addition place two additional text boxes and another button on the next row. The form should look like Figure 7.4.
Figure 7.4. The DataSet demo form.
You can use the same form as we did in the previous chapter; just copy it into the new directory and edit your project. Make sure you set the startup project to be the new project, and then set the startup form to be the new form. We will use the Show button to demonstrate a little bit of what we've been talking about with typed DataSets. Copy the following code into your project.
Private Sub btnRefresh_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnRefresh.Click Dim mDS As ADOBook07_01.DataSet1 Try mDS = DataSet11 If mDS.Tables.Count > 0 Then mDS.Clear() End If SqlDataAdapter1.SelectCommand.Parameters("@CoName").Value = txtSearch.Text SqlDataAdapter1.Fill(mDS) DataGrid1.DataSource = mDS.Tables(0) Catch errobj As Exception MsgBox(errobj.Message & vbCrLf & errobj.StackTrace) End Try End Sub Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click Dim cust As DataSet1.CustomersDataTable Dim rows As DataSet1.CustomersRow Dim i As Integer cust = DataSet11.Customers() i = Me.BindingContext(DataSet11.Customers).Position rows = DataSet11.Customers.Rows(i) TextBox1.Text = rows.Address TextBox2.Text = cust.AddressColumn.Caption End Sub
Pay special attention to the Button1_Click event. Notice how we reference the objects in the DataSet by name. It makes the code much more readable than using arcane indices. When you click the button, the current value of the address column should appear in the text box. You can navigate through the grid control and you will always get the value of the current row. We will explain DataTable navigation in depth later.
Now let's have a look at the untyped version of the same code.
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click Dim i As Integer i = Me.BindingContext(DataSet11.Tables(0)).Position TextBox1.Text = DataSet11.Tables(0).Rows(i)("Address") TextBox2.Text = DataSet11.Tables(0).Columns("Address").Caption End Sub
Notice how we can access all the properties via the indexes instead of the names. This can be useful when you do not know in advance what your SQL statement will look like. An ad hoc Query Builder would be an example of where this would be very useful.
DataSet Properties and Methods
The DataSet contains methods that operate on the DataSet as a whole. The two Collection properties, Tables and Relations, reference the DataTable collection and Relation collection. The following are other methods worth noting:
ReadXML and WriteXML These methods read and write XML text to and from disk files. ReadXML can also be used to read XML from a string source.
ReadXMLSchema and WriteXMLSchema These methods read and write XSD schema text to and from disk files. ReadXMLSchema can also be used to read XSD text from a string source.
GetXML and GetXMLSchema Methods which a programmer can use to retrieve the XML text into a string variable.
InferXMLSchema Will try to create a schema from data read from a file. For example, a comma-separated values file can infer a schema where each value is a column.
Clear Clears out the data in the tables but does not clear the schema information.
EnforceConstraints When loading data through one of the ReadXML methods, tells the DataSet to enforce or ignore constraints such as primary keys or relations.
Clone Copies the schema information only (no data) from an existing DataSet to a new DataSet.
Copy Copies the schema and data from an existing DataSet to a new DataSet.
AcceptChanges, RejectChanges Tells the DataSet to either accept or cancel any changes that were made since the last call to Accept or Cancel changes.
HasChanges Returns True if any data in any table in the DataSet has changed since the last call to Accept or Cancel changes.
GetChanges Returns another DataSet object with only those rows and tables that have changed.
Let's look at some code examples. These examples will demonstrate the XML Related methods. We will modify our DataSet demonstration project. Change the Text property of Button2 to read XML. Create another button and change its Text property to read Schema. Delete the text box named TextBox2. Set the properties of TextBox1 so that multiline is True and scroll bars are set to both. You will also have to change the wordwrap property to False. When you're finished your form should look like Figure 7.5.
Figure 7.5. The modified DataSet demo form.
Now let's update click events for the two buttons. Place the following code in the Button2_Click event. Erase any code that was already there.
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click TextBox1.Text = DataSet11.GetXml End Sub
Place the following code in the Button3_Click event. Erase any code that was already there.
Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click TextBox1.Text = DataSet11.GetXmlSchema End Sub
Now when you run the project, if you click the XML button you'll see the XML text appear in the text box. Likewise, if you click the Schema button you'll see the XSD appear in the text box. Your project should look like Figure 7.6 if you have clicked the Schema button.
Figure 7.6. The demo with the XSD displayed.
Now let's see how to read and write XML data to and from a disk file. Let's modify our form again. This time we will use the XML button to write the data and the Schema button to read the data. We will make the Show button into a Clear button so that we can erase the data in a DataSet and reload it from disk without shutting down and restarting the program. Make your form look like Figure 7.7.
Figure 7.7. The revised form for reading and writing XML.
Now let's add two common dialog controls to the form. Add one Save File dialog, and add another File Open dialog. These can be found in the Windows Forms tab of the toolbox.
VB 6 VB 6 veterans should remember the common dialog control. This control served as a template for all of the styles of common dialog. Besides the File Open and Save As versions, there are also color choosers, printer choosers, and font choosers . These were handled by a method call in the VB 6 version of the control. In the .NET version, there are separate components for each type of dialog.
Change the code in the buttons' click events to look like the following.
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click DataSet11.Clear() End Sub Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click Dim strFile As String Try SaveFileDialog1.ShowDialog() strFile = SaveFileDialog1.FileName If strFile = "" Then Exit Sub DataSet11.WriteXml(strFile) Catch errobj As Exception MsgBox(errobj.Message) End Try End Sub Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click Dim strFile As String Try OpenFileDialog1.ShowDialog() strFile = OpenFileDialog1.FileName If strFile = "" Then Exit Sub DataSet11.ReadXml(strFile) DataSet11.AcceptChanges() Catch errobj As Exception MsgBox(errobj.Message) End Try End Sub
With this code, the program will prompt the user for a file name using a common dialog display. Then the program will save the contents of the DataSet to the file or read the contents of the file back into the DataSet. When we read the file back into the DataSet the grid control will automatically be populated with the contents of the file. Note the presence of the call to the AcceptChanges method. This is necessary when reading XML from a file because all rows are marked as added. If you call the Update method of the DataAdapter you will get duplicate key errors returned.
There are some things to take note of. If you sort the grid by clicking on a column heading, it has no effect on the order of records in the XML file. Likewise for any sorts or filters you place on the default DataView of the DataTable. (We will look at the DataView later on.) If you want to limit the output of the XML methods, you must change the SQL query that read the data from the database.