Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

C# ПІДРУЧНИКИ / c# / Hungry Minds - C# Bible

.pdf
Скачиваний:
240
Добавлен:
12.02.2016
Размер:
4.21 Mб
Скачать

{

databaseConnection.Close();

}

}

Data operations returning sets of rows

You already saw two ways of retrieving sets of rows when you examined how to retrieve a single row. Listing 23-16 uses a stored procedure to illustrate the retrieval of a set of rows. It uses a TOP 5 statement to keep the number of returned rows to an acceptable number. The only significant difference between Listing 23-17 and Listing 23-8 is the use of a while loop (instead of an if statement) to iterate through all the records.

Listing 23-16: A SQL Server Stored Procedure to Select a Set of Records

USE [Northwind] GO

CREATE PROCEDURE [pc_getCustomers] AS

SELECT TOP 5 [CustomerID], [CompanyName], [ContactName], [ContactTitle], [Address], [City], [Region], [PostalCode], [Country], [Phone],

[Fax]

FROM [Northwind].[dbo].[Customers]

Listing 23-17: Retrieving a Set of Records with a DataReader

static void TestSelectManyWithDataReader(string customerID)

{

// Set SQL statement strings

string strSQLSelect = "[pc_getCustomers]";

// Create OleDb objects

OleDbConnection databaseConnection = new OleDbConnection(oleDbConnectionString);

OleDbCommand selectCommand = new OleDbCommand(strSQLSelect, databaseConnection);

// We are dealing with a Stored Proc (i.e. NOT a SQL statement)

selectCommand.CommandType = CommandType.StoredProcedure;

try

{

// Establish database connection databaseConnection.Open();

// Execute SQL Command OleDbDataReader rowReader =

selectCommand.ExecuteReader();

// Report results while(rowReader.Read())

{

string contactName = rowReader["ContactName"].ToString();

string contactTitle = rowReader["ContactTitle"].ToString();

Console.WriteLine("Contact name is {0}, title is {1}.", contactName, contactTitle);

}

}

catch (Exception e)

{

Console.WriteLine("****** Caught an exception:\n{0}", e.Message);

}

finally

{

databaseConnection.Close();

}

}

Using a DataSet object to retrieve a set of records will equally look familiar (refer to Listing 23-9). Again, all that you need to do is add an iteration to catch all the retrieved records. This can be done with a for loop, as shown in Listing 23-18.

Listing 23-18: Retrieving a Set of Records with a DataSet

static void TestSelectManyWithDataSet(string customerID)

{

// Set SQL statement strings

string strSQLSelect = "[pc_getCustomers]";

// Create OleDb objects

OleDbConnection databaseConnection = new OleDbConnection(oleDbConnectionString);

OleDbCommand selectCommand = new OleDbCommand(strSQLSelect, databaseConnection);

OleDbDataAdapter dsCmd = new OleDbDataAdapter(); DataSet resultDataSet = new DataSet();

// We are dealing with a Stored Proc (i.e. NOT a SQL statement)

selectCommand.CommandType = CommandType.StoredProcedure;

try

{

//Establish database connection databaseConnection.Open();

//Execute SQL Command dsCmd.SelectCommand = selectCommand;

int numRows = dsCmd.Fill(resultDataSet, "Customers");

// Report results if (numRows > 0)

{

// numRows = resultDataSet.Tables["Customers"].Rows.Count for (int i=0; i<= numRows - 1; i++)

{

string contactName

=

resultDataSet.Tables["Customers"].Rows[i]["ContactName"].

ToString();

string contactTitle

=

resultDataSet.Tables["Customers"].Rows[i]["ContactTitle"].

ToString();

Console.WriteLine("Contact name is {0}, title is {1}.", contactName, contactTitle);

}

}

else

{

Console.WriteLine("No rows found!");

}

}

catch (Exception e)

{

Console.WriteLine("****** Caught an exception:\n{0}", e.Message);

}

finally

{

databaseConnection.Close();

}

}

Data operations affecting sets of rows

Data operations affecting a set of rows follow the same structure as operations affecting a single row. Listing 23-19 adds two new rows before calling the Update() command. A comparison with Listing 23-10 reveals no difference apart from the obvious addition of the coding that creates, sets, and adds the second row. Because of the similarities between the code for a single row and the code for multiple rows, this section does not repeat all the examples shown previously for Update and Delete statements affecting multiple rows.

