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

C# Bible - Jeff Ferguson, Brian Patterson, Jason Beres

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

Handling data postback

As mentioned earlier, a WebForm is posted back to the server only when a Button, LinkButton, or ImageButton control is clicked. After the form is posted to the server, it is processed at the server. You can handle data postback corresponding to the click of a button in one of the following ways:

Write an event handler for the Click event of the button.

Write the event handler for the Load event of the WebForm. The Load event is generated when the form is loaded. You can use the IsPostBack property in the Load event to determine whether the page has been processed for the first time or by a button click. The following code depicts the event handler for a WebForm's Load event:

protected void Page_Load(object sender, EventArgs e)

{

if (!IsPostBack)

{

//Evals true first time browser hits the page

}

}

Using the view state

In traditional Web applications, whenever a Web page is processed at the server, the page is created from scratch. The server discards the current page information after it processes the information and sends the page to the client (browser). Because the page information is not preserved on the server, the Web pages are called stateless. However, the ASP.NET framework works around this limitation and can save the state information of the form and its controls. To manage state information, Visual Studio .NET provides the following options:

Save View state: You can save the view state of server controls to an object. In each round-trip, the state of the server control can be loaded from the saved state, so that the user can view all options that he or she selected previously.

StateBag: The StateBag class is the storage mechanism for server controls. This class provides properties to store information in key-value pairs. For example, if you want to store data specified by a user for a page, you can use an instance of the StateBag class to store this data.

Each server control includes an EnableViewState property. When you set this property to true, the state of the control is retained between round trips on the server. Thus, if the user has selected one or more options from a list, the options will be retained through round trips to the server.

Summary

In this chapter, you learned to create a simple Web application by using Visual C# in the ASP.NET framework. You learned the basics of ASP.NET and how you can create Web applications in Visual C#.

ASP.NET includes a separate runtime environment that manages the execution of ASP.NET applications. It also includes new server components, referred to as WebForms, which encapsulate the functionality of a Web page. You can add one or more server controls to a WebForm. Server controls are responsible for displaying data to users and processing user interactions.

You created a Web application project and added a WebForm to it. When creating an application, you used the ASP.NET Web application template to create a solution and add an ASP.NET project to the solution. Next, you designed a Web page by using common Web controls, such as the controls that represent labels, text boxes, list boxes, hyperlinks, buttons, and so on. Finally, you learned to handle events that are generated by controls on the WebForm.

Chapter 23: Database Programming with ADO.NET

In This Chapter

