Você está na página 1de 51

ASP.

NET

ADO.NET
Table of Contents
History...................................................................................................................................3
Universal Data Access..........................................................................................................3
ODBC................................................................................................................................. 3
OLE-DB.............................................................................................................................. 3
OLE-DB Providers.................................................................................................................3
OLE-DB Consumers..............................................................................................................4
Data Access Consumers.......................................................................................................4
DAO................................................................................................................................... 4
RDO................................................................................................................................... 5
ADO................................................................................................................................... 6
ADO.NET............................................................................................................................8
An Introduction to ADO.NET.................................................................................................10
What is ADO.NET?..............................................................................................................10
Evolution of ADO.NET........................................................................................................10
Why ADO.NET?..................................................................................................................10
Design Goals for ADO.NET..................................................................................................10
Differences between ADO and ADO.NET.............................................................................11
ADO.NET Architecture........................................................................................................12
XML and ADO.NET..............................................................................................................13
The ADO.NET Object Model..................................................................................................13
ADO.NET Data Providers.....................................................................................................14
Creating a Custom Data Provider..........................................................................................19
ADO.NET DataSets.............................................................................................................21
Choosing a DataReader or a DataSet.....................................................................................23
ADO.NET in Visual Studio .NET..............................................................................................25
Using the Server Explorer..................................................................................................25
Creating a Data Adapter.....................................................................................................27
Creating a DataSet.............................................................................................................30
Creating the User Interface...............................................................................................32
ADO.NET Classes .................................................................................................................40
Creating a Connection........................................................................................................40
Opening a Connection........................................................................................................41
Building a Command..........................................................................................................41
Connection property...........................................................................................................42
CommandText property.......................................................................................................42
CommandType property......................................................................................................43
CommandTimeout property.................................................................................................43

Ravi Varma Thumati (www.rthumati.wordpress.com)

Page 1 of 51

ASP.NET

Appending parameters........................................................................................................44
Executing a Command.......................................................................................................45
ExecuteNonQuery method...................................................................................................45
ExecuteReader method.......................................................................................................45
ExecuteScalar() Method......................................................................................................46
Prepare () method..............................................................................................................46
Introducing DataReader Class...........................................................................................47
Using DataReader Properties................................................................................................47
Item property ...................................................................................................................47
FieldCount property............................................................................................................48
IsClosed property...............................................................................................................48
RecordsAffected property....................................................................................................49
Using DataReader Methods..................................................................................................50
GetValue method...............................................................................................................50
Get[Data Type] methods.....................................................................................................50
GetOrdinal method.............................................................................................................51
GetName method...............................................................................................................51
Close method....................................................................................................................51

Ravi Varma Thumati (www.rthumati.wordpress.com)

Page 2 of 51

ASP.NET

History
The main goal of all APIs is to provide universal data access by means of having a single code
base for accessing data from any source, from any language.
Having universal data access is important for four reasons:

Developers can easily work on applications targeting different data stores without needing to
become experts on each one.

Developers can have a common framework for data access when switching between
programming languages, making the transition to new languages easier.

It enables developers to more easily write a single application that can be deployed against
multiple data stores.

Finally, it provides a level of abstraction between the application and direct communication to
the database to simplify the code the average developer needs to write.
Universal Data Access
At first, there were no common interfaces for accessing data. Each data provider exposed an API or
other means of accessing its data. The developer only had to be familiar with the API of the data
provider used. When companies switched to a new database system, any knowledge of how to use the
old system became worthless and the developer had to learn a new system from scratch. Something
needed to be done to standardize the way in which data was retrieved from various sources.
ODBC
Open Database Connectivity (ODBC) helped address the problem of needing to know the details of each
DBMS used. ODBC provides a single interface for accessing a number of database systems. To
accomplish this, ODBC provides a driver model for accessing data. Any database provider can write a
driver for ODBC to access data from their database system. This enables developers to access that
database through the ODBC drivers instead of talking directly to the database system. For data sources
such as files, the ODBC driver plays the role of the engine, providing direct access to the data source. In
cases where the ODBC driver needs to connect to a database server, the ODBC driver typically acts as a
wrapper around the API exposed by the database server.
ODBC was a huge leap forward and helped to greatly simplify database-driven application development.
It does have some shortfalls, though.

First, it is only capable of supporting relational data. If you need to access a hierarchical data
source such as LDAP, or semi-structured data, ODBC cant help you.
Second, it can only handle SQL statements, and the result must be represent able in the form of
rows and columns.

Overall, ODBC was a huge success, considering what the previous environment was like.
OLE-DB
Object Linking and Embedding Database (OLE-DB) was the next big step forward in data providers, and
it is still widely used today. With OLE-DB, Microsoft applied the knowledge learned from developing
ODBC to provide a better data access model. OLE-DB marked Microsofts move to a COM-based API,
which made it easily consumable by most programming languages, and the migration to a 32-bit OS
with the release of Windows 95.
OLE-DB Providers
OLE-DB is also much less dependent upon the physical structure of the database. It supports both
relational and hierarchical data sources, and does not require the query against these data sources to
follow a SQL structure. As with ODBC, vendors can create custom providers to expose access to their

Ravi Varma Thumati (www.rthumati.wordpress.com)

Page 3 of 51

ASP.NET

database system. Most people wouldnt argue with the belief that it is far easier to write an OLE-DB
provider than an ODBC driver. A provider needs to perform only four basic steps:
1. Open the session.
2. Process the command.
3. Access the data.
4. Prepare a rowset.
OLE-DB Consumers
The other half of the OLE-DB framework is the OLE-DB consumer. The consumer is the layer that speaks
directly to the OLE-DB providers, and it performs the following steps:
1.
2.
3.
4.

Identify the data source.


Establish a session.
Issue the command.
Return a rowset.

Figure 1-1 shows how this relationship works.

Data Access Consumers


Developers who use languages that support pointerssuch as C, C++, VJ++, and so oncan speak
directly to the ODBC and OLE-DB APIs. However, developers using a language such as Visual Basic need
another layer. This is where the data access consumers such as DAO, RDO, ADO, and ADO.NET come
into play.
DAO
With the release of Visual Basic 2.0, developers were introduced to a new method for accessing data,
known as Data Access Objects (DAO). This was Microsofts first attempt to create a data consumer API.
DAO was based on the JET engine, which was largely designed to help developers take advantage of the
desktop database application Microsoft was about to release, Microsoft Access. It served to provide

Ravi Varma Thumati (www.rthumati.wordpress.com)

Page 4 of 51

ASP.NET

another layer of abstraction between the application and data access, making the developers task
simpler. With release of DAO 1.0, which supported direct communication with Microsoft Access
databases without using ODBC?

The main problem with DAO is that it can only talk to the JET engine. The JET engine then
communicates with ODBC to retrieve the data. Going through this extra translation layer adds
unnecessary overhead and makes accessing data through DAO slow.
RDO
Remote Data Objects (RDO) was Microsofts solution to the slow performance created by DAO. For
talking to databases other than Microsoft Access, RDO did not use the JET engine like DAO; instead, it
communicated directly with the ODBC layer. Figure 1-3 shows this relationship.
Removing the JET engine from the call stack greatly improved performance to ODBC data sources! The
JET engine was only used when accessing a Microsoft Access Database. In addition, RDO had the

Ravi Varma Thumati (www.rthumati.wordpress.com)

Page 5 of 51

ASP.NET

capability to use client-side cursors to navigate the records, as opposed to the server-side cursor
requirements of DAO.
This greatly reduced the load on the database server, enabling not only the application to perform
better, but also the databases on which that application was dependant.

RDO was primarily targeted toward larger, commercial customers, many of whom avoided DAO due to
the performance issues. Instead of RDO replacing DAO, they largely co-existed. This resulted for several
reasons: First, users who developed smaller applications, where performance wasnt as critical, didnt
want to take the time to switch over to the new API. Second, RDO was originally only released with the
Enterprise Edition of Visual Basic, so some developers didnt have a choice. Third, with the release of
ODBCDirect, a DAO add-on that routed the ODBC requests through RDO instead of the JET engine, the
performance gap between the two became much smaller. Finally, it wasnt long after the release of RDO
that Microsofts next universal access API was released.
ADO
Microsoft introduced ActiveX Data Objects (ADO) primarily to provide a higher-level API for working with
OLE-DB. With this release, Microsoft took many of the lessons from the past to build a lighter, more

Ravi Varma Thumati (www.rthumati.wordpress.com)

Page 6 of 51

ASP.NET

efficient, and more universal data access API. Unlike RDO, ADO was initially promoted as a replacement
for both DAO and RDO. At the time of its release, it (along with OLE-DB) was widely believed to be a
universal solution for accessing any type of datafrom databases to e-mail, flat text files, and
spreadsheets.
As stated before, ADO was primarily released to complement OLE-DB; however, ADO was not limited to
just communicating with OLE-DB data sources. ADO introduced the provider model, which enabled
software vendors to create their own providers relatively easily, which could then be used by ADO to
communicate with a given vendors data source and implement many of the optimizations specific to
that data source. The ODBC provider that shipped with ADO was one example of this. When a developer
connected to an ODBC data source, ADO would communicate through the ODBC provider instead of
through OLE-DB. More direct communication to the data source resulted in better performance and an
easily extensible framework. Figure 1-4 shows this relationship.

In addition to being a cleaner object model, ADO also offered a wider feature set to help lure developers
away from DAO and RDO. These included the following:
Batch UpdatingFor the first time, users enjoyed the capability to make changes to an entire
recordset in memory and then persist these changes back to the database by using the UpdateBatch
command.
Disconnected Data AccessAlthough this wasnt available in the original release, subsequent
releases offered the capability to work with data in a disconnected state, which greatly reduced the load
placed on database servers.

Ravi Varma Thumati (www.rthumati.wordpress.com)

Page 7 of 51

ASP.NET

Multiple RecordsetsADO provided the capability to execute a query that returns multiple
recordsets and work with all of them in memory. This feature wasnt even available in ADO.NET until
this release, now known as Multiple Active Result Sets (MARS).
ADO.NET
With the release of the .NET Framework, Microsoft introduced a new data access model, called
ADO.NET. The ActiveX Data Object acronym was no longer relevant, as ADO.NET was not ActiveX, but
Microsoft kept the acronym due to the huge success of ADO. In reality, its an entirely new data access
model written in the .NET Framework.
ADO.NET supports communication to data sources through both ODBC and OLE-DB, but it also offers
another option of using database-specific data providers. These data providers offer greater
performance by being able to take advantage of data-source-specific optimizations. By using custom
code for the data source instead of the generic ODBC and OLE-DB code, some of the overhead is also
avoided. The original release of ADO.NET included a SQL provider and an OLE-DB provider, with the
ODBC and Oracle providers being introduced later. Many vendors have also written providers for their
databases since. Figure 1.5 shows the connection options available with ADO.NET.

With ADO.NET, the days of the recordset and cursor are gone. The model is entirely new, and consists of
five basic objects:
ConnectionThe Connection object is responsible for establishing and maintaining the connection to
the data source, along with any connection-specific information.

Ravi Varma Thumati (www.rthumati.wordpress.com)

Page 8 of 51

ASP.NET

