Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
(ebook) Visual Studio .NET Mastering Visual Basic.pdf
Скачиваний:
136
Добавлен:
17.08.2013
Размер:
15.38 Mб
Скачать

964 Chapter 22 PROGRAMMING THE ADO.NET OBJECTS

The Structure of a DataSet

The main object of any ADO.NET application is the DataSet object, which is s a miniature database that lives on the client. The main purpose of the Connection and DataAdapter objects is to populate the DataSet object, as well as move information from the DataSet to the database and update the underlying table(s). The basic concept behind ADO.NET is to move the required data to the client, process them there, and then, optionally, update the database with the changes made by the client application to the local data. The data on the client is a copy of the data on the server the moment the DataSet was generated, and the DataSet is totally disconnected from the underlying tables in the database.

The structure of the DataSet object is quite simple. VB6 programmers will have to get used to living with client-side data, but those of you new to VB.NET will find the DataSet a convenient method of working with subsets of tables. It’s made up of tables, which may or may not correspond to tables of the database. You can bring in an entire table, like the Categories table. Or you can select a few columns and/or a few rows from a table in the database and store them to a table in the DataSet. Finally, you can create a table by combining rows from multiple tables. For example, you can execute a query that retrieves all product names from the Products table along with the name of the category they belong to from the Categories tables and stores the returned rows to a new table in the DataSet. It is also possible to add and drop tables from a DataSet at any time during the course of the application.

Finally, you can create new tables from within your code. To do so, we create a DataTable object to represent the new table and then a series of DataRow objects to represent the table’s rows. Each row must have a data type, an optional default value, a length, and so on. You can create the same table structures from within your code as you would do with the visual tools of Enterprise Manager. After specifying the structure of the tables, you can add relations between them. Everything you can do visually with the XSD Designer, you can also do from within your code. The process of establishing relations between tables using the Designer was described in the last chapter. The product documentation discusses extensively how to create DataSets programmatically. You will find this information useful in situations where the data comes from someone else’s database. As you will see later in this chapter, it is possible to create a DataSet programmatically, populate it, and then store its contents to a XML file. This chapter shows you how to use DataSets to manipulate databases, which is what most of us will be doing anyway.

If the tables were totally independent of one another, the DataSet would be nothing more than a collection of arrays. Yet it’s not. In addition to the tables, the DataSet maintains relations between tables, as you have seen. If you need to see the product names along with the names of the categories they belong to, you can bring both tables into the DataSet and establish a relationship between them.

DataSets are not aware of the data sources. The exchange of information between DataSets and data sources (which, in most cases, are databases) takes place through the Data Adapter object, described in Chapter 21. Each table in a DataSet has an associated Data Adapter object, whose function is to retrieve data from the underlying table and populate the DataSet, as well as update the underlying table in the database with data from the DataSet.

The DataSet’s structure and its data are described with XML keywords. You don’t have to learn much about XML, but if you open the XSD file, you’ll see the XML code that describes the structure of the DataSet. The data aren’t available at design time, but once you populate the tables of the

Copyright ©2002 SYBEX, Inc., Alameda, CA

www.sybex.com

THE STRUCTURE OF A DATASET 965

DataSet, the data will also be described in XML. The Data Adapter formats the data as XML documents and passes them to the DataSet. It also receives data in XML format from the DataSet and updates the underlying tables in the database.

Navigating the Tables of a DataSet

In this section, we’re going to take a closer look at the structure of the DataSet and how it organizes data in tables. You’ll learn how to enumerate the tables in the DataSet and how to iterate through their rows and access the values of individual columns. The information in this section will enable you to write applications for displaying data. Later in the chapter, we’ll take a closer look at techniques for updating the tables in the data source.

The DataSet object exposes members for accessing its contents. The tables in a DataSet are exposed through the Tables collection, which is made up of DataTable objects. If the tables are related, the relations are exposed by the Relations collection, which is made up of DataRelation objects. The following two loops print the names of the tables and relations in a DataSet:

Dim tbl As System.Data.DataTable

For Each tbl In AllOrders1.Tables

Console.WriteLine(tbl.TableName)

Next

Dim rel As System.Data.DataRelation

For Each rel In AllOrders1.Relations

Console.WriteLine(rel.RelationName)

Next

The DataTable object’s most important property is the Rows property, which is a collection of DataRow objects. The DataRow object, in turn, exposes the Item property, which is the value of a specific column (field) in a row. If the DSCustomers DataSet contains the Customers table, the following statement returns the CustomerID field of the third row in the Customers table:

DSCustomers.Tables(“Customers”).Rows(2).Item(“CustomerID”)

There’s a simpler expression for retrieving the same value, which is the following:

DSCustomers.Customers(2).CustomerID

Obviously, the second expression is easier to write and makes your code easier to read and maintain. However, this notation applies only to a typed DataSet—one with a schema available at design time. The DataSet is based on existing tables, so that their structures are known at design time and the compiler can create typed DataSets. If you create a DataSet at runtime from within your code, then the compiler has no way of knowing the structure of the DataSet and therefore can’t create a typed DataSet. This chapter deals with typed datasets only.

If you’re wondering how the compiler knows about the structure of the DataSet and how it can expose the names of the tables as members of the DataSet object, or the names of the columns as members of the DataTable object, the answer is quite simple. It creates a class for every DataSet and for every table you add to the DataSet. This is done on the fly, and all the information is hidden from you. To view the class that implements the DataSet, click the Show All Files button on the Solution Explorer. Under the XDS file with the DataSet’s schema, there’s another file with the same name and extension VB. If you open this for a DataSet that contains the Customers table, you will

Copyright ©2002 SYBEX, Inc., Alameda, CA

www.sybex.com

966 Chapter 22 PROGRAMMING THE ADO.NET OBJECTS

see that it contains the definition of the class DSCustomers, which in turn contains the classes CustomersDataTable and CustomersRow. All these classes expose their own members, which you can access through the DSCustomers1 object. DSCustomers1 is an instance of the DSCustomers class.

Using the DataTable, DataRelation, and DataRow objects, you can navigate through all the tables of a DataSet, going from each row of any given table to the related rows in any other table. Let’s say you have created a DataSet with the Customers, Orders, and Order Details tables. Obviously, data binding is not the only way to build a user interface for browsing or editing a DataSet. You can write code to display the current row’s fields to a TextBox control and store the edited values back to the same row. You can also write code to navigate through the rows of a table or through the tables of a DataSet. Let’s look at an example of navigating through the tables of a DataSet and reading their fields with code.

VB.NET at Work: The Datasets Project

The Datasets project, whose main form is shown in Figure 22.1, can display the names of all tables in a DataSet, the names of all relations in the DataSet, and finally all the data in the DataSet. The Show Tables button displays the names of all the tables in the DataSet to the top-left ListBox control. The Show Relations button displays the names of all relations in the second ListBox control. The Show All Data button adds the customers, their orders, and each order’s details to a TreeView control. As you can see, the structure of the tree reflects the structure of the data in the DataSet.

Figure 22.1

The Datasets project demonstrates how to navigate through a DataSet and its tables.

The root nodes are customers (the parent table of the DataSet). Under each root node, you can find the customer’s orders (the IDs of the orders), and under each order you can find the order’s details. The nodes with the details contain the product ID, quantity, and price. You can modify the SELECT statement of the Order Details table so that it displays product names instead of product IDs, or include each order’s total. Or you can map the DataSet’s structure to a ListView control, which gives you more control over the appearance of the data. Listing 22.1 shows the code behind the Show Tables button, which iterates through the Tables collection of the AllOrders1 DataSet object. The DataSet was created by dropping the Customers, Orders, and Order Details tables of the Northwind database on the design surface, as described in the previous chapter. All three tables are stored in the same DataSet, and the relations between tables were established on the XSD file, as described in the previous chapter.

Copyright ©2002 SYBEX, Inc., Alameda, CA

www.sybex.com

THE STRUCTURE OF A DATASET 967

Listing 22.1: Navigating through the Tables of a DataSet

Private Sub Tables(ByVal sender As System.Object, _

ByVal e As System.EventArgs) Handles bttnTables.Click