ADO.NET is the newest data access technology and is part of the .NET Framework. ADO.NET builds and improves upon previous data access technologies. Microsoft's foray into universal data access started with open database connectivity (ODBC). The idea behind this ODBC technology - to create a standard way to access databases programmatically - has been used in all subsequent data access technologies coming from Redmond, Washington (where Microsoft's headquarters are located). In the case of ODBC, this standard method is documented as the ODBC API (Application Programmer's Interface). Any database vendor wanting to claim compliance with the ODBC standard is responsible for building the software that translates an ODBC call (made in accordance with the API) into a native database call. Such software is called an ODBC driver and is the bridge between a generic client application and a specific database. Through this approach, the application programmers are shielded from having to learn how to use a specific vendor's database API. All an application programmer needs to know is how to write client applications using the ODBC API. This improves productivity, and enables you to write a program that can be used with different databases.

However, the ODBC API was originally designed primarily with C application programmers in mind, and was difficult to use in other languages (such as Visual Basic). This eventually led to the introduction of Active Data Objects (ADO), a data access technology designed for use with any language that supports Microsoft's Common Object Model (COM). ADO introduced a simple object model that made accessing data in MS Windows programs a straightforward task. In addition, ADO introduced the concept of disconnected recordsets as a way to transport data between the tiers of a distributed application. The low-level API behind ADO is called OLE DB. This API is designed for C++ programmers and is what database vendors typically use to write OLE DB providers (the preferred term for OLE DB drivers, the software that translates ADO calls into native database calls). Microsoft has also written an OLE DB provider for OBDC. This provider enables you to issue ADO calls against any ODBC-compliant database.

As this chapter shows you, ADO.NET keeps an object model that is similar to ADO and improves on the concept of disconnected recordsets by providing a way to bundle more information into an ADO.NET object called a dataset. In fact, ADO.NET was designed with the disconnected data in mind, because this stateless approach works best for distributed Internet applications. In this chapter, you learn how to use ADO.NET to manipulate data. If you are familiar with ADO, many of the concepts will look familiar, and even the code will not be completely unfamiliar.

Understanding the Dataset Classes and Their Relatives

This section examines the ADO.NET classes. If you are familiar with ADO, you will recognize many of the concepts presented here. Be aware, however, that some ADO concepts have come a long way in ADO.NET and are considerably expanded from their original forms. Let's start with the new kid on the block: the DataSet class and its relatives.

DataSet expands on the recordset concept found in ADO. As given away by its name, ADO's recordset is an abstraction of a set of records, such as the resulting data retrieved by issuing a SQL Select statement. A recordset can actually contain more than one set of records, but the records are independent of one another and must be processed sequentially by calling NextRecordSet(). ADO.NET's DataSet is an abstraction of an entire database. Not only can a DataSet contain more than one set of records (appropriately called a DataTable), you can define relationships between DataTables. Table 23-1 describes all the DataSet-related classes. Like the ADO classes, the ADO.NET classes make extensive use of collections: the DataSet class contains a collection of DataTables; the DataTable class contains a collection of DataColumns, and so on.

 

Table 23-1: DataSet and Related Classes

 

 

 

 

Class

 

 

Description

 

 

 

 

DataSet

 

 

An in-memory cache of data, which may consist of several related

 

 

 

DataTables. Designed for disconnected use in distributed

 

 

 

applications.

 

 

 

 

DataTable

 

 

A container of data, which may consist of several DataColumns.

 

 

 

Each row of data is contained in a DataRow.

 

 

 

 

DataRow

 

 

A specific row of data in a DataTable

 

 

 

 

DataColumn

 

 

The definition of a column (name, data type, etc.) in a DataTable

 

 

 

 

DataRelation

 

 

A relationship between two DataTables within a DataSet,

 

 

 

typically used to represent foreign-key relationships

 

 

 

 

Constraint

 

 

A restriction on one or more DataColumns, used to represent

 

 

 

constraints such as uniqueness

 

 

 

 

DataColumnMapping

 

 

Maps the column names from the table in a database to the

 

 

 

column names of the DataTable in the DataSet

 

 

 

 

DataTableMapping

 

 

Maps the table names in a database to the names of the

 

 

 

DataTables in the DataSet

 

 

 

 

DataView

 

 

A customized view of a DataTable that can be used for sorting,

 

 

 

filtering, and searching

 

 

 

 

ADO's RecordSet gradually evolved as the standard way to marshal data between tiers of a distributed application. DataSet takes over this role in ADO.NET and provides a number of methods to reconcile the in-memory cache of the data with its source data in the database. These methods include AcceptChanges(), GetChanges(), HasChanges(), HasErrors(), and RejectChanges(). These methods enable you to retrieve any changes in the form of a changed DataSet, inspect the changes for errors, and decide whether to accept or reject the changes. At the end of this process, you can update the source data in the database with a simple call to the Update() method.

Understanding OLE DB and SQL Server Support

ADO.NET contains two sets of similar classes. One set is a generic set of classes that can be used to access all databases that have OLE DB providers. A second set of classes has been fine-tuned for Microsoft's flagship database, SQL Server. The names of the generic classes all start with OleDb. The SQL Server-specific classes all begin with Sql. Each generic class has a corresponding SQL Server-specific class. For example, the class you use to execute SQL statements against SQL Server is called SqlCommand. The generic class is called OleDbCommand.

Note If you look at any code that was designed with the first public beta of the .NET Framework, you see code with the Ado prefix. This prefix was changed to OleDb starting with beta 2.

This chapter uses the generic classes, even when accessing a SQL Server database. When writing your own applications accessing SQL Server, you need to decide whether to use the speedier SQL Server specific classes or the more generic classes that enable you to switch database vendors simply by changing the connection string. The trade-off is speed versus portability. The SQL Server classes directly call the native database layer. The generic classes use OleDb and go through a COM layer before calling the native database layer. The overhead of an extra layer results in a decrease in performance.

The DataSet classes are used in conjunction with both the OLE DB provider and the SQL Server provider. Table 23-2 lists the provider-specific classes. Many of these look familiar to a trained ADO eye.

 

 

Table 23-2: DataSet and Related Classes

 

 

 

 

 

SQL Server Provider

 

OLE DB Provider

 

Description

Class

 

Class

 

 

 

 

 

 

 

SqlCommand

 

OleDbCommand

 

A class wrapper for a SQL statement. The

 

 

 

 

class can manage both direct SQL

 

 

 

 

statements such as a SELECT, UPDATE,

 

 

 

 

DELETE, or INSERT state-ment and a

 

 

 

 

stored procedure call.

 

 

 

SqlCommandBuilder

 

OleDbCommandBuilder

 

Used to generate SQL SELECT, UPDATE,

 

 

 

 

DELETE, or INSERT statements

 

 

 

 

 

SqlDataConnection

 

OleDbConnection

 

A connection to a database

 

 

 

 

 

SqlDataAdapter

 

OleDbDataAdapter

 

A set of SELECT, UPDATE, DELETE, or

 

 

 

 

INSERT statements and a connection to a

 

 

 

 

 

 

 

Table 23-2: DataSet and Related Classes

 

 

 

 

 

SQL Server Provider

 

OLE DB Provider

 

Description

Class

 

Class

 

 

 

 

 

 

 

 

 

 

 

database, which can be used to fill a

 

 

 

 

DataSet and update the underlying

 

 

 

 

database

 

 

 

 

 

SqlDataReader

 

OleDbDataReader

 

A forward-only set of data records

 

 

 

 

 

SqlError

 

OleDbError

 

A warning or error returned by the

 

 

 

 

database, contained in an Errors collection

 

 

 

 

 

SqlException

 

OleDbException

 

The type of exception thrown when

 

 

 

 

database errors occur

 

 

 

 

 

SqlParameter

 

OleDbParameter

 

A parameter to a stored procedure

 

 

 

 

 

SqlTransaction

 

OleDbTransaction

 

A database transaction

 

 

 

 

 

In the next section, you learn how these classes work with the common classes, DataSet and its relatives, to perform common database operations.

Understanding Common Database Operations Using

ADO.NET

Each of the examples shown in this section omits the using declarations for simplicity's sake. The following three namespace declarations are assumed present throughout this chapter:

using System;

using System.Data;

using System.Data.OleDb;

In addition, most functions are taken out of their class context. The functions are supposed to be scoped with the following class definition:

namespace ADOdotNET

{

class ADOdotNET

{

// NOTE: Function goes here

}

}

With these preliminary remarks out of the way, we can delve into ADO.NET. One by one, this section examines each category of operations that can be performed with ADO.NET:

Operations that do not return rows

Operations that return only one row

Operations that affect only one row

Operations that return multiple rows

Operations that affect multiple rows

Operations that return hierarchical data

Operations that don't return rows

Many SQL operations (for example, Insert, Delete, and Update statements) return only success or failure (or the numbers of rows affected by the operation).

Note The SQL Server programmer controls whether the number of rows affected is returned from a stored procedure through the SET NOCOUNT [ON | OFF] statement. SQL Server programmers often turn this feature off with SET NOCOUNT ON because of the slight improvement in performance.

Listing 23-3 shows how simple it is to execute a nonrow-returning SQL statement in ADO.NET. You use two objects in this process. You first use an OleDbConnection object to establish a connection to a database. Listing 23-1 shows a sample connection string used to access the Northwind database on a locally installed instance of SQL Server.

Listing 23-1: A Sample Connection String for SQL Server

private static string oleDbConnectionString

{

get

{

//NOTE: Using the sa account in production

//applications is, of course, a BAD, BAD

//practice. In addition, leaving the password

//blank for the sa account is equally

//inadmissible.

return "Provider=SQLOLEDB.1;"

+"User ID=sa;Initial Catalog=Northwind;Data Source=localhost;";

}

}

Listing 23-2 shows a sample connection string for Access 2000.

Listing 23-2: A Sample Connection String for Microsoft Access

private static string oleDbConnectionString

{

get

{

//NOTE: This assumes that Microsoft Office Pro

//was installed in the default location. return "Provider=Microsoft.Jet.OLEDB.4.0;"

+"Data Source=C:\\Program Files\\Microsoft Office\\Office\\Samples\\Northwind.MDB";

}

}

The second object used to execute a query is the OleDbCommand object. Its constructor takes a string argument (the text of the SQL statement you want to execute) and an

OleDbConnection object. The CommandType property lets you specify whether the command being executed is a stored procedure, an Access query, or plain text. The query is executed through the ExecuteNonQuery() method. Errors, such as a primary key violation, are reported through exceptions.

You can also use Command objects to execute SQL commands that do not returns rows of data, such as in the example in Listing 23-3:

Listing 23-3: A Template for Using a Command to Execute a Nonrow-Returning SQL Statement

//Declare and set appropriate values for oleDbConnectionString and strSQLStatement

//Create OleDb objects

OleDbConnection databaseConnection = new

OleDbConnection(oleDbConnectionString);

OleDbCommand databaseCommand = new

OleDbCommand(strSQLStatement, databaseConnection);

//NOTE: Only one of the two following statements should be used, NOT BOTH.

//If we are dealing with a SQL statement (i.e. NOT a Stored Proc), use:

databaseCommand.CommandType = CommandType.Text;

//If we are dealing with a Stored Proc, use: databaseCommand.CommandType = CommandType.StoredProcedure;

try

{

//Establish database connection databaseConnection.Open();

//Execute SQL Command

int numRows = databaseCommand.ExecuteNonQuery(); // Do something else, e.g. report numRows

}

catch (Exception e)

{

// Handle Exception, e.g.:

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

}

finally

{

databaseConnection.Close();

}

Calling a stored procedure with parameters is only slightly more complicated. Listing 23-4 shows the SQL code for a stored procedure that you want to call.

Listing 23-4: A SQL Server Stored Procedure to Insert a Record

USE [Northwind] GO

CREATE PROCEDURE [pc_insCustomers] (@CustomerID_1 [nchar](5), @CompanyName_2 [nvarchar](40), @ContactName_3 [nvarchar](30),

@ContactTitle_4 [nvarchar](30), @Address_5 [nvarchar](60), @City_6 [nvarchar](15), @Region_7 [nvarchar](15), @PostalCode_8 [nvarchar](10), @Country_9 [nvarchar](15), @Phone_10 [nvarchar](24), @Fax_11 [nvarchar](24))

AS

INSERT INTO [Northwind].[dbo].[Customers] ( [CustomerID],

[CompanyName],

[ContactName],

[ContactTitle],

[Address],

[City],

[Region],

[PostalCode],

[Country],

[Phone],

[Fax]) VALUES

( @CustomerID_1, @CompanyName_2, @ContactName_3, @ContactTitle_4, @Address_5, @City_6, @Region_7, @PostalCode_8, @Country_9, @Phone_10, @Fax_11)

The only tricky part is knowing how to define and set the parameters. This is done through the Parameters collection. As with any collection, you create new members with the Add() method. The newly created parameter is returned, and you can set the direction (whether the parameter is used for input only, for output only, or for both) and the value. The parameters of the Add() method are the name of the stored procedure parameter, its name, its data type, and its size. Listing 23-5 shows the code for the whole process.

Listing 23-5: Calling a Parameterized Stored Procedure in ADO.NET

static void TestInsertWithSPStatement(string customerID)

{

// Set SQL statement string

string strSQLInsert = "[pc_insCustomers]";

// Create OleDb objects

OleDbConnection databaseConnection = new OleDbConnection(oleDbConnectionString);

OleDbCommand insertCommand = new OleDbCommand(strSQLInsert, databaseConnection);

// We are dealing with a Stored Proc (i.e. NOT a SQL statement) insertCommand.CommandType = CommandType.StoredProcedure;

//Add each parameter (#1 of 11) OleDbParameter param = insertCommand.Parameters.Add("@CustomerID_1", OleDbType.VarChar, 5);

param.Direction = ParameterDirection.Input; param.Value = customerID;

//Add each parameter (#2 of 11)

param = insertCommand.Parameters.Add("@CompanyName_2", OleDbType.VarChar, 40);

param.Direction = ParameterDirection.Input; param.Value = "Hungry Coyote Export Store";

//Add each parameter 3-10

//Etc.

//Add each parameter (#11 of 11)

param = insertCommand.Parameters.Add("@Fax_11", OleDbType.VarChar, 24);

param.Direction = ParameterDirection.Input; param.Value = "(503) 555-2376";

try

{

//Establish database connection databaseConnection.Open();

//Execute SQL Command

int numRows = insertCommand.ExecuteNonQuery();

// Report results Console.WriteLine("Inserted {0} row(s).",

numRows.ToString());

}

catch (Exception e)

{

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

}

finally

{

databaseConnection.Close();

}

}