CommandThe Command object stores the query that is to be sent to the data source, and any
applicable parameters.
DataReaderThe DataReader object provides fast, forward-only reading capability to quickly loop
through the records.
DataSetthe DataSet object, along with its child objects, is what really makes ADO.NET unique. It
provides a storage mechanism for disconnected data. The DataSet never communicates with any data
source and is totally unaware of the source of the data used to populate it. The best way to think of it is
as an in-memory repository to store data that has been retrieved.
DataAdapterthe DataAdapter object is what bridges the gap between the DataSet and the data
source. The DataAdapter is responsible for retrieving the data from the Command object and populating
the DataSet with the data returned. The DataAdapter is also responsible for persisting changes to the
DataSet back to the data source.
ADO.NET made several huge leaps forward. Arguably, the greatest was the introduction of truly
disconnected data access. Maintaining a connection to a database server such as MS SQL Server is an
expensive operation. The server allocates resources to each connection, so its important to limit the
number of simultaneous connections. By disconnecting from the server as soon as the data is retrieved,
instead of when the code is done working with that data, that connection becomes available for another
process, making the application much more scalable
Another feature of ADO.NET that greatly improved performance was the introduction of connection
pooling. Not only is maintaining a connection to the database an expensive operation, but creating and
destroying that connection is also very expensive. Connection pooling cuts down on this. When a
connection is destroyed in code, the Framework keeps it open in a pool. When the next process comes
around that needs a connection with the same credentials, it retrieves it from the pool, instead of
creating a new one.
Several other advantages are made possible by the DataSet object. The DataSet object stores the data
as XML, which makes it easy to filter and sort the data in memory. It also makes it easy to convert the
data to other formats, as well as easily persist it to another data store and restore it again.
Data Storage
Data storage is a method of storing specific items that together constitute a unit of information.
Individual data items themselves are of little use. They become value resources only when put into
context with other data items.
Type

Characteristics

Examples

Unstructured

Data has no logical order

Simple memos

Structured, non-hierarchical

Data is separated into units, but the


units are organized strictly by their
order.

Comma separated CVS files,


tab separated files, Excel
Spreadsheets

Hierarchical

Data is organized in a tree structure,


with nodes that contain other nodes

XML data documents

Relational Database

Data is organized in table, with columns


containing a specific type of data and
rows containing a single record. Table
can be related over columns with
identical data.

Microsoft SQL Server, MS


Access and Oracle Databases

Object oriented Database

Data is organized into objects

Objectivity/DB

Ravi Varma Thumati (www.rthumati.wordpress.com)

Page 9 of 51

ASP.NET

An Introduction to ADO.NET
What is ADO.NET?
ADO.NET is a set of libraries included with the Microsoft .NET Framework that helps you communicates
with various data stores from .NET applications. The ADO.NET libraries include classes for connecting to
a data source, submitting queries, and processing results. You can also use ADO.NET as a robust,
hierarchical, disconnected data cache to work with data off line. The central disconnected objects, the
DataSet, allow you to sort, search, filter, store pending changes, and navigate through hierarchical data.
The DataSet also includes a number of features that bridge the gap between traditional data access and
XML development. Developers can now work with XML data through traditional data access interfaces
and vice-versa.
As an integral part of the .NET framework, it shares many of its features: such as multi-language
support, garbage collection, just-in-time compilation, object-oriented design, and dynamic caching, and
is far more than an upgrade of previous versions of ADO.
The ADO.NET classes are found in System.Data.dll, and are integrated with the XML classes found in
System.Xml.dll. When compiling code that uses the System.Data namespace, reference both
System.Data.dll and System.Xml.dll.
Evolution of ADO.NET
The first data access model, DAO (data access model) was created for local databases with the built-in
Jet engine which had performance and functionality issues. Next RDO (Remote Data Object) and ADO
(Active Data Object) which were designed for Client Server architectures but soon ADO took over RDO.
ADO was a good architecture but as the language changes so is the technology. With ADO, all the data
is contained in a recordset object which had problems when implemented on the network and
penetrating firewalls. ADO was a connected data access, which means that when a connection to the
database is established the connection remains open until the application is closed. Leaving the
connection open for the lifetime of the application raises concerns about database security and network
traffic; also, as databases are becoming increasingly important and as they are serving more people, a
connected data access model makes us think about its productivity.
For example, an application with connected data access may do well when connected to two clients, the
same may do poorly when connected to 10 and might be unusable when connected to 100 or more.
Also, open database connections use system resources to a maximum extent making the system
performance less effective.
Why ADO.NET?
ADO .NET addresses the above mentioned problems by maintaining a disconnected database access
model which means, when an application interacts with the database, the connection is opened to serve
the request of the application and is closed as soon as the request is completed. Likewise, if a database
is updated, the connection is opened long enough to complete the Update operation and is closed. By
keeping connections open for only a minimum period of time, ADO .NET conserves system resources
and provides maximum security for databases and also has less impact on system performance. Also,
ADO .NET when interacting with the database uses XML and converts all the data into XML format for
database related operations making them more efficient.
Design Goals for ADO.NET
As application development has evolved, new applications have become loosely coupled based on the
Web application model. More and more of today's applications use XML to encode data to be passed

Ravi Varma Thumati (www.rthumati.wordpress.com)

Page 10 of 51

ASP.NET

over network connections. Web applications use HTTP as the fabric for communication between tiers,
and therefore must explicitly handle maintaining state between requests. This new model is very
different from the connected, tightly coupled style of programming that characterized the client/server
era, where a connection was held open for the duration of the program's lifetime and no special handling
of state was required.
In designing tools and technologies to meet the needs of today's developer, Microsoft recognized that an
entirely new programming model for data access was needed, one that is built upon the .NET
Framework. Building on the .NET Framework ensures that the data access technology would be uniform
components would share a common type system, design patterns, and naming conventions.
ADO.NET was designed to meet the needs of this new programming model: disconnected data
architecture, tight integration with XML, common data representation with the ability to combine data
from multiple and varied data sources, and optimized facilities for interacting with a database, all native
to the .NET Framework.
In creating ADO.NET, Microsoft embraced the following design goals.

Leverage Current ADO Knowledge


The design for ADO.NET addresses many of the requirements of today's application development
model. At the same time, the programming model stays as similar as possible to ADO, so
current ADO developers do not have to start from the beginning in learning a brand new data
access technology. ADO.NET is an intrinsic part of the .NET Framework without seeming
completely foreign to the ADO programmer.
ADO.NET coexists with ADO. While most new .NET-based applications will be written using
ADO.NET, ADO remains available to the .NET programmer through .NET COM interoperability
services.

Support the N-Tier Programming Model


ADO.NET provides first-class support for the disconnected, n-tier programming environment for
which many new applications are written. The concept of working with a disconnected set of
data has become a focal point in the programming model. The ADO.NET solution for n-tier
programming is the DataSet.

Integrate XML Support


XML and data access are intimately tied XML is all about encoding data, and data access is
increasingly becoming all about XML. The .NET Framework does not just support Web standards
it is built entirely on top of them.
XML support is built into ADO.NET at a very fundamental level. The XML classes in the .NET
Framework and ADO.NET are part of the same architecture they integrate at many different
levels. You no longer have to choose between the data access set of services and their XML
counterparts; the ability to cross over from one to the other is inherent in the design of both.

Differences between ADO and ADO.NET


ADO and ADO.NET are different in several ways:

ADO works with connected data. This means that when you access data, such as viewing and
updating data, it is real-time, with a connection being used all the time. This is barring, of
course, you programming special routines to pull all your data into temporary tables.

Ravi Varma Thumati (www.rthumati.wordpress.com)

Page 11 of 51

ASP.NET

ADO.NET uses data in a disconnected fashion. When you access data, ADO.NET makes a copy of
the data using XML. ADO.NET only holds the connection open long enough to either pull down
the data or to make any requested updates. This makes ADO.NET efficient to use for Web
applications. It's also decent for desktop applications.

ADO has one main object that is used to reference data, called the Recordset object. This object
basically gives you a single table view of your data, although you can join tables to create a new
set of records. With ADO.NET, you have various objects that allow you to access data in various
ways. The DataSet object will actually allow you to store the relational model of your database.
This allows you to pull up customers and their orders, accessing/updating the data in each
related table individually.

ADO allows you to create client-side cursors only, whereas ADO.NET gives you the choice of
either using client-side or server-side cursors. In ADO.NET, classes actually handle the work of
cursors. This allows the developer to decide which is best. For Internet development, this is
crucial in creating efficient applications.

Whereas ADO allows you to persist records in XML format, ADO.NET allows you to manipulate
your data using XML as the primary means. This is nice when you are working with other
business applications and also helps when you are working with firewalls because data is passed
as HTML and XML.

ADO.NET Architecture
Data processing has traditionally relied primarily on a connection-based, two-tier model. As data
processing increasingly uses multi-tier architectures, programmers are switching to a disconnected
approach to provide better scalability for their applications.
The following diagram illustrates the components of ADO.NET architecture.

Ravi Varma Thumati (www.rthumati.wordpress.com)

Page 12 of 51

ASP.NET

XML and ADO.NET


ADO.NET leverages the power of XML to provide disconnected access to data. ADO.NET was designed
hand-in-hand with the XML classes in the .NET Framework both are components of a single
architecture.
ADO.NET and the XML classes in the .NET Framework converge in the DataSet object. The DataSet can
be populated with data from an XML source, whether it is a file or an XML stream. The DataSet can be
written as World Wide Web Consortium (W3C) compliant XML, including its schema as XML Schema
definition language (XSD) schema, regardless of the source of the data in the DataSet. Because the
native serialization format of the DataSet is XML, it is an excellent medium for moving data between
tiers making the DataSet an optimal choice for remoting data and schema context to and from an XML
Web service.
The DataSet can also be synchronized with an XmlDataDocument to provide relational and
hierarchical access to data in real time.
The ADO.NET Object Model
ADO.NET is designed to help developers build efficient multi-tiered database applications across
intranets and the Internet, and the ADO.NET object model provides the means.

Figure above shows the classes that comprise the ADO.NET object model. A dotted line separates the
object model into two halves. The objects to the left of the line are connected objects. These objects
communicate directly with your database to manage the connection and transactions as well as to
retrieve data from and submit changes to your database. The objects to the right of the line are
disconnected objects that allow a user to work with data offline.
The objects that comprise the disconnected half of the ADO.NET object model do not communicate
directly with the connected objects. This is a major change from previous Microsoft data access object
models. In ADO, the Recordset object stores the results of your queries. You can call its Open method to
fetch the results of a query and call its Update (or UpdateBatch) method to submit changes stored
within the Recordset to your database.
The ADO.NET object model consists of two fundamental components: the DataSet, which is
disconnected from the data source and doesn't need to know where the data it holds came from; and

Ravi Varma Thumati (www.rthumati.wordpress.com)

Page 13 of 51

ASP.NET

the .NET data provider. The .NET data providers allow us to connect to the data source, and to
execute SQL commands against it.

ADO.NET Data Providers


A .NET Framework data provider is used for connecting to a database, executing commands, and
retrieving results. Those results are either processed directly, placed in a DataSet in order to be exposed
to the user as needed, combined with data from multiple sources, or remoted between tiers. .NET
Framework data providers are lightweight, creating a minimal layer between the data source and code,
increasing performance without sacrificing functionality. Below figure shows the relationship between the
ADO.NET data providers that ship with .NET, the data sources that they access, and the disconnected
ADO.NET classes.

The following table lists the data providers that are included in the .NET Framework.
.NET Framework data provider

Description

Ravi Varma Thumati (www.rthumati.wordpress.com)

Page 14 of 51

ASP.NET

.NET Framework Data Provider for Provides data access for Microsoft SQL Server version 7.0 or
SQL Server
later. Uses the System.Data.SqlClient namespace.
.NET Framework Data Provider for For data sources exposed by
OLE DB
System.Data.OleDb namespace.
.NET Framework Data Provider for For data sources exposed
ODBC
System.Data.Odbc namespace.

by

using

OLE

using

DB.