Listing 23-19: Adding Two Records Through an Auto-Generated InsertCommand

static void TestAutoInsert2WithDataSet(string customerID1, string customerID2)

{

// Set SQL statement strings

string strSQLSelect = "EXEC [pc_getCustomer_ByCustomerID] @CustomerID_1='???'";

// Create OleDb objects

OleDbConnection databaseConnection = new OleDbConnection(oleDbConnectionString);

OleDbCommand selectCommand = new OleDbCommand(strSQLSelect, databaseConnection);

OleDbDataAdapter dsCmd = new OleDbDataAdapter();

//The following line is key to auto-generating statements!!!

OleDbCommandBuilder custCB = new OleDbCommandBuilder(dsCmd);

DataSet resultDataSet = new DataSet();

//We are dealing with a SQL statement (i.e. NOT a Stored

Proc)

selectCommand.CommandType = CommandType.Text;

try

{

//Establish database connection databaseConnection.Open();

//Execute SQL Command dsCmd.SelectCommand = selectCommand;

int numRows = dsCmd.Fill(resultDataSet, "Customers");

//Create a first new Row

DataRow workRow = resultDataSet.Tables["Customers"].NewRow();

// Fill in workrow data workRow["CustomerID"] = customerID1;

// 1

workRow["CompanyName"] = "Hungry Coyote Export Store";

// 2

workRow["ContactName"] = "Yoshi Latimer";

// 3

workRow["ContactTitle"] = "Sales Representative";

// 4

workRow["Address"] = "City Center Plaza 516 Main St.";

// 5

workRow["City"] = "Elgin";

// 6

 

workRow["Region"] = "OR";

// 7

 

workRow["PostalCode"] = "97827";

// 8

workRow["Country"] = "USA";

//

9

workRow["Phone"] = "(503) 555-6874";

 

// 10

workRow["Fax"] = "(503) 555-2376";

 

// 11

resultDataSet.Tables["Customers"].Rows.Add(workRow);

// Create a second new Row

workRow = resultDataSet.Tables["Customers"].NewRow();

// Fill in workrow data

// 1

workRow["CustomerID"] = customerID2;

workRow["CompanyName"] = "Hungry Coyote Export Store";

// 2

// 3

workRow["ContactName"] = "Yoshi Latimer";

workRow["ContactTitle"] = "Sales Representative";

 

// 4

workRow["Address"] = "City Center Plaza 516 Main St.";

// 5

workRow["City"] = "Elgin";

// 6

 

workRow["Region"] = "OR";

// 7

 

workRow["PostalCode"] = "97827";

// 8

workRow["Country"] = "USA";

//

9

workRow["Phone"] = "(503) 555-6874";

 

// 10

workRow["Fax"] = "(503) 555-2376";

 

// 11

resultDataSet.Tables["Customers"].Rows.Add(workRow);

//Reconcile changes with the data source dsCmd.Update(resultDataSet, "Customers");

//Report results Console.WriteLine("Inserted 2 rows.");

}

catch (Exception e)

{

Console.WriteLine("****** Caught an exception:\n{0}", e.Message);

}

finally

{

databaseConnection.Close();

}

}

Operations that return hierarchical data

A little known feature of ADO is that you can retrieve several sets of data in one pass. ADO.NET also has this feature. In the stored procedure listed in Listing 23-20, notice the two consecutive stored procedures. (Again, the output is limited to five records each for testing purposes.)

Listing 23-20: A SQL Server Stored Procedure with Two Select Statements

USE [Northwind]

GO

CREATE PROCEDURE [pc_getOrdersAndDetails]

AS

SELECT TOP 5

OrderID,

CustomerID,

EmployeeID,

OrderDate,

RequiredDate,

ShippedDate,

ShipVia,

Freight,

ShipName,

ShipAddress,

ShipCity,

ShipRegion,

ShipPostalCode,

ShipCountry

FROM Orders

SELECT TOP 5

OrderID,

ProductID,

UnitPrice,

Quantity,

Discount

FROM [Order Details]

GO

To retrieve this data, you use a DataReader in a similar fashion as you have done to retrieve a set of rows. However, if you compare the code in Listing 23-21 with the code in Listing 2317, you will notice an extra loop around the row iteration. This extra loops ends when NextResults() is False. That's actually all that is needed to retrieve multiple-row sets.