Data operations that return single-row entities

Some data operations, such as retrieving a record based on a primary key, return only a single row. ADO.NET provides three ways to retrieve a single row. One way applies only to singlerow entities, while the other two ways are generic and can be used to retrieve multiple rows (as you learn in the next section).

The most efficient way to retrieve a single-row entity is often through an output parameter. You can only use this method when you are certain the procedure returns only a single row, however. Listing 23-6 shows a SQL Server stored procedure that retrieves a single record through output parameters.

Listing 23-6: A SQL Server Stored Procedure to Retrieve a Single Record

USE [Northwind] GO

CREATE PROCEDURE [pc_getContact_ByCustomerID] (@CustomerID_1 [nchar](5), @ContactName_2 [nvarchar](30) output, @ContactTitle_3 [nvarchar](30) output)

AS SELECT

@ContactName_2 = [ContactName], @ContactTitle_3 = [ContactTitle]

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

[CustomerID] = @CustomerID_1

Calling such a stored procedure is similar to the code used to call the stored procedure that inserts a row (refer to Listing 23-3). Of course, the direction for the output parameters is set to ParameterDirection.Output. After you execute the stored procedure, you can use the Parameters collection to retrieve the values of the output parameters, as shown in Listing 23- 7.

Listing 23-7: Retrieving a Single Record Through Output Parameters

static void TestSPWithOutParam(string customerID)

{

// Set SQL statement strings

string strSQLSelect = "[pc_getContact_ByCustomerID]";

// 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;

//Add each parameter (#1 of 3)

OleDbParameter param = selectCommand.Parameters.Add("@CustomerID_1", OleDbType.VarChar, 5);

param.Direction = ParameterDirection.Input; param.Value = customerID;

// Add each parameter (#2 of 3)

param = selectCommand.Parameters.Add("@ContactName_2", OleDbType.VarChar, 30);

param.Direction = ParameterDirection.Output; // Add each parameter (#3 of 3)

param = selectCommand.Parameters.Add("@ContactTitle_3", OleDbType.VarChar, 30);

param.Direction = ParameterDirection.Output;

try

{

// Establish database connection databaseConnection.Open();

Соседние файлы в предмете Программирование