ODBC.

Uses

the

Uses

the

.NET Framework Data Provider for For Oracle data sources. The .NET Framework Data Provider for
Oracle
Oracle supports Oracle client software version 8.1.7 and later,
and uses the System.Data.OracleClient namespace.
EntityClient Provider

Provides data access for Entity Data Model (EDM) applications.


Uses the System.Data.EntityClient namespace.

Core Objects of .NET Framework Data Providers


The following table outlines the four core objects that make up a .NET Framework data provider.
Object

Description

Connection

Establishes a connection to a specific data source. The base class for all Connection
objects is the DbConnection class.

Command

Executes a command against a data source. Exposes Parameters and can execute in
the scope of a Transaction from a Connection. The base class for all Command
objects is the DbCommand class.

DataReader

Reads a forward-only, read-only stream of data from a data source. The base class for
all DataReader objects is the DbDataReader class.

DataAdapter

Populates a DataSet and resolves updates with the data source. The base class for all
DataAdapter objects is the DbDataAdapter class.

The Connection object represents the physical connection to a data source. Its properties determine
the data provider (in the case of the OLE DB Data Provider), the data source and database to which it
will connect, and the string to be used during connecting. Its methods are fairly simple: You can open
and close the connection, change the database, and manage transactions.
The Command object represents a SQL statement or stored procedure to be executed at the data
source. Command objects can be created and executed independently against a Connection object, and
they are used by DataAdapter objects to handle communications from a DataSet back to a data source.
Command objects can support SQL statements and stored procedures that return single values, one or
more sets of rows, or no values at all.
A DataReader is a fast, low-overhead object for obtaining a forward-only, read-only stream of data
from a data source. They cannot be created directly in code; they are created only by calling the
ExecuteReader method of a Command.
The last main component of the .NET data provider is the DataAdapter. The DataAdapter acts as a
bridge between the disconnected DataSet and the data source. It exposes two interfaces; the first of
these, IDataAdapter, defines methods for populating a DataSet with data from the data source, and for
updating the data source with changes made to the DataSet on the client. The second interface,
IDbDataAdapter, defines four properties, each of type IDbCommand. These properties each set or return
a command object specifying the command to be executed when the data source is to be queried or
updated:

Ravi Varma Thumati (www.rthumati.wordpress.com)

Page 15 of 51

ASP.NET

In addition to the core classes listed in the table earlier in this document, a .NET Framework data
provider also contains the classes listed in the following table.
Object

Description

Transaction

Enlists commands in transactions at the data source. The base class for
all Transaction objects is the DbTransaction class. ADO.NET also
provides
support
for
transactions
using
classes
in
the
System.Transactions namespace.

CommandBuilder

A helper object that automatically generates command properties of a


DataAdapter or derives parameter information from a stored procedure
and populates the Parameters collection of a Command object. The
base class for all CommandBuilder objects is the DbCommandBuilder
class.

ConnectionStringBuilder

A helper object that provides a simple way to create and manage the
contents of connection strings used by the Connection objects. The
base class for all ConnectionStringBuilder objects is the
DbConnectionStringBuilder class.

Parameter

Defines input, output, and return value parameters for commands and
stored procedures. The base class for all Parameter objects is the
DbParameter class.

Exception

Returned when an error is encountered at the data source. For an error


encountered at the client, .NET Framework data providers throw a .NET
Framework exception. The base class for all Exception objects is the
DbException class.

Error

Exposes the information from a warning or error returned by a data


source.

ClientPermission

Provided for .NET Framework data provider code access security


attributes. The base class for all ClientPermission objects is the
DBDataPermission class.

.NET Framework Data Provider for SQL Server (SqlClient)


The .NET Framework Data Provider for SQL Server (SqlClient) uses its own protocol to communicate
with SQL Server. It is lightweight and performs well because it is optimized to access a SQL
Server directly without adding an OLE DB or Open Database Connectivity (ODBC) layer. The following

Ravi Varma Thumati (www.rthumati.wordpress.com)

Page 16 of 51

ASP.NET

illustration contrasts the .NET Framework Data Provider for SQL Server with the .NET Framework Data
Provider for OLE DB. The .NET Framework Data Provider for OLE DB communicates to an OLE DB data
source through both the OLE DB Service component, which provides connection pooling, and transaction
services, and the OLE DB provider for the data source.
Comparison of the dnprdnshort Data Provider for ssNoVersionr and the dnprdnshort Data
Provider for OLE DB

To use the .NET Framework Data Provider for SQL Server, you must have access to SQL Server 7.0 or
later versions. The .NET Framework Data Provider for SQL Server classes is located in the
System.Data.SqlClient namespace. For earlier versions of SQL Server, use the .NET Framework Data
Provider for OLE DB with the SQL Server OLE DB provider System.Data.OleDb.
The .NET Framework Data Provider for SQL Server supports both local and distributed transactions. For
distributed transactions, the .NET Framework Data Provider for SQL Server, by default, automatically
enlists in a transaction and obtains transaction details from Windows Component Services or
System.Transactions.
The following code example shows how to include the System.Data.SqlClient namespace in your
applications.
Visual Basic
Imports System.Data.SqlClient
C#
using System.Data.SqlClient;
.NET Framework Data Provider for OLE DB
The .NET Framework Data Provider for OLE DB (OleDb) uses native OLE DB through COM interop to
enable data access. The .NET Framework Data Provider for OLE DB supports both local and distributed
transactions. For distributed transactions, the .NET Framework Data Provider for OLE DB, by default,
automatically enlists in a transaction and obtains transaction details from Windows 2000 Component
Services.
The following table shows the providers that have been tested with ADO.NET.
Driver

Provider

SQLOLEDB

Microsoft OLE DB provider for SQL Server

MSDAORA

Microsoft OLE DB provider for Oracle

Microsoft.Jet.OLEDB.4.0

OLE DB provider for Microsoft Jet

The .NET Framework Data Provider for OLE DB does not support OLE DB version 2.5 interfaces. OLE DB
Providers that require support for OLE DB 2.5 interfaces will not function correctly with the .NET
Framework Data Provider for OLE DB. This includes the Microsoft OLE DB provider for Exchange and the
Microsoft OLE DB provider for Internet Publishing.

Ravi Varma Thumati (www.rthumati.wordpress.com)

Page 17 of 51

ASP.NET

The .NET Framework Data Provider for OLE DB does not work with the OLE DB provider for ODBC
(MSDASQL). To access an ODBC data source using ADO.NET, use the .NET Framework Data Provider for
ODBC.
.NET Framework Data Provider for OLE DB classes is located in the System.Data.OleDb namespace. The
following code example shows how to include the System.Data.OleDb namespace in your applications.
Visual Basic
Imports System.Data.OleDb
C#
using System.Data.OleDb;
.NET Framework Data Provider for ODBC
The .NET Framework Data Provider for ODBC (Odbc) uses the native ODBC Driver Manager (DM) to
enable data access. The ODBC data provider supports both local and distributed transactions. For
distributed transactions, the ODBC data provider, by default, automatically enlists in a transaction and
obtains transaction details from Windows 2000 Component Services.
The following table shows the ODBC drivers tested with ADO.NET.
Driver
SQL Server
Microsoft ODBC for Oracle
Microsoft Access Driver (*.mdb)
.NET Framework Data Provider for ODBC classes is located in the System.Data.Odbc namespace.
The following code example shows how to include the System.Data.Odbc namespace in your
applications.
Visual Basic
Imports System.Data.Odbc
C#
using System.Data.Odbc;
.NET Framework Data Provider for Oracle
The .NET Framework Data Provider for Oracle (OracleClient) enables data access to Oracle data sources
through Oracle client connectivity software. The data provider supports Oracle client software version
8.1.7 or a later version. The data provider supports both local and distributed transactions.
The .NET Framework Data Provider for Oracle requires Oracle client software (version 8.1.7 or a later
version) on the system before you can connect to an Oracle data source.
.NET Framework Data Provider for Oracle classes is located in the System.Data.OracleClient namespace
and are contained in the System.Data.OracleClient.dll assembly. You must reference both the
System.Data.dll and the System.Data.OracleClient.dll when you compile an application that uses
the data provider.
The following code example shows how to include the System.Data.OracleClientnamespace in your
applications.
Visual Basic
Imports System.Data
Imports System.Data.OracleClient

Ravi Varma Thumati (www.rthumati.wordpress.com)

Page 18 of 51

ASP.NET

C#
using System.Data;
using System.Data.OracleClient;
Choosing a .NET Framework Data Provider
Depending on the design and data source for your application, your choice of .NET Framework data
provider can improve the performance, capability, and integrity of your application. The following table
discusses the advantages and limitations of each .NET Framework data provider.
EntityClient Provider
The EntityClient provider is used for accessing data based on an Entity Data Model (EDM). Unlike the
other .NET Framework data providers, it does not interact directly with a data source. Instead, it uses
Entity SQL to communicate with the underlying data provider.
Provider

Notes

.NET Framework Data Provider for Recommended for middle-tier applications that use Microsoft SQL
SQL Server
Server 7.0 or a later version.
Recommended for single-tier applications that use Microsoft
Database Engine (MSDE) or SQL Server 7.0 or a later version.
Recommended over use of the OLE DB provider for SQL Server
(SQLOLEDB) with the .NET Framework Data Provider for OLE DB.
For SQL Server 6.5 and earlier, you must use the OLE DB
provider for SQL Server with the .NET Framework Data Provider
for OLE DB.
.NET Framework Data Provider for Recommended for middle-tier applications that use SQL Server
OLE DB
6.5 or earlier.
For SQL Server 7.0 or a later version, the .NET Framework Data
Provider for SQL Server is recommended.
Also recommended for single-tier applications that use Microsoft
Access databases. Use of an Access database for a middle-tier
application is not recommended.
.NET Framework Data Provider for Recommended for middle and single-tier applications that use
ODBC
ODBC data sources.
.NET Framework Data Provider for Recommended for middle and single-tier applications that use
Oracle
Oracle data sources.
Creating a Custom Data Provider
ADO.NET provides a set of interfaces that allow you to build a custom .NET data provider. While most
DBMSs can be accessed through a specific .NET data provider or through the OLE DB .NET data
provider, some reasons to implement a custom data provider include:

To access proprietary data sources that have neither a specific .NET data provider nor an OLE
DB provider that can be accessed through the .NET OLE DB data provider.
To expose specific functionality of the data source that is accessed through a general-purpose
provider. For example, a database that is accessed through the OLE DB .NET data provider
might have functionality that isn't available through that provider. A custom data provider can
be written to expose the database-specific functionality.

Ravi Varma Thumati (www.rthumati.wordpress.com)

Page 19 of 51

ASP.NET

To provide application-specific data access architecture to improve performance, simplify


programming, and improve maintainability.

An alternative to writing a custom data provider for a proprietary data source is to write an OLE DB
provider for the data source and use the OLE DB .NET data provider to access the data through that OLE
DB provider. This approach might make sense in situations when broad access to a full set of database
features is required. Once the OLE DB provider is written, the data source can also be accessed not only
with the OLE DB .NET data provider but by any application or tool that supports OLE DB provider data
access.
A custom .NET data provider must at least support the DataSet through the IDataAdapter interface, and
possibly the IDataParameter interface for parameterized queries. Such a minimal data provider allows a
DataSet to be loaded with data from the data source, the modification of data within the DataSet, and
the reconciliation of the changed DataSet with the data source. A minimal provider can support clients
that deal primarily with a disconnected data, thereby functioning as a bridge between the DataSet and
data source.
A complete .NET data provider supports the minimum functionality described here, as well as connected
data access using connections, commands, and transactions. A complete .NET data provider implements
the complete set of IData* and IDb* interfaces.
When developing a custom provider, you must first identify the ADO.NET interfaces and classes that
must be implemented to achieve the required functionality. Unsupported classes and methods should
raise a NotSupportedException or a NotImplementedException as appropriate.
Table 2-2 describes the available ADO.NET interfaces.