Listing 23-21: Retrieving Several Sets of Data with a DataReader

static void TestSelectHierWithDataReader()

{

// Set SQL statement strings

string strSQLSelect = "[pc_getOrdersAndDetails]";

// Create OleDb objects

OleDbConnection databaseConnection = new OleDbConnection(oleDbConnectionString);

OleDbCommand selectCommand = new OleDbCommand(strSQLSelect, databaseConnection);

// We are dealing with a Stored Proc (i.e. NOT a SQL statement)

selectCommand.CommandType = CommandType.StoredProcedure;

try

{

//Establish database connection databaseConnection.Open();

//Execute SQL Command OleDbDataReader rowReader =

selectCommand.ExecuteReader();

// Report results for (;;)

{

while(rowReader.Read())

{

string row = "";

for (int i=0; i<= rowReader.FieldCount - 1; i++)

{

row = row + rowReader[i] + ", ";

}

Console.WriteLine("Row is {0}", row.Substring(0,row.Length -2));

}

if(!rowReader.NextResult())

break;

else

Console.WriteLine("Next Results:");

}

}

catch (Exception e)

{

Console.WriteLine("****** Caught an exception:\n{0}", e.Message);

}

finally

{

databaseConnection.Close();

}

}

In the last code section, you discover one of the strengths of the DataSet object: retrieving related data. Because the DataSet was designed to function as an in-memory database, it has all the functionality you need to deal with parent-child relationships. The next two listings illustrate data retrieval. Listing 23-22 shows how related data is retrieved through a SQL statement, while Listing 23-23 shows how to retrieve related data using a DataSet object. Listing 23-23 demonstrates how you deal with such relationships. Again, it uses a stored procedure that returns data from two Select statements. The Select statements are related and you want to achieve the same result in ADO.NET as if you retrieved the data with a SQL statement similar to the one shown in Listing 23-22.

Listing 23-22: Retrieving Related Data with a SQL Statement

SELECT

Orders.OrderID,

Orders.CustomerID,

Orders.EmployeeID,

Orders.OrderDate,

Orders.RequiredDate,

Orders.ShippedDate,

Orders.ShipVia,

Orders.Freight,

Orders.ShipName,

Orders.ShipAddress,

Orders.ShipCity,

Orders.ShipRegion,

Orders.ShipPostalCode,

Orders.ShipCountry,

[Order Details].ProductID,

[Order Details].UnitPrice,

[Order Details].Quantity,

[Order Details].Discount

FROM Orders

INNER JOIN [Order Details]

ON Orders.OrderID = [Order Details].OrderID

Inside the try statement in Listing 23-23, you start by mapping the data tables to the source tables used in the SQL query. The code that follows, filling the DataSet, is the familiar code seen below. Next is the part where you define the relationship between the two tables. You

need primary keys when setting up foreign key relationships in a relational database management system (RDBMS), and our in-memory database is no different. The PrimaryKey property takes an array of DataColumn objects. After you set up the primary keys, you can define a relationship. The first parameter is the name of the relationship, which you use below when retrieving child records. For demonstration purposes, the example retrieves only the first parent row. It then retrieves all associated child rows by using the GetChildRows() method using the name of the relationship. You can then loop through the array of DataRow objects to display the children.

Listing 23-23: Retrieving Related Data with a DataSet

static void TestSelectHierWithDataSet()