ListBox1.Items.Clear()

Dim tbl As System.Data.DataTable

For Each tbl In AllOrders1.Tables

ListBox1.Items.Add(tbl.TableName)

Next

End Sub

The Show Relations button is quite similar; it iterates through the Relations collection of the DataSet and prints the names of the relations on the second ListBox control. Listing 22.2 shows the code behind this button.

Listing 22.2: Navigating through the Relations of a DataSet

Private Sub Relations(ByVal sender As System.Object, _

ByVal e As System.EventArgs) Handles bttnRelations.Click ListBox2.Items.Clear()

Dim rel As System.Data.DataRelation For Each rel In AllOrders1.Relations

ListBox2.Items.Add(rel.RelationName) Next

End Sub

When you select a relation by clicking its name, the program displays the properties of the selected relation in a message box. If you click the CustomerOrders relation, the following will appear on a message box:

PARENT Customers

CHILD Orders

ON CustomerID = CustomerID

The last button on the form displays the entire hierarchy of the DataSet on a TreeView control with the statements shown in Listing 22.3.

Listing 22.3: Navigating through the Rows of Related Tables

Private Sub Data(ByVal sender As System.Object, _

ByVal e As System.EventArgs) Handles bttnData.Click

DAcustomers.Fill(AllOrders1, “Customers”)

DAOrders.Fill(AllOrders1, “Orders”)

DAOrderDetails.Fill(AllOrders1, “Order Details”)

Dim CORelation As String

Copyright ©2002 SYBEX, Inc., Alameda, CA

www.sybex.com

968 Chapter 22 PROGRAMMING THE ADO.NET OBJECTS

CORelation = AllOrders1.Customers.ChildRelations(0).RelationName()

Dim ODRelation As String

ODRelation = AllOrders1.Orders.ChildRelations(0).RelationName

Dim row As AllOrders.CustomersRow

For Each row In AllOrders1.Customers

Dim Cnode As TreeNode

Cnode = TreeView1.Nodes.Add(row.CompanyName)

Dim OrderRows() As AllOrders.OrdersRow

OrderRows = row.GetChildRows(CORelation)

Dim Orow As AllOrders.OrdersRow

For Each Orow In OrderRows

Dim Onode As TreeNode

Onode = Cnode.Nodes.Add(Orow.OrderID)

Dim DetailRows() As AllOrders.Order_DetailsRow

Dim Drow As AllOrders.Order_DetailsRow

 

DetailRows = Orow.GetChildRows(ODRelation)

 

For Each Drow In DetailRows

 

Onode.Nodes.Add(Drow.ProductID & “

“ & Drow.Quantity & “ X “ & _

Drow.UnitPrice)

 

Next

 

Next

 

Next

 

End Sub

 

 

 

The code starts by populating all three tables of the DataSet. Then it retrieves the name of the first child relation of the Customer table (the CORelation string, which is the name of the relation between the Customers and Orders tables) and the first child relation of the Orders table (the ODRelation string, which is the relation between the Orders and Order Details tables). We’ll use the names of these relations to navigate from each row to its child rows.

Then the code starts scanning the rows of the Customers table with a For Each loop. In the loop’s body, it adds a new root node to the TreeView control with the customer’s name. Then it retrieves the child rows of the current row. The child rows are stored in an array of OrdersRow objects with the following statement:

Dim OrderRows() As AllOrders.OrdersRow

OrderRows = row.GetChildRows(CORelation)

The GetChildRows method of the DataRow object accepts as argument the name of a relation, and it retrieves the child rows related to the current row with the specified relation. The rows are stored in properly declared array or OrdersRow items.

Then the code goes through the child rows and, for each row, adds a child node under the current customer’s node in the TreeView control. As you can guess, it retrieves the current order’s child rows, which are the order’s details. The innermost loop adds these rows under the current order’s node. All three nested loops have the same structure, and you should examine the code to understand how it navigates through all the rows of all tables following the relations between tables.

Copyright ©2002 SYBEX, Inc., Alameda, CA

www.sybex.com