Using Strongly Typed DataSet Objects
Strongly typed DataSet objects simplify the development process, making it easier to write code to access and modify the contents of your DataSet. Let's look at a few examples that compare working with data in a standard untyped DataSet vs. working with a strongly typed DataSet.
Adding a Row
Each class for the DataTable objects in your DataSet offers two ways to add a new row to the DataTable. Calling New<TableName>Row returns a new strongly typed DataRow for your DataTable. You can then set values for columns in the row using properties of the strongly typed DataRow,as shown in the following code snippet.
Visual Basic .NET
Dim ds As New Chapter9() Dim tblCustomers As Chapter9.CustomersDataTable = ds.Customers Dim rowCustomer As Chapter9.CustomersRow rowCustomer = tblCustomers.NewCustomersRow() rowCustomer.CustomerID = "ABCDE" rowCustomer.CompanyName = "New Company" rowCustomer.ContactName = "New Contact" rowCustomer.Phone = "(800) 555-1212" tblCustomers.AddCustomersRow(rowCustomer) 'Instead of Dim rowCustomer As DataRow = tblCustomers.NewRow() rowCustomer("CustomerID") = "ABCDE" rowCustomer("CompanyName") = "New Company" rowCustomer("ContactName") = "New Contact" rowCustomer("Phone") = "(800) 555-1212" tblCustomers.Rows.Add(rowCustomer)
Visual C# .NET
Chapter9 ds = new Chapter9(); Chapter9.CustomersDataTable tblCustomers = ds.Customers; Chapter9.CustomersRow rowCustomer = tblCustomers.NewCustomersRow(); rowCustomer.CustomerID = "ABCDE"; rowCustomer.CompanyName = "New Company"; rowCustomer.ContactName = "New Contact"; rowCustomer.Phone = "(800) 555-1212"; tblCustomers.AddCustomersRow(rowCustomer); //Instead of DataRow rowCustomer = tblCustomers.NewRow(); rowCustomer["CustomerID"] = "ABCDE"; rowCustomer["CompanyName"] = "New Company"; rowCustomer["ContactName"] = "New Contact"; rowCustomer["Phone"] = "(800) 555-1212"; tblCustomers.Rows.Add(rowCustomer);
The benefits of using the strongly typed DataRow in this example are not clear when you see the code in print. Seeing this code in the Visual Studio .NET development environment will provide a better demonstration.
Figure 9-3 is a snapshot of the development environment. It shows that the columns of the strongly typed DataRow are easily accessible through statement completion. I was thrilled when Visual Basic introduced statement completion in version 5. However, if I made a mistake typing the name of a column in my Visual Basic 6 and ADO 2.x code, I would not discover that mistake until I ran my application. Strongly typed DataSet objects and statement completion can all but eliminate such problems in the development cycle.
Figure 9-3
Using a strongly typed DataSet in code in Visual Studio .NET
Like the Add method of the DataRowCollection, the Add<TableName>Row method of the strongly typed DataTable is overloaded. The following code snippet uses the Customers DataTable in the strongly typed DataSet we created earlier in the chapter.
Visual Basic .NET
Dim ds As New Chapter9() Dim tblCustomers As Chapter9.CustomersDataTable = ds.Customers tblCustomers.AddCustomersRow("ABCDE", "New Company", _ "New Contact", "(800) 555-1212") 'Instead of tblCustomers.Rows.Add(New Object() {"ABCDE", "New Company", _ "New Contact", "(800) 555-1212"})
Visual C# .NET
Chapter9 ds = new Chapter9(); Chapter9.CustomersDataTable tblCustomers = ds.Customers; tblCustomers.AddCustomersRow("ABCDE", "New Company", "New Contact", "(800) 555-1212"); //Instead of tblCustomers.Rows.Add(new object[] {"ABCDE", "New Company", "New Contact", "(800) 555-1212"});
Again, thanks to IntelliSense and statement completion, this is one of those features that looks more impressive as you write code. In the Visual Studio .NET development environment, the names of the parameters of the method appear as you type, so you don't have to jump back to other parts of your code to figure out the names and order of your columns.
Finding a Row
With a standard untyped DataSet, you can use the Find method of the DataTable object's Rows collection to locate a particular row based on its primary key value(s). Using that Find method can be confusing, especially if the DataTable has a multi-column primary key. For example, the Order Details table in the Northwind database uses the OrderID and ProductID columns as its primary key. Code that uses the Find method for a corresponding DataTable would therefore look like this:
Visual Basic .NET
Dim tblDetails As DataTable Dim rowDetail As DataRow rowDetail = tblDetails.Find(New Object() {10245, 7})
Visual C# .NET
DataTable tblDetails; DataRow rowDetail; rowDetail = tblDetails.Find(new object[] {10245, 7});
This code works, but it can be confusing to write. More important, it is difficult to read, which makes it challenging to maintain.
Each DataTable class in a strongly typed DataSet exposes its own Find method if the DataTable has a primary key defined. If we add a DataTable for the Order Details table to our strongly typed DataSet, we can replace the previous code snippet with this one, which is much easier to write and maintain.
Visual Basic .NET
Dim ds As New Chapter9() ... Dim tblDetails As Chapter9.Order_DetailsDataTable = ds.Order_Details Dim rowDetail As Chapter9.Order_DetailsRow rowDetail = tblDetails.FindByOrderIDProductID(10245, 7) If rowCustomer Is Nothing Then Console.WriteLine("Row not found!") Else Console.WriteLine("Found " & rowDetail.OrderID & " - " & _ rowDetail.ProductID) End If
Visual C# .NET
Chapter9 ds = new Chapter9(); ... Chapter9.Order_DetailsDataTable tblDetails = ds.Order_Details; Chapter9.Order_DetailsRow rowDetail; rowDetail = tblDetails.FindByOrderIDProductID(10245, 7); if (rowDetail == null) Console.WriteLine("Row not found!"); else Console.WriteLine("Found " + rowDetail.OrderID.ToString() + " - " + rowDetail.ProductID.ToString());
Editing a Row
Editing rows in a strongly typed DataSet is similar to editing rows in a standard DataSet. You still have the option of using the BeginEdit, EndEdit, and CancelEdit methods. However, you can access the values of columns of the DataRow using properties of the strongly typed DataRow, as shown here.
Visual Basic .NET
Dim ds As New Chapter9() OleDbDataAdapter1.Fill(ds) Dim rowCustomer As Chapter9.CustomersRow = ds.Customers(0) rowCustomer.CompanyName = "Modified" 'Instead of rowCustomer("CompanyName") = "Modified"
Visual C# .NET
Chapter9 ds = new Chapter9(); OleDbDataAdapter1.Fill(ds); Chapter9.CustomersRow rowCustomer = ds.Customers[0]; rowCustomer.CompanyName = "Modified"; //Instead of rowCustomer["CompanyName"] = "Modified";
Working with Null Data
During the Visual Studio .NET beta, several newsgroup threads were concerned with null values. Many developers were confused about how to set the value in a column to null or how to determine whether a column contains a null value. In Chapter 6, you saw that you can use the DataRow object's IsNull function to check for null values and use System.Convert.DBNull to assign null values to columns.
Strongly typed DataSet objects also make working with null values easier. Each strongly typed DataRow also includes two methods per columnone to check whether the column contains a null value and one to set the column's value to null. The following code snippet works with the ContactName column for a row, so these methods are named IsContactNameNull and SetContactNameNull.
Visual Basic .NET
Dim ds As New Chapter9() OleDbDataAdapter1.Fill(ds) Dim rowCustomer As Chapter9.CustomersRow = ds.Customers(0) 'Check to see whether the ContactName property is Null. If rowCustomer.IsContactNameNull() Then Console.WriteLine("Contact name is Null") Else Console.WriteLine("Contact name: " & rowCustomer.ContactName) End If 'Set the ContactName property to Null. rowCustomer.SetContactNameNull() 'Instead of If rowCustomer.IsNull("ContactName") Then ... 'And rowCustomer("ContactName") = Convert.DBNull
Visual C# .NET
Chapter9 ds = new Chapter9(); OleDbDataAdapter1.Fill(ds); Chapter9.CustomersRow rowCustomer = ds.Customers[0]; //Check to see whether the ContactName property is Null. if (rowCustomer.IsContactNameNull()) then Console.WriteLine("Contact name is Null"); else Console.WriteLine("Contact name: " + rowCustomer.ContactName); //Set the ContactName property to Null. rowCustomer.SetContactNameNull(); //Instead of if (rowCustomer.IsNull("ContactName")) ... //And rowCustomer["ContactName"] = Convert.DBNull;
Working with Hierarchical Data
The DataRow object exposes two methods that let you navigate through your hierarchical dataGetChildRows and GetParentRow. These methods require you to supply either the name of the DataRelation you want to reference or the object itself.
If your strongly typed DataSet contains DataRelation objects, the XML Schema Definition Tool will add methods that let you navigate through your hierarchical data without having to specify the DataRelation or its name. In the strongly typed DataSet we built, we added a DataRelation to relate the Customers and Orders DataTable objects. When we saved the changes to the DataSet object's .xsd file, the XML Schema Definition Tool added a GetOrdersRows method to the strongly typed DataRow class for the Customers DataTable and a GetCustomersRow method to the strongly typed DataRow class for the Orders DataTable.
The following code snippet uses the GetOrdersRows method to display all customers as well as the orders for each customer.
Visual Basic .NET
Dim ds As New Chapter9() OleDbDataAdapter1.Fill(ds) OleDbDataAdapter2.Fill(ds) Dim rowCustomer As Chapter9.CustomersRow Dim rowOrder As Chapter9.OrdersRow For Each rowCustomer In ds.Customers Console.WriteLine("Orders for " & rowCustomer.CompanyName) For Each rowOrder In rowCustomer.GetOrdersRows() Console.WriteLine(vbTab & rowOrder.OrderID & _ " - " & rowOrder.OrderDate) Next rowOrder Next rowCustomer 'Instead of Dim rowCustomer, rowOrder As DataRow For Each rowCustomer In ds.Tables("Customers").Rows Console.WriteLine("Orders for " & rowCustomer("CompanyName")) For Each rowOrder In rowCustomer.GetChildRows("CustomersOrders") ...
Visual C# .NET
Chapter9 ds = new Chapter9(); OleDbDataAdapter1.Fill(ds); OleDbDataAdapter2.Fill(ds); CustomersDataTable tblCustomers = ds.Customers; CustomersRow rowCustomer = tblCustomers[0]; foreach (Chapter9.CustomersRow rowOrder in ds.Tables["Customers"].Rows) { Console.WriteLine("Orders for " + rowCustomer.CompanyName); foreach (Chapter9.OrdersRow rowOrder in rowCustomer.GetOrdersRows()) Console.WriteLine("\t" + rowOrder.OrderID.ToString() + _ " - " + rowOrder.OrderDate.ToString()); } //Instead of foreach (DataRow rowCustomer in ds.Tables["Customers"].Rows) { Console.WriteLine("Orders for " + rowCustomer["CompanyName"]); foreach (DataRow rowOrder in rowCustomer.GetChildRows("CustomersOrders")) ... }
Other DataSet, DataTable, and DataRow Features
The classes that the XML Schema Definition Tool generates are derived from the DataSet, DataTable, and DataRow classes. This means you can also treat the classes as their untyped counterparts.
For example, the strongly typed DataSet classes do not have their own methods for reading and writing XML data and schema information. But because the classes are derived from the DataSet class, they still expose methods such as ReadXml and WriteXml. So for all other tasks, such as using a DataAdapter to retrieve data or submit changes, you can treat the strongly typed DataSet just like any other DataSet.