{

// Set SQL statement strings

string strSQLSelect = "[pc_getOrdersAndDetails]";

// Create OleDb objects

OleDbConnection databaseConnection = new OleDbConnection(oleDbConnectionString);

OleDbCommand selectCommand = new OleDbCommand(strSQLSelect, databaseConnection);

OleDbDataAdapter dsCmd = new OleDbDataAdapter(); DataSet resultDataSet = new DataSet();

// We are dealing with a Stored Proc (i.e. NOT a SQL statement)

selectCommand.CommandType = CommandType.StoredProcedure;

try

{

dsCmd.TableMappings.Add("Orders", "Orders"); dsCmd.TableMappings.Add("Orders1", "Order Details");

//Establish database connection databaseConnection.Open();

//Execute SQL Command dsCmd.SelectCommand = selectCommand;

//Since there were no tables within the DataSet prior to invoking the Fill method,

//the OleDbDataAdapter will automatically create the tables for the DataSet

//and populate them with the returned data. If you build the tables prior to executing

//the FillDataSet method, the OleDbDataAdapter will simply fill the existing tables.

int numRows = dsCmd.Fill(resultDataSet, "Orders");

//Reduce the number of dots by saving the references to the tables

DataTable orderTable = resultDataSet.Tables["Orders"]; DataTable detailsTable = resultDataSet.Tables["Order

Details"];

//Set the Primary Key for the Tables orderTable.PrimaryKey =

new DataColumn[] { orderTable.Columns["OrderID"]

};

detailsTable.PrimaryKey = new DataColumn[] { detailsTable.Columns["OrderID"], detailsTable.Columns["ProductID"] };

// Establish the Foreign Key relationship between the tables resultDataSet.Relations.Add(new

DataRelation("Order_Detail",

new DataColumn[] { orderTable.Columns["OrderID"]

},

new DataColumn[] { detailsTable.Columns["OrderID"]

}));

//Report results

//Display an order

DataRow orderRow = orderTable.Rows[0]; Console.WriteLine("Order ID is {0}, date is {1}, Ship

To is {2}.", orderRow["OrderID"], orderRow["OrderDate"], orderRow["ShipName"]);

//Retrieve child rows for the order using the Name of the Relation

DataRow[] detailRows = orderRow.GetChildRows("Order_Detail");

//Do something with the child rows collection DataRow detailRow;

for (int i=0; i <= detailRows.Length - 1; i++)

{

//Do something with the detail row

detailRow = detailRows[i]; Console.WriteLine("Product ID is {0}, Quantity is

{1}.",

detailRow["ProductID"],

detailRow["Quantity"]);

}

}

catch (Exception e)

{

Console.WriteLine("****** Caught an exception:\n{0}", e.Message);

}

finally

{

databaseConnection.Close();

}

}

Summary

This chapter describes all the types of operations you may want to perform with ADO.NET. You see how easy and versatile the objects in the System.Data namespace are, including the powerful DataSet object, which acts as an in-memory database. It also describes how to return rows with single entities and how to delete and update operations.

Keep in mind that the ADO.NET provider architecture is reflected in the .NET Framework classes that support ADO.NET. You can use the Sql classes to make use of the ADO.NET

SQL Server provider or the OleDb classes to make use of the ADO.NET OLE DB provider. If your application is going to support only SQL Server, then you should use the Sql classes, since the SQL Server provider for ADO.NET is more efficient and a better performer than the OLE DB provider when targeting SQL Server. If your application may need to support databases other than SQL Server, then using the OleDb classes will be a better choice.

Chapter 24: Working with Files and the Windows Registry

In This Chapter

File operations are something every programmer has to deal with at one point or another. The System.IO class contains a plethora of methods for reading and writing to and from files. This class makes file IO and manipulation simple yet enables you a great deal of control when accessing files. Much like file IO in the past, accessing the Windows Registry was always a tedious task. Accessing the Windows Registry involved many API calls that degraded application performance and typically forced programmers to write their own wrapper classes for these operations. With .NET all of this changes.

In this chapter you learn to read and write data to and from files. The chapter covers IO operations that deal with standard text as well as binary data. It then covers some useful file operations, such as moving, renaming, and deleting files. Finally, you learn to monitor the file system for changes to specific files and then move on to navigating the Windows Registry.

Accessing Files

File access in .NET is generally handled with stream objects. A few classes, however, rely on the stream object for access to files. In this chapter, you examine two of these classes to build on your knowledge of file IO.

Binary access

The BinaryReader and BinaryWriter classes support binary file access. These classes permit binary operations to and from streams. Because streams are used, the classes can be very flexible, and they don't have to deal with details, such as stream position or access. The following section examines the BinaryWriter access class.

BinaryWriter

The BinaryWriter class allows primitive data types to be written to streams; and with the use of subclassing, you can override the methods of this class and fulfill your own unique character encoding requirements. Because this class uses an underlying stream, you have very few methods and properties to deal with. Table 24-1 contains the five basic properties and methods that you would typically use when dealing with the BinaryWriter class.

Table 24-1: Basic BinaryWriter Class Members

Соседние файлы в папке c#