ADO.NET interfaces

Interface
IDbConnection

IDbTransaction

IDbCommand

IDataParameter

Description
A unique session that communicates with the data source.
A local transaction. This interface supports nested transactions, although
providers aren't required to support them.
Represents a query, stored procedure, or command against the data source.
Exposes properties and methods required to define parameters for
commands. Implementing this interface is optional for an IDataAdapteronly provider.

IDataParameterCollection Allows a user to implement a parameter to a command and its mapping to


DataSet columns.
IDataReader
IDataAdapter

A read-only, forward-only stream of data from the data source.


A DataAdapter that populates a DataSet and reconciles any changes to the

Ravi Varma Thumati (www.rthumati.wordpress.com)

Page 20 of 51

ASP.NET

ADO.NET interfaces

Interface

Description
DataSet back to the data source.
This is the only
implementation.

interface

required

for

every

.NET

data

provider

A DataAdapter for use with a relational database that inherits from


IDataAdapter. It populates a DataSet and reconciles any changes to the
DataSet back to the data source.
IDbDataAdapter

The .NET Framework also includes a utility class called DbDataAdapter that
can be inherited along with IDbDataAdapter, which helps implement the
IDbDataAdapter interface.

A custom adapter can provide access to data stored in a relational database. It is important to
remember that there are no constraints as to how the ADO.NET disconnected classes are filled and how
the changed data is updated back to the data source. Consider a solution other than developing a
custom .NET data provider, if it is appropriate.
ADO.NET DataSets
The DataSet object is central to supporting disconnected, distributed data scenarios with ADO.NET. The
DataSet is a memory-resident representation of data that provides a consistent relational programming
model regardless of the data source. It can be used with multiple and differing data sources, with XML
data, or to manage data local to the application. The DataSet represents a complete set of data,
including related tables, constraints, and relationships among the tables. The following illustration shows
the DataSet object model.
DataSet object model

Ravi Varma Thumati (www.rthumati.wordpress.com)

Page 21 of 51

ASP.NET

The DataSet is composed of two primary objects: the DataTableCollection and the
DataRelationCollection. The DataTableCollection contains zero or more DataTable objects, which are in
turn made up of three collections: Columns, Rows, and Constraints.The DataRelationCollection contains
zero or more DataRelations.
The DataTables Columns collection defines the columns that compose the DataTable. In addition to
ColumnName and DataType properties, a DataColumns properties allow you to define such things as
whether or not it allows nulls (AllowDBNull), its maximum length (MaxLength), and even an expression
that is used to calculate its value (Expression).
The DataTables Rows collection, which may be empty, contains the actual data as defined by the
Columns collection. For each Row, the DataTable maintains its original, current, and proposed values. As
well see, this ability greatly simplifies certain kinds of programming tasks.
The DataTables Constraints collection contains zero or more Constraints. Just as in a relational
database, Constraints are used to maintain the integrity of the data. ADO.NET supports two types of
constraints: ForeignKeyConstraints, which maintain relational integrity (that is, they ensure that a child
row cannot be orphaned), and UniqueConstraints, which maintain data integrity (that is, they ensure
that duplicate rows cannot be added to the table). In addition, the PrimaryKey property of the DataTable
ensures entity integrity (that is, it enforces the uniqueness of each row).
Finally, the DataSets DataRelationCollection contains zero or more DataRelations. DataRelations provide
a simple programmatic interface for navigating from a master row in one table to the related rows in
another. For example, given an Order, a DataRelation allows you to easily extract the related
OrderDetails rows.

Ravi Varma Thumati (www.rthumati.wordpress.com)

Page 22 of 51

ASP.NET

Note: however, that the DataRelation itself doesnt enforce relational integrity. A Constraint is used for
that.
The methods and objects in a DataSet are consistent with those in the relational database model.
The DataSet can also persist and reload its contents as XML, and its schema as XML schema definition
language (XSD) schema.
The DataTableCollection
An ADO.NET DataSet contains a collection of zero or more tables represented by DataTable objects.
The DataTableCollection contains all the DataTable objects in a DataSet.
A DataTable is defined in the System.Data namespace and represents a single table of memoryresident data. It contains a collection of columns represented by a DataColumnCollection, and
constraints represented by a ConstraintCollection, which together define the schema of the table. A
DataTable also contains a collection of rows represented by the DataRowCollection, which contains the
data in the table. Along with its current state, a DataRow retains both its current and original versions to
identify changes to the values stored in the row.
The DataView Class
A DataView enables you to create different views of the data stored in a DataTable, a capability that is
often used in data-binding applications. Using a DataView, you can expose the data in a table with
different sort orders, and you can filter the data by row state or based on a filter expression.
The DataRelationCollection
A DataSet contains relationships in its DataRelationCollection object. A relationship, represented by the
DataRelation object, associates rows in one DataTable with rows in another DataTable; A relationship
is analogous to a join path that might exist between primary and foreign key columns in a relational
database. A DataRelation identifies matching columns in two tables of a DataSet.
Relationships enable navigation from one table to another in a DataSet. The essential elements of a
DataRelation are the name of the relationship, the name of the tables being related, and the related
columns in each table. Relationships can be built with more than one column per table by specifying an
array of DataColumn objects as the key columns. When you add a relationship to the
DataRelationCollection, you can optionally add a UniqueKeyConstraint and a ForeignKeyConstraint
to enforce integrity constraints when changes are made to related column values.
XML
You can fill a DataSet from an XML stream or document. You can use the XML stream or document to
supply to the DataSet either data, schema information, or both. The information supplied from the XML
stream or document can be combined with existing data or schema information already present in the
DataSet.
ExtendedProperties
The DataSet, DataTable, and DataColumn all have an ExtendedProperties property.
ExtendedProperties is a PropertyCollection where you can place custom information, such as the
SELECT statement that was used to generate the result set, or the time when the data was generated.
The ExtendedProperties collection is persisted with the schema information for the DataSet.
LINQ to DataSet
LINQ to DataSet provides language-integrated querying capabilities for disconnected data stored in a
DataSet. LINQ to DataSet uses standard LINQ syntax and provides compile-time syntax checking, static
typing, and IntelliSense support when you are using the Visual Studio IDE.
Choosing a DataReader or a DataSet
When deciding whether your application should use a DataReader or a DataSet, you should consider
the type of functionality that your application requires. Use a DataSet to do the following:

Ravi Varma Thumati (www.rthumati.wordpress.com)

Page 23 of 51

ASP.NET

Remote data between tiers or from an XML Web service.

Interact with data dynamically such as binding to a Windows Forms control or combining and
relating data from multiple sources.

Cache data locally in your application.

Provide a hierarchical XML view of relational data and use tools like an XSL Transformation or an
XML Path Language (XPath) Query on your data.

Perform extensive processing on data without requiring an open connection to the data source,
which frees the connection to be used by other clients.

If you do not require the functionality provided by the DataSet, you can improve the performance of
your application by using the DataReader to return your data in a forward-only read-only fashion.
Although the DataAdapter uses the DataReader to fill the contents of a DataSet, by using the
DataReader you can receive performance gains because you will save memory that would be
consumed by the DataSet, as well as saving the processing required to create and fill the contents of
the DataSet.

Ravi Varma Thumati (www.rthumati.wordpress.com)

Page 24 of 51

ASP.NET

ADO.NET in Visual Studio .NET


Because data access is so central to the design of most corporate enterprise applications, VS .NET
includes a wealth of features designed to make using ADO.NET simple. These include the use of the
Server Explorer, component designer, and wizards to graphically manipulate the underlying database
and write the code to work with the ADO.NET classes.
To begin, let's walk through the construction of a very simple ASP.NET application in C#. This
application queries the ComputeBooks product catalog and allows the user to make some modifications
to demonstrate the graphical features of VS .NET that can be used with ADO.NET.
First, of course, you need to create an ASP.NET Web site using the New Project dialog accessed by
clicking File, New Project or through the New Project button on the VS .NET Start Page. In this case,
under Visual C# projects, select ASP.NET Web Application and call it ComputeBooksSimple. When
completed, a new virtual directory will have been created on the Web server and the project will contain
AssemblyInfo.cs, WebForm1.aspx, Global.asax, and Web.config files.
Using the Server Explorer
The Server Explorer window is available in all VS .NET projects and can be used for viewing resources
such as message queues, event logs, performance counters, services, and databases on a local or
remote machine. The purpose of the Server Explorer is to enable graphical interaction with these
services and visual designers within the development environment. It can be accessed from the View
menu or by typing Ctrl+Alt+S. Like other windows in VS .NET, it will by default auto-hide itself when
your cursor is not over it. To pin it to the surface, use the pin icon in the upper-right corner.
In addition to the server-based resources, the Server Explorer also contains a Data Connections node
that can be used to connect to and view a particular data source. If you're familiar with the Visual Studio
6.0 IDE, you'll recognize this as analogous to the Visual Data Tools.
Creating a Connection
To create a connection to a data source, simply right-click on the Data Connections node and click Add
Connection. You'll notice that you can create both a SQL Server database and a data connection
simultaneously by selecting Create New SQL Server Database.
The resulting dialog, shown in Figure 2.1, is the familiar Data Link Properties dialog. Through this dialog,
you can configure the connection. By default, it assumes you're going to connect to SQL Server,
although this can be changed by selecting the appropriate OLE DB provider in the Provider tab. In this
case we're going to connect to the ComputeBooks database you created during the exercise, "ADO.NET
in Perspective," on the local server (denoted by using a "." in the server name field), and authenticate
using Windows NT integrated security. Obviously, you would change these settings if the location or
authentication requirements of your server differed.
Figure2.1: Data Link Properties. This dialog is used to create a data connection in the Server
Explorer.

Ravi Varma Thumati (www.rthumati.wordpress.com)

Page 25 of 51

ASP.NET

Note
Note that even though we're using SQL Server for our data connection, we're not yet using the SQL
Server .NET Data Provider. The connections made through the Server Explorer use the SQL Server OLE
DB provider. This is evident by selecting the connection in the Server Explorer window and choosing
Properties.
When it's connected, the new connection will appear in the Server Explorer. You can then drill down
through it to view the tables, views, and stored procedures. By double-clicking on a table, you can view
and edit the data (depending on your permissions as defined by how you authenticated). Figure 2.2
shows the IDE after double-clicking the Titles table and using the Query toolbar to activate all the panes
(diagram, grid, SQL, results) available. You can use the panes to modify the query, as was done in this
case, to show only some of the data by selecting particular columns or to add a where clause or sorting
condition.
Figure 2.2. Using a data connection. You can use a data connection to inspect and edit the
data in the underlying database.

Ravi Varma Thumati (www.rthumati.wordpress.com)

Page 26 of 51

ASP.NET

Tip
In the Professional edition of VS .NET, you can view and edit data and execute views and stored
procedures, but cannot create and alter these objects. These features are enabled in the Enterprise
Developer and Enterprise Architect versions of the product. To ensure that all readers will be able to
follow along, in this book I'll use the features of the Professional edition.
Creating a Data Adapter
When the connection (or reference) has been established to the database, you can use it to incorporate
data access code into your project. To do so, simply drag and drop an object from the connection onto a
designer in the IDE. For example, to create code to access the Titles table, drag and drop the table onto
the WebForm1.aspx designer surface.
As a result of dragging and dropping the table onto the WebForm1.aspx designer surface, two objects,
sqlConnection1 and sqlDataAdapter1, will be created and placed at the bottom of the designer surface.
By dropping the table, VS .NET assumed you wanted to connect to the database and select, insert,
update, and delete data from the table and so it added both objects to the form. You can then inspect
and change the default properties it set for the objects by clicking on them and viewing their properties
in the Properties window. To provide easier configuration, if the Data Adapter Configuration Wizard does
not open automatically, you can invoke it by right-clicking on the sqlDataAdapter1 object and selecting
Configure Data Adapter.
This wizard has several interesting features, including the ability to generate SQL statements and stored
procedures to populate the data adapter. To begin, it allows you to choose an existing data connection
or create a new one on the fly. After selecting the connection, you are presented with the dialog shown
in Figure 2.3.
Figure 2.3. Choose a query type. This part of the Data Adapter Configuration Wizard allows
you to specify how data from the database is accessed.

Ravi Varma Thumati (www.rthumati.wordpress.com)

Page 27 of 51

ASP.NET

In this case, you'll choose Create new stored procedures and click Next. From here, you can specify a
SQL statement to use as the basis for specifying the data that the data adapter will access, or you can
use the query builder to build a SQL statement graphically. By default, a SELECT statement with all the
columns will be created. In this case, either use the Query Builder or simply modify the SQL to remove
the Cover column from the SELECT clause and sort the result set in ascending order by title. This dialog
also contains the Advanced Options button, which controls how the stored procedures are to be written.
The three options allow you to specify

Whether insert, update, and delete statements are generated in addition to a select statement
Whether optimistic concurrency is used when formulating the WHERE clauses in the update and
delete stored procedures

Whether a SELECT statement will be added to the insert and update stored procedures

The third option to refresh the DataSet is useful, especially in situations where the table contains
server-generated values such as IDENTITY columns and default values in order to make sure they are
visible if the modifications are successful. Click OK to close the Advanced Options dialog and click Next
to move to the Create the Stored Procedures step shown in Figure 2.4.
Figure 2.4. Naming procedures. This step of the Data Adapter Configuration Wizard allows
you to pick names for the stored procedures, and optionally review and save the
script used to create them.

Ravi Varma Thumati (www.rthumati.wordpress.com)

Page 28 of 51

ASP.NET

In the step shown in Figure 2.4, you have the option of naming the stored procedures that are about to
be created and either allowing the wizard to create the procedures or saving the script and running it at
a later time. You should use the latter option when you do not have permissions to create objects in the
database or if you want to further modify the script by, for example, adding additional SQL Data
Definition Language (DDL) statements to it.
As shown in the figure, you should publish and use a naming convention in your organization for your
procedures so that other developers can easily identify them. In this example, we're using the
convention where the procedure is prefixed with usp (so as not to be confused with the sp prefix
reserved for system stored procedures), followed by Select, Ins, Upd, or Del, depending on the function
of the procedure and finally the name of the table affected.
You should then preview the SQL script using the appropriate button to make sure that the script is as
you specified. By clicking Next, the wizard presents a final checklist and then creates the procedures and
writes the necessary code in the Web Form to instantiate, populate, and invoke the SqlConnection and
SqlDataAdapter objects.
In this example, in addition to querying data for the Titles, we also need to query data from the
Categories table because there is a foreign key relationship between Titles and Categories (each book
belongs to exactly one category). To query the Categories table, simply drag and drop it on the Web
Form and once again invoke the Data Adapter Configuration Wizard. This time, however, instead of
creating stored procedures, select Use existing stored procedures and select the existing usp_SelectCats
stored procedure for the Select command. Because the Categories table will not be modified in this
application, you needn't fill in the rest of the commands. You'll notice that the wizard allows you to
modify the names of the columns in the data row that map to the parameters that are required by the
stored procedure. The data adapter contains a mapping collection that is used to map columns in the
data source to those in the DataSet. By default, mappings are assumed to use identical names and so
you don't need to change the default values.

Ravi Varma Thumati (www.rthumati.wordpress.com)

Page 29 of 51

ASP.NET

After finishing the wizard, you will now see two data adapter objects and one SqlConnection in the
designer. To make things simpler to read, you can rename sqlDataAdapter1 to daTitles and
sqlDataAdapter2 to daCategories using the Properties window.
As a final touch, invoke the Table Mappings dialog for the daCategories object by clicking on the
ellipses button next to the TableMappings property in the Properties window. The dialog shown in
Figure 2.5 can be used to modify the table mappings discussed previously. It can also be used to set the
name of the table in the DataSet this data adapter will fill. Change the name from usp_SelectCats
to Categories.
Figure 2.5. Table mappings. This dialog is primarily used to provide the mapping layer
between a DataSet and the data adapter.

Tip
After the data adapters are configured, you can preview them by choosing the Preview Data option on
their context menus. The resulting dialog simply allows you to see the data selected by the adapter, and
that will be used to populate a DataSet.
Creating a DataSet
Now that the project contains two data adapters that will provide the communication to the data source,
you must add a DataSet to cache the data for display and modification. To do so, right-click on the
designer near the objects you just created and select Generate DataSet.
The Generate DataSet dialog presents a list of the data tables it found in the Web Form; in this case,
finding both the Titles and Categories tables from the data adapters. By selecting both tables, the tool

Ravi Varma Thumati (www.rthumati.wordpress.com)

Page 30 of 51

ASP.NET

will create a DataSet that contains two distinct tables that can contain data from different data adapters.
The obvious implication is that DataSet objects can be used to store and combine heterogeneous data
easily. You can also change the name of the DataSet; in this case, change it to dsTitles.
The DataSet then will be added to the project (viewable in the Solution Explorer window) as
dsTitles.xsd (because the structure of a DataSet is represented by the XML Schema Definition [XSD]
grammar). By double-clicking on the DataSet, the schema editor will be invoked showing the two data
tables side by side. As mentioned previously, in the ComputeBooks database, the Titles and
Categories tables have a foreign key relationship. To represent that relationship in the DataSet, you
need to create a relation by right-clicking on the CatID element (column) and selecting Add, New
Relation. You can then use the Edit Relation dialog, shown in Figure 2.6, to set the properties of the
relationship, including the name of the relation, the parent and child elements, the fields that will
participate, and any additional constraints such as mandating cascading deletes or setting the child field
to Null in the event the parent field is deleted. In this case, the child element should be set to Titles,
which will automatically change the name as well.
Figure 2.6. Editing relations. You can visually edit and define relationships between data
tables in a DataSet using the Edit Relation dialog.

By clicking OK, the relation will be created and depicted with a dotted line.
At this point, you can view the XSD syntax for the DataSet by clicking on the XML pane at the bottom
of the editor. You'll see that beneath the DataSet is a file called dsTitles.cs. This code file contains the
programmatic definition of a typed DataSet that maps to the schema.

Ravi Varma Thumati (www.rthumati.wordpress.com)

Page 31 of 51

ASP.NET

Viewing the Code


One glaring fact should be pointed out before going any further: Up until this point, we've not written a
single line of code in order to declare, instantiate, populate, or invoke any of the objects we've
manipulated graphically. So, where's the code?
To access the code for the Web Form, right-click on the designer and choose View Code. The code for
the form should appear similar to that shown in Listing 2.1.
Listing 2.1 The generated code. This listing shows the code generated by the various wizards
used thus far today.
using
using
using
using
using
using
using
using
using
using

System;
System.Collections;
System.ComponentModel;
System.Data;
System.Drawing;
System.Web;
System.Web.SessionState;
System.Web.UI;
System.Web.UI.WebControls;
System.Web.UI.HtmlControls;

namespace ComputeBooksSimple
{
/// <summary>
/// Summary description for WebForm1.
/// </summary>
public class WebForm1 : System.Web.UI.Page
{
protected System.Data.SqlClient.SqlDataAdapter daTitles;
protected System.Data.SqlClient.SqlDataAdapter daCategories;
protected System.Data.SqlClient.SqlCommand sqlSelectCommand2;
protected System.Data.SqlClient.SqlCommand sqlSelectCommand1;
protected System.Data.SqlClient.SqlCommand sqlInsertCommand1;
protected System.Data.SqlClient.SqlCommand sqlUpdateCommand1;
protected System.Data.SqlClient.SqlCommand sqlDeleteCommand1;
protected ComputeBooksSimple.dsTitles dsTitles1;
protected System.Data.SqlClient.SqlConnection sqlConnection1;
private void Page_Load(object sender, System.EventArgs e)
{
// Put user code to initialize the page here
}
#region Web Form Designer generated code
#endregion
}

Creating the User Interface


The only remaining task to make this application functional is to put a user interface on the Web Form
and write some minimal code to get things rolling.
To begin, you'll need to drag and drop a DataGrid control, found in the Toolbox on the Web Forms tab,
onto the Web Form. VS .NET ships with a variety of controls that can be bound to a DataSet, data

Ravi Varma Thumati (www.rthumati.wordpress.com)

Page 32 of 51

ASP.NET

reader, or in fact any object whose class implements the IEnumerable interface. When the grid is
positioned and sized correctly, right-click on it and chooses Property Builder to invoke the Properties
dialog. From the Properties dialog, you can configure almost all the properties necessary to allow the
user to display and edit the information in the product catalog.
Under the General tab, you first must bind the DataGrid to the dsTitles1 DataSet by choosing it
from the drop-down menu. In addition, you must set the DataMember property to Titles because
dsTitles1 contains two tables and therefore can be used to display both Titles and Categories
data. You can then set the Data key field option to ISBN because ISBN is the primary key of the
Titles table, as shown in Figure 2.7.
Figure 2.7. The DataGrid Properties dialog. The General tab is used to set up data binding on
the DataGrid.

You'll do most of your work under the Columns tab. As you'll notice, the Create columns automatically at
run time option are selected by default and will set up the grid for you automatically. In this case,
because you want to allow editing of the grid, you should uncheck the box and select (All Fields) from
the Available columns list and move it across to the Selected columns list using the arrow button.
As you scroll through the Select columns list, you'll notice that properties, such as the header text, can
be set for each column. In this case, because you want the user to be able to edit only certain columns,
you can go through the columns and mark ISBN, Title, Author, Publisher, and PubDate as read-only. In
addition, because several of the columns (BulkDiscount, Discount, and Price) display currency, change
their data formatting expression to "{ 0:c} " in order to display the amounts correctly.

Ravi Varma Thumati (www.rthumati.wordpress.com)

Page 33 of 51

ASP.NET

For the grid to allow the user to shift a particular row into edit mode and subsequently update that row,
you must add a Edit, Update, Cancel column to the grid found under the Button Column node in the
Available columns list. After selecting it and moving to the Selected columns list, be sure to move it to
the top of the list using the arrow button.
The only remaining task in the Columns tab is to transform the editable columns into template columns.
Simply put, template columns can be used in a DataGrid control to render data items differently
depending on the edit mode. In this case, we want to make sure that each editable column is rendered
with a TextBox control to allow the user to change its values. To create the template columns, simply
click on each editable column (Description, BulkAmount, BulkDiscount, Discount, and Price) in the Select
columns list and click the link found at the bottom of the dialog to convert the column into a template
column.
Also, in the Properties dialog, you can set the paging options using the Paging tab to ensure that only a
specific number of rows is visible at any particular time. In this case, you might check the Allow paging
option and set the page size to 5. Other navigation options are available, although the defaults will
suffice in this case. Of course, you might also want to format the grid using the Format tab, for
example, to change the font and size of the text that displays for the DataGrid, header, footer, or to
even alternate items. After you've set the options you want, click OK and the grid should appear with
the bound columns on the Web Form, as shown in Figure 2.8.
Figure 2.8. A formatted DataGrid control. Note that the Properties window can also be used
to configure properties such as data binding for the grid.

Tip
To easily format the grid with a professional appearance, right-click on it in the designer and select Auto
Format. The grid shown in Figure 2.8 has been formatted using the Colorful 2 option.
As mentioned previously, in this example we're using template columns to allow users to edit some of
the columns when they click on the Edit link for a particular row. To provide specific names for the

Ravi Varma Thumati (www.rthumati.wordpress.com)

Page 34 of 51

ASP.NET

TextBox controls that are displayed, you can right-click the grid and select Edit Template. You'll see the
five templated columns displayed. Click on each one in turn and the grid will show the template editor.
Note that a templated column can render the header template, item template (the default view of the
column), edit template, and footer template. The EditItemTemplate should already contain a TextBox,
so you can simply click on it and change its ID in the Properties dialog. The names you can use might
include txtDesc, txtBulkAmount, txtBulkDiscount, txtDiscount, and txtPrice. When you've renamed each
of the controls, select End Template Editing from the context menu.
In addition to the grid control, add a Label control from the Web Forms tab of the toolbox to the top of
the Web Form. This control will be used for rudimentary error handling, so rename the control lblError.
You can place it at the top of the page.
At this point, we need to add code to the page to query the database and bind the data to the grid, to
handle the paging of the grid, and to respond to the user clicking on Edit, Update, or Cancel for a
particular row. To view the code, again right-click on the designer and select View Code or double-click
on the WebForm1.aspx.cs code file in the Solution Explorer.
Retrieving Data
First, in a Web Form, the Load event of the page is called each time the page is first loaded and when
the page is posted back to the Web server in response to a user action such as clicking the Edit link. To
make the data from the database available to code in the page, you must write code that queries the
database, populates the DataSet, and binds it to the DataGrid control. To do so, you can write the code
shown in Listing 2.2.
Note
One of the great things about ASP.NET is that it provides an event-driven programming model for Webbased applications similar to that familiar to developers who have used Visual Basic in the past. As in
this example, you can simply write code that handles events fired (actually posted) as the user
manipulates the page. The ASP.NET page framework handles the details of posting the events and
calling the correct event handlers on the server side. For more information about the sequence and
processing that takes place, see Chapter 11 of my book Building Distributed Applications with Visual
Basic .NET, published by Sams.
Listing 2.2 Page Load event. The Load event of the page can be used to populate the
DataSet and bind it to controls on the page.
private void Page_Load(object sender, System.EventArgs e)
{
// Fill the dataset
daCategories.Fill(dsTitles1, "Categories");
daTitles.Fill(dsTitles1, "Titles");

if (!Page.IsPostBack)
{
// Bind the Data
DataGrid1.DataBind();
}

Allowing Navigation
Next, you can add code for the PageIndexChanged event raised when the user navigates to a new
page. Although the Properties dialog sets up all the required properties, it does not write the code to
handle the event, which is shown in Listing 2.3.

Ravi Varma Thumati (www.rthumati.wordpress.com)

Page 35 of 51

ASP.NET

Listing 2.3 Handling navigation. The PageIndexChanged event will fire when the user
navigates to a new page within the grid control.
private void DataGrid1_PageIndexChanged(object source,
DataGridPageChangedEventArgs e)
{
DataGrid1.CurrentPageIndex = e.NewPageIndex;
DataGrid1.DataBind();
}
Handling Editing Events
Finally, the most sophisticated code you need to write handles the three events raised when the user
initially selects a row for editing, updates the row, or chooses the cancel option. Keep in mind that these
options are available because you added the button column to the grid in the Property Builder dialog.
The three events can be handled using the code shown in Listing 2.4.
Listing 2.4 Handling editing events. This listing shows how you can handle the events fired
when the user edits data in a DataGrid control.
private void DataGrid1_EditCommand(object source, DataGridCommandEventArgs e)
{
DataGrid1.EditItemIndex = e.Item.ItemIndex;
DataGrid1.DataBind();
}
private void DataGrid1_CancelCommand(object source, DataGridCommandEventArgs e)
{
DataGrid1.EditItemIndex = -1;
DataGrid1.DataBind();
}
private void DataGrid1_UpdateCommand(object source, DataGridCommandEventArgs e)
{
// Extract the data from the template controls
TextBox descBox = (TextBox)(e.Item.Cells[1].FindControl("txtDesc"));
string desc = descBox.Text;
TextBox baBox = (TextBox)(e.Item.Cells[1].FindControl("txtBulkAmount"));
string bulkAmount = baBox.Text;
TextBox bdBox = (TextBox)(e.Item.Cells[1].FindControl("txtBulkDiscount"));
string bulkDiscount = bdBox.Text;
TextBox disBox = (TextBox)(e.Item.Cells[1].FindControl("txtDiscount"));
string discount = disBox.Text;
TextBox prBox = (TextBox)(e.Item.Cells[1].FindControl("txtPrice"));
string price = prBox.Text;
try
{
// Update the DataSet
dsTitles1.Titles[e.Item.DataSetIndex].Description = desc;
dsTitles1.Titles[e.Item.DataSetIndex].BulkAmount =
System.Int16.Parse(bulkAmount);
dsTitles1.Titles[e.Item.DataSetIndex].BulkDiscount =
System.Decimal.Parse(bulkDiscount);
dsTitles1.Titles[e.Item.DataSetIndex].Discount =

Ravi Varma Thumati (www.rthumati.wordpress.com)

Page 36 of 51

ASP.NET

System.Decimal.Parse(discount);
dsTitles1.Titles[e.Item.DataSetIndex].Price =
System.Decimal.Parse(price);
// Update the database
daTitles.Update(dsTitles1.GetChanges());
}
catch (Exception ex)
{
lblError.Text = "An error occurred:" + ex.Message;
return;
}

// Switch out of edit mode.


lblError.Text = "";
DataGrid1.EditItemIndex = -1;
DataGrid1.DataBind();

The code in the UpdateCommand, however, is more complicated because it's responsible for extracting
the modified data from the TextBox controls and saving the data to the database. As you can see, the
first section of the code extracts the values from the Text property of the TextBox controls by finding
the control in the current row using the FindControl method. The Item (row) that is being edited is
passed in the DataGridEventArgs object and its cells accessed through the Cells collection.
Next, the DataSet and database updates are wrapped in a try catch block in order to intercept any
exceptions that might be thrown and display the message in the lblError control before exiting the
method. Exiting the method before changing the EditItemIndex property back to 1 ensures that the
grid will remain in edit mode, and gives the users a chance to correct their errors and resubmit the
changes. The DataSet needs to be updated because while the columns in the grid are bound to the
DataSet for display, they do not automatically update the DataSet. In this case, the DataSetIndex
property of the Item can be used to determine which row in the DataSet was changed, and then its
properties are set accordingly using the Titles DataTable object exposed by the dsTitles class. Note that
the data types of the columns must be honored, so the strings must be converted to short or decimal
data types in this case.
Note
As you learned yesterday, the ability to access data both relationally and in an object-oriented syntax
was a design goal for ADO.NET. The typed DataSet dsTitles exposes objects such as Titles that make
accessing the underlying data much easier.
To actually perform the database update, call the Update method of the daTitles data adapter and pass
it a DataSet to update. The GetChanges method of the DataSet simply creates a DataSet with only the
modified rows in it. This obviously cuts down on the amount of data being passed to the method, and is
particularly effective when passing data between processes or machines in a distributed application. The
Update method looks for modified and deleted rows and invokes its UpdateCommand or
DeleteCommand, respectively. In this case, the usp_UpdTitles stored procedure that was created
previously will be called with the new data.
If no exceptions occur, the EditItemIndex property is set back to 1 to take the grid out of edit mode
and the new data is bound to the grid for display.
Binding the Events

Ravi Varma Thumati (www.rthumati.wordpress.com)

Page 37 of 51

ASP.NET

The final step is to bind the three methods in Listing 2.4 and the method for the PageIndexChanged
event to the events of the grid control. This can be done graphically by clicking on the lightning bolt in
the Properties window for the grid and dropping down the appropriate event and selecting the methods
you just created. This adds code like that shown in Listing 2.5 to the Web Form Designer generated
code in the InitializeComponent method.
Listing 2.5 Binding the events. This listing shows how you add event handlers for the events
of the DataGrid control.
this.DataGrid1.PageIndexChanged += new
System.Web.UI.WebControls.DataGridPageChangedEventHandler(
this.DataGrid1_PageIndexChanged);
this.DataGrid1.CancelCommand += new
System.Web.UI.WebControls.DataGridCommandEventHandler(
this.DataGrid1_CancelCommand);
this.DataGrid1.EditCommand += new
System.Web.UI.WebControls.DataGridCommandEventHandler(
this.DataGrid1_EditCommand);
this.DataGrid1.UpdateCommand += new
System.Web.UI.WebControls.DataGridCommandEventHandler(
this.DataGrid1_UpdateCommand);
Note
In case you're not familiar with C#, the this operator is used to reference members of the current
instance. In other words, this.DataGrid1 refers to the instance of the DataGrid1 object associated with
the current page.
You can then run the page in the browser and it should appear as shown in Figure 2.9. Note that in this
figure the Edit link for the row has been clicked and the row's data has been edited. The product catalog
application can be tested by modifying the values and clicking Update to save the record to the
database.
Figure 2.9. Running the product catalog. Here the row is in edit mode and its data can be
modified.

Ravi Varma Thumati (www.rthumati.wordpress.com)

Page 38 of 51

ASP.NET

Ravi Varma Thumati (www.rthumati.wordpress.com)

Page 39 of 51

ASP.NET

ADO.NET Classes
Main Namespaces Used to Access ADO.NET Objects
Name Space

Contains

System.Data

ADO.NET base objects.

System.Data.OleDb

Managed OLDDB data store objects

System.Data.SqlClient

SQL Server-specific implementation


of the ADO.NET objects

System.Data.OracleClient

Oracle specific implementation of the ADO.NET


objects.

System.Data.ODBC

ODBC specific implementation of the ADO.NET


objects

Before you can access any part of a database, you need to establish a connection to it. You can use
either the OleDbConnection or SqlConnection object to represent that connection.
By creating an instance of a Connection object, and specifying data source-specific information in its
properties, you can build a predefined link between the data consumer (your ASP.NET application) and
the data provider (your database). Then, once you have established the connection to the data source,
you can use the connection, in concert with other ADO.NET objects, to execute commands directly
against the data source, to execute stored procedures, and to retrieve and manipulate data.
The Connection object also offers you the benefit of connection poolinga mechanism that keeps
connections pooled after you have explicitly closed them. Repeatedly opening and closing connections
may consume Web server resources and can be a time-consuming process. Connection pooling is
effectively a method of improving performance when a Web server is regularly accessed.
Creating a Connection
With ADO, ADO.NETs predecessor, you, as the developer, could create Connection objects explicitly or
through another object such as a Command object. That is no longer the case. With ADO.NET you must
explicitly open your connections using one of its constructors. Some developers might be taken aback by
this feature, however, we recommend that developers create their connection objects explicitly for two
reasons: (1) the code is easier to maintain and (2) connection pooling can be utilized.
In order to use the OLEDB Managed Provider objects, such as OleDbConnection, OleDbCommand,
and so on, you need to include the OleDb namespace in your ASP.NET page. To do this, you use the
Import construct at the top of your ASP.NET page:
<%@ Import Namespace=System.Data.OleDb %>
For SQL
<%@ Import Namespace=System.Data.SQLClient %>
Now youre ready to create your connection object. In the following code, weve used VB to create a
connection object called oConn:
<%@ Import Namespace=System.Data.OleDb %>
<script language=VB runat=server>
Dim oConn As New OleDbConnection

Ravi Varma Thumati (www.rthumati.wordpress.com)

Page 40 of 51

ASP.NET

</script>
The ADO.NET Connection objects, OleDb and Sql, have several constructors. A constructor is essentially
the syntax you use to instantiate an object. Any given object can have several constructors, like the
Connection objects, or no constructors, like the DataReader objects, which need to be instantiated or
created by another object. In the previous code snippet, we demonstrated the use of the
OleDbConnections default constructor that does not accept any parameters. The Connection objects
have another constructor that accepts a connection string as its only parameter.
Here is a snippet of code that opens, using the OleDbConnection objects Open method, a connection
to a SQL Server database named Music that is located on my local machine:
Sub Page_Load(Sender As Object, E As EventArgs)
Dim oConn As OleDBConnection
Dim sConnString As String
sConnString = Provider=SQLOLEDB;Data Source=(local);Initial
Catalog=Music;User ID=music;Password=music
oConn = New OleDBConnection(sConnString)
oConn.Open()
oConn.Close()
End Sub
Opening a Connection
In order to open a connection to a data source, you need to know a little about the database. Kind of
like making a phone call, you need to have a phone number. When opening a connection, you need to
supply several pieces of information depending on the Relational Database Management System
(RDBMS) you are using. Some of these pieces of information could be server name or IP address,
database name, user name, and password. With this crucial information, you will construct a connection
string that is effectively a collection of name/value pairs, separated by semicolons, which tell the
Connection, object how to connect to your database. The information that you use to construct your
connection string will vary depending on the type of database to which you are trying to connect. Table
17-2 lists some of the most common parameters you will use to build a connection string
Parameters Used to Construct a Connection String
Parameter

Description

Provider

The OLEDB provider used to access the database.

Data Source

The IP address or name of the server on which the


database resides.

Database

The name of the database to be used once the


connection is open.

User ID

The user ID for the account used to access the


database.

Password

The password for the account used to access the


database.

Building a Command
There are many ways of building, or constructing, a command object with ADO.NET. You can explicitly
set the command objects properties, pass parameters into the command objects constructor, or a
combination of the two. Following are several examples of how to initialize (or construct) an
OleDbCommand object:

Ravi Varma Thumati (www.rthumati.wordpress.com)

Page 41 of 51

ASP.NET

oCmd = New OleDbCommand()


oCmd = New OledbCommand(sSQL)
oCmd = New OledbCommand(sSQL, oConn)
In the previous listing, oConn is an OleDbConnection object and sSQL is a query command string.
sqlCmd = New SQLCommand()
sqlCmd = New SQLCommand (sSQL)
sqlCmd = New SQLCommand (sSQL, sqlConn)
In the previous listing, sqlConn is an SQLConnection object and sSQL is a query command string.
The OleDbCommand/SQLCommand object has several properties that you can explicitly set. Some of
these properties are Connection, CommandText, CommandType, and CommandTimeout.
Connection property
The Connection property is used to set or get the connection against which to execute the command.
You must pass a valid Connection object to the Connection property or you will receive an error.
Below shows an example of how you might explicitly set the Connection property by passing it a valid
Connection object.
Example:
Sub Page_Load(Sender As Object, E As EventArgs)
Dim oConn As OleDbConnection
Dim oCmd As OleDbCommand
Dim sSQL As String
Dim sConnString As String
sSQL = SELECT * FROM t_bands
sConnString = Provider=SQLOLEDB;Data Source=(local);Initial
Catalog=Music;User ID=music;Password=music
oConn = New OleDbConnection
With oConn
.ConnectionString = sConnString
.Open()
End With
oCmd = New OledbCommand(sSQL)
With oCmd
.Connection = oConn
End With
oConn.Close()
End Sub
CommandText property
The CommandText property gives you a means of holding your command (as a string) for later
execution. It can contain a SQL statement, a stored procedure name, or a table name. For example, you
can assign a simple SQL statement to the CommandText property as follows:
oCmd.CommandText = SELECT band_id, band_title, music_type_id, record_company_id FROM
t_bands
Alternatively, you could assign a stored procedure name to the CommandText property and tell the
Command object you are using a stored procedure by setting the CommandType property
accordingly:

Ravi Varma Thumati (www.rthumati.wordpress.com)

Page 42 of 51

ASP.NET

oCmd.CommandText = prGetBands
oCmd.CommandType = CommandType.StoredProcedure
CommandType property

The CommandType property gets the CommandText or sets how it is interpreted. The possible
values, or enumerations, of the CommandType property are

StoredProcedure

TableDirect

Text

When the CommandType property is set to StoredProcedure, the CommandText property is


interpreted as a stored procedure. If the CommandType is set to TableDirect and the
CommandText property is set to a valid table name, then all the rows and columns for the
specified table are returned. This is generally not a good idea, for performance reasons, when
executing the command against a large database. Finally, if the CommandType property is set
to Text, then the CommandText is executed as a SQL text command.
Below is an example of how to execute a stored procedure called prCountBands.
Sub Page_Load(Sender As Object, E As EventArgs)
Dim
Dim
Dim
Dim

oConn As OledbConnection
oCmd As OledbCommand
sSQL As String
iBandCount As Integer

oConn = New OledbCOnnection(Provider=SQLOLEDB;Data


Source=(local);Initial Catalog=Music;User ID=music;Password=music)
oConn.Open()
oCmd = New OleDbCommand(sSQL, oConn)
oCmd.CommandType = CommandType.StoredProcedure
oCmd.CommandText = prCountBands
iBandCount = oCmd.ExecuteScalar()
oConn.Close()
lblBandCount.Text = iBandCount
End Sub
CommandTimeout property
The CommandTimeout property gets or sets the time, in seconds, to wait while executing the
command before terminating the attempt and generating an error. The syntax for setting the
CommandTimeout property follows:
oCmd.CommandTimeout = 60
The default value for the CommandTimeout property is 30 seconds. The CommandTimeout property
is not inherited from the commands Connection. The command objects CommandTimeout property
and the connection objects CommandTimeout property are completely disparate properties. The
Command objects CommandTimeout property sets the maximum amount of time, in seconds, for a
command to attempt to execute before returning an error. The Connection objects
ConnectionTimeout works the same way. The connection object attempts to open the connection for
a designed amount of time before returning an error.
Setting the CommandTimeout propertys value to 0 indicates that the command will attempt
to execute indefinitely. It is not a good practice to recommend this!

Ravi Varma Thumati (www.rthumati.wordpress.com)

Page 43 of 51

ASP.NET

Appending parameters
The Command object supports a collection property named Parameters. The Parameters property is
actually a ParameterCollection object that can contain more than one Parameter object. The
Parameters property enables you to append parameters to the Command object. Parameters are
generally attached to commands that are executing stored procedure that require input parameters. For
example, you could write the following stored procedure to return a bands title based on its band_id:
CREATE PROCEDURE prGetBandTitle
@iID AS INT
AS
SELECT band_title from t_bands WHERE band_id = @iID
RETURN
So how do you append parameters? First you create a Parameter object. A Parameter object can be
constructed in several ways. For now, well focus on constructing the Parameter objects by setting its
Properties explicitly rather than passing them into the Parameter object constructor. The properties
well set are ParameterName, DBType, and Value as follows:
oParam = New OleDbParameter()
oParam.ParameterName = @iID
oParam.DBType = OleDbType.Integer
oParam.Value = 1
The OledbParameter object supports an Add () method that you can call to append the Parameter
to your OLEDBCommand as shown in Listing.
Sub Page_Load(Sender As Object, E As EventArgs)
Dim
Dim
Dim
Dim

oConn As OleDbConnection
oCmd As OleDbCommand
oParam As OleDbParameter
sSQL As String

oConn = New OleDbConnection(Provider=SQLOLEDB;Data


Source=(local);Initial Catalog=Music;User ID=music;Password=music)
oConn.Open()
oCmd = New OleDbCommand(sSQL, oConn)
oCmd.CommandType = CommandType.StoredProcedure
oCmd.CommandText = prGetBandTitle
oParam = New OleDbParameter()
oParam.ParameterName = @iID
oParam.DBType = OleDbType.Integer
oParam.Value = 1
oCmd.Parameters.Add(oParam)
End Sub
Youll notice in Listing that we first created the OleDbCommand, oCmd object. Next, we constructed
the OleDbParameter oParam, object, and set its properties. Finally, we attached the
OledbParameter to the OleDbCommand object using the Parameters collections Add () method
with the following line of code:
oCmd.Parameters.Add(oParam)
The Parameter object supports many properties and methods and can become very complex.

Ravi Varma Thumati (www.rthumati.wordpress.com)

Page 44 of 51

ASP.NET

Executing a Command
Now that you know how to construct an Command object, it is time that you ask it do something. The
Command object has many useful methods, including the ExecuteReader(), ExecuteNonQuery(),
ExecuteScalar(), and Prepare() methods.
ExecuteNonQuery method
The ExecuteReader() and ExecuteNonQuery() methods are similar in that they both execute
commands against a data source. The main difference is the number of rows returned when the
command is executed. As indicated by its name, the ExecuteNonQuery() method does not return any
rows from the datasource; you probably wont use this command when executing a SQL SELECT
command. It could, however, be useful when executing INSERT, UPDATE or DELETE commands
depending on your requirements. The ExecuteNonQuery() method does not require, or for that matter
accept any parameters in its constructor. Here is a sample of calling the ExecuteNonQuery method:
oCmd = New OleDbCommand()
oCmd.Connection = oConn
oCmd.CommandType = CommandType.Text
oCmd.CommandText = UPDATE t_bands SET band_title = Hootie and The
Blowfish WHERE band_title = Hootie & The Blowfish
oCmd.ExecuteNonQuery()
Youll notice that we are executing a SQL UPDATE command so we probably dont want any records
returned. The ExecuteNonQuery() method does return the number of rows, as an integer, that were
affected by the executed command. So if you wanted to determine how many records were affected by
a command, you could use the following code:
Dim iAffected As Integer
iAffected = oCmd.ExecuteNonQuery()
ExecuteReader method
The ExecuteReader() method executes the CommandText against the commands Connection and
builds an object capable of forward-only data reads. This object is an OleDbDataReader. The syntax is
oDR = oCmd.ExecuteReader()
Where oDR is an OleDbDataReader object. Simple! Once you have populated the OleDbDataReader
object by calling the OleDbCommands ExecuteReader() method, you have access to the data. We
cover DataReader objects in detail in Session 19, Using DataReaders. Listing 18-5 demonstrates how
to populate a datareader via the command objects Execute method.
Sub Page_Load(Sender As Object, E As EventArgs)
Dim oConn As OleDbConnection
Dim oCmd As OleDbCommand
Dim oDR As OleDbDataReader
oConn = New OleDbConnection(Provider=SQLOLEDB;Data
Source=(local);Initial Catalog=Music;User ID=music;Password=music)
oConn.Open()
oCmd = New OleDbCommand()
With oCmd
.Connection = oConn
.CommandType = CommandType.StoredProcedure
.CommandText = prGetBands
oDR = oCmd.ExecuteReader()

Ravi Varma Thumati (www.rthumati.wordpress.com)

Page 45 of 51

ASP.NET

End With
While oDR.Read()
lstBands.Items.Add(New
ListItem(oDR.Item(band_title),oDR.Item(band_id)))
End While
oDR.Close()
oConn.Close()
End Sub
we use the following line of code to populate the DataReader object with the results of the query:
oDR = oCmd.ExecureReader()
ExecuteScalar() Method
The ExecuteScalar() method executes the CommandText against the commands Connection and
returns a single value which is the first column of the first row of the resulting rowset. The syntax is
iBandCount = oCmd.ExecuteScalar()
Where iBandCount is a integer value returns the result set of query. And it fast and efficient in cases
where a singleton value is required
Sub Page_Load(Sender As Object, E As EventArgs)
Dim
Dim
Dim
Dim

oConn As OledbConnection
oCmd As OledbCommand
sSQL As String
iBandCount As Integer

oConn = New OledbCOnnection(Provider=SQLOLEDB;Data


Source=(local);Initial Catalog=Music;User ID=music;Password=music)
oConn.Open()
oCmd = New OleDbCommand(sSQL, oConn)
oCmd.CommandType = CommandType.StoredProcedure
oCmd.CommandText = prCountBands
iBandCount = oCmd.ExecuteScalar()
oConn.Close()
lblBandCount.Text = iBandCount
End Sub
Prepare () method
The Prepare () method is used to create a prepared, or compiled, version of the command on the
datasource. This method is generally used only when the CommandType property is set to Text; but it
does improve performance when executing large SQL commands or dynamically generated SQL
commands that contain parameters. The syntax for preparing a command is
oCmd.Prepare()

Ravi Varma Thumati (www.rthumati.wordpress.com)

Page 46 of 51

ASP.NET

Introducing DataReader Class


So, what is a DataReader? A DataReader object is effectively a forward-only collection of records from
your data source. The interesting thing about DataReader is that they do not have a public constructor
per se. The DataReader is created via a Command objects ExecuteReader method. Another
interesting thing to note about DataReader objects is that, unlike many other ADO.NET objects, they
cant be disconnectedthat is, they always need an active connection. Thus, you cant, for example,
pass them between business objects. The purpose of the DataReader is to provide data for display,
thats it. The DataReader objects are lightweight and very fast so they are ideal for this purpose.
When creating a DataReader, start by declaring a variable as follows:
Dim oDR As OleDbDataReader
The next thing you need to do is construct your Connection and Command objects. Next, initialize the
DataReader object by calling the Command objects ExecuteReader method as follows:
oDR = oCmd.Execute()
Now that is easy! Lets bring it all together . . . The following example illustrates how to (1) construct
and open a Connection, (2) construct a Command, and (3) call the Commands ExecuteReader method
and pass the result to a DataReader, as shown in Listing
Sub Page_Load(Sender As Object, E As EventArgs)
Dim oConn As OleDbConnection
Dim oCmd As OleDbCommand
Dim oDR As OleDbDataReader
oConn = New OleDbConnection(Provider=SQLOLEDB;Data
Source=(local);Initial Catalog=Music;User ID=music;Password=music)
oConn.Open()
oCmd = New OleDbCommand()
With oCmd
.Connection = oConn
.CommandType = CommandType.Text
.CommandText = SELECT * FROM t_bands
oDR = .ExecuteReader()
End With
End Sub
Using DataReader Properties
OK, so now that you have your DataReader object, what can you do with it? Well, just like all other
objects, the DataReader object has numerous properties and methods. Well start with the properties:
Item property
The Item property returns the value for a given column in its native format. In order to reference the
value of a column, you need to pass a string representing the column name or an integer representing
the columns index. Take for example the following table called t_bands:

You could reference the band_title field in either of the following ways:

Ravi Varma Thumati (www.rthumati.wordpress.com)

Page 47 of 51

ASP.NET

oDR.Items(band_title)
oDR.Items(1)
Youll notice that we passed a one (1) to the DataReader objects Items property. To clarify, the 1 is
the column index or location of the column in the row from which we want to retrieve the data. We used
1 as the index, because the numbering of column indexes begins with 0.
FieldCount property
The FieldCount property, which is obviously read-only, returns the number fields, as an integer, in the
current record. Here is some sample syntax for getting the FieldCount:
Dim iFCount As Integer
iFCount = oDR.FieldCount
One possible application of the FieldCount property is to iterate through the columns in a DataReader
and write out the columns value as shown in Listing
Sub Page_Load(Sender As Object, E As EventArgs)
Dim oConn As OleDbConnection
Dim oCmd As OleDbCommand
Dim oDR As OleDbDataReader
Dim iFieldCount As Integer
Dim x As Integer
oConn = New OleDbConnection(Provider=SQLOLEDB;Data
Source=(local);Initial Catalog=Music;User ID=music;Password=music)
oConn.Open()
oCmd = New OleDbCommand()
oCmd.Connection = oConn
oCmd.CommandType = CommandType.Text
oCmd.CommandText = SELECT * FROM t_bands
oDR = oCmd.ExecuteReader()
iFieldCount = oDR.FieldCount
While oDR.Read()
Dim oRow As New TableRow()
For x = 0 To (iFieldCount - 1)
Dim oCell As New TableCell()
oCell.Text = oDR.Item(x)
oRow.Cells.Add(oCell)
Next
tblExample.Rows.Add(oRow)
End While
oDR.Close
oConn.Close
End Sub
In Listing19-2, all weve done is open our OleDbDataReader object, obtain the number of fields in the
DataReader using the FieldCount property, and iterate through the rows of the DataReader using the
Read method. (We discuss the Read method later in this session.) For each row, we loop through the
fields and create a table cell containing the columns value. Simple!
IsClosed property
The IsClosed method returns a Boolean value indicating whether the DataReader is closed. A value of
true means that the DataReader is closed.

Ravi Varma Thumati (www.rthumati.wordpress.com)

Page 48 of 51

ASP.NET

RecordsAffected property
The RecordsAffected property returns the number of rows that are changed, inserted, or deleted by
the Command object that opens the DataReader. 0 is returned from the RecordsAffected property if
no records were affected by the command object, and 1 is returned for SELECT commands. The
RecordsAffected property is not set until the DataReader object is closed. The isClosed and
RecordsAffected are the only DataReader properties that can be accessed after the DataReader has
been closed.
Listing illustrates how you can use the isClosed and RecordsAffected properties to display information
about the Command that was executed to create a DataReader object.
Sub Page_Load(Sender As Object, E As EventArgs)
Dim oConn As OleDbConnection
Dim oCmd As OleDbCommand
Dim oDR As OleDbDataReader
Dim oParam As OleDbParameter
Dim iBandID As Integer = 0
If Page.IsPostBack Then iBandID = lstBands.SelectedItem.Value
oConn = New OleDbConnection(Provider=SQLOLEDB;Data
Source=(local);Initial Catalog=Music;User ID=music;Password=music)
oConn.Open()
oCmd = New OleDbCommand()
With oCmd
.Connection = oConn
.CommandType = CommandType.StoredProcedure
.CommandText = prBandDelete
oParam = New OleDbParameter
With oParam
.ParameterName = BandID
.OleDbType = OleDbType.Integer
.Value = iBandID
End With
.Parameters.Add(oParam)
Try
oDR = .ExecuteReader()
lstBands.Items.Clear
lstBands.Items.Add(New ListItem(,0))
While oDR.Read()
lstBands.Items.Add(New
ListItem(oDR.Item(band_title),oDR.Item(band_id)))
End While
Catch err As Exception
Response.Write(The following error occurred:<BR> &
err.Message & <BR>)
End Try
End With
oDR.Close
oConn.Close
If oDR.isClosed Then
If oDR.RecordsAffected > 0 Then lblDeleted.Text = You deleted &
oDR.RecordsAffected & bands from the database.
End If
End Sub
Youll notice that Listing 19-3 uses a ListBox Web Control, lstBands, to allow a user to select a band
that he or she would like to delete from the t_bands table. When the Web Form containing lstBands is
submitted, the id of the band is gathered and passed to OleDbCommand, oCmd, as an
OleDbParameter, oParam. The prBandDelete stored procedure, which deletes the selected band and

Ravi Varma Thumati (www.rthumati.wordpress.com)

Page 49 of 51

ASP.NET

returns a recordset containing the remaining bands, is then executed by calling the Command objects
ExecuteReader method. Following is the code for the prBandDelete stored procedure.
CREATE PROCEDURE prBandDelete
@BandID INT = 0
AS
IF @iBandID > 0
BEGIN
DELETE FROM t_songs WHERE album_id IN (SELECT
album_id FROM t_albums WHERE band_id = @iBandID)
DELETE FROM t_albums WHERE band_id = @iBandID
DELETE FROM t_band_members WHERE band_id = @iBandID
DELETE FROM t_bands WHERE band_id = @iBandID
END
SELECT
band_id, band_title
OM
t_bands
DER BY and_title
When the ExecuteReader method is called, an OleDbDataReader, oDR, is constructed and then
iterated through the data to repopulate the lstBands ListBox. After repopulating lstBands, we inspect
to see that the DataReader has been closed, using the isClosed property. We then use the
RecordsAffected property to display the number of records that were deleted from t_bands table.

Using DataReader Methods


Now that you know the properties you are likely to use most often, lets move onto the DataReaders
methods. The DataReader objects provide a plethora of methods. Read method weve touched on the
Read method in an earlier example (Listing 19-2). The Read method advances the DataReader object
to the next record each time it is called. In the Old World of ADO you would have had to use a
combination of several properties and methods, including EOF and MoveNext, to perform the same
function as the DataReaders Read method.
Since the DataReader provides for forward-only navigation, the Read method really minimizes the
amount of code you need to write to get to your data. We do not provide a Read method example here
as you can refer to several of the previous examples to see it in action.
GetValue method
The GetValue method returns the value of a specified field in its native format. You can effectively use
the GetValue method in place of the Item property. The GetValue method accepts either an integer
representing a column index or a string representing a column name. For example, if the first column
(index of 0) of our table is called band_id, we can use the following statement to get its value:
iID = oDR.GetValue(0)
Or, we can use the following:
iID = oDR.GetValue(band_id)
Since band_id is set as an integer in our table, the value returned from the GetValue method will
return an integer, its native format.
Get[Data Type] methods
The DataReader object provides a multitude of what we call the Get[Data Type] methods, including
the GetString, GetInt32, and GetBoolean methods. The Get[Data Type] methods return the data in
a column as the specified data type. For example, the GetString method will return the data in a
column in the form of a string. However, no data type conversion is performed, so the data type of the
column must be of the data type specified.

Ravi Varma Thumati (www.rthumati.wordpress.com)

Page 50 of 51

ASP.NET

Unlike the GetValue method, the Get[Data Type] methods only accept a column index, also called an
ordinal reference, as a parameter. The following statements demonstrate the GetString method:
Dim sBandName As String
sBandName = oDR.GetString(0)
GetOrdinal method
The GetOrdinal method returns a columns ordinal reference value, or index, as an integer when
passed a column name. For example, the following code returns 0 because band_id is the first column
in the t_bands table:
Dim iOrdinal As Integer
iOrdinal = oDR.GetOrdinal(band_id)
GetName method
The GetName method is the exact opposite of the GetOrdinal method. It returns a columns name as a
string when passed its index. For example, the following code snippet will return band_id:
Dim sName As String
sName = oDR.GetName(0)
Close method
As the name implies, the Close method closes a DataReader object. Unlike other objects, closing the
DataReader object is mandatory when youre done using it. You will get an error if you dont close your
DataReader and then attempt to alter your Connection object. Closing a DataReaders Connection
object immediately closes the DataReader. The syntax is very simple:
oDR.Close()
That covers the DataReaders major properties and methods.

Ravi Varma Thumati (www.rthumati.wordpress.com)

Page 51 of 51

Você também pode gostar