ADO.NET introduction

ADO.NET introduction

Most applications require some kind of data access. Desktop applications need to integrate with central databases, Extensible Markup Language (XML) data stores, or local desktop databases. ADO.NET data access technology allows simple, powerful data access while maximizing system resource usage.


Different applications have different requirements for data access. Whether your application simply displays the contents of a table or processes and updates data to a central SQL server, ADO.NET provides the tools to implement data access easily and efficiently.


Disconnected Database Access

Previous versions of data access technologies have provided continuously connected data access by default. In such a model, an application creates a connection to a database and keeps it open for the life of the application or at least for the amount of time that data is required. As applications become more complex and databases begin to serve more and more clients, a connected data access technology becomes impractical for a variety of reasons.

For example:

Open database connections are expensive in terms of system resources. The more open connections, the less efficient system performance becomes.

Applications with connected data access are very difficult to scale up. An application that can maintain connections with two clients might do poorly with 10, and might be completely unusable with 100.

ADO.NET addresses these issues by implementing a disconnected database access model by default. In this model, data connections are established and only left open long enough to perform the requisite action. For example, if data is being requested from a database by an application, the connection is opened just long enough to load the data into the application, and then it is closed.

Likewise, if a database is being updated, the connection is opened to execute the UPDATE command, and then closed again. By keeping connections open only for the minimum required time, ADO.NET conserves system resources and allows data access to be scaled up with a minimal impact on performance.


ADO.NET Data Architecture

Data access in ADO.NET relies on two components: the DataSet, which stores data on the local machine, and the Data Provider, which mediates interaction between the program and the database.


The DataSet

The DataSet is a disconnected, in-memory representation of data. It can be thought of as a local copy of the relevant portions of the database. Data can be loaded into a DataSet from any valid data source, such as a Microsoft SQL Server database, a Microsoft Access database, or an XML file.

The DataSet is persisted in memory, and the data therein can be manipulated and updated independent of the database. When appropriate, the DataSet can then act as a template for updating the central database.

The DataSet object contains a collection of zero or more DataTable objects, each of which is an in-memory representation of a single table. The structure of a particular DataTable is defined by the DataColumns collection, which enumerates the columns in a particular table, and the Constraint Collection, which enumerates the constraints on the table.

Together, these two collections make up the schema of the table. A DataTable also contains a DataRows collection, which contains the actual data in the DataSet.

The DataSet contains a DataRelations collection. A DataRelation object allows you to create associations between rows in one table and rows in another table. The DataRelations collection enumerates a set of DataRelation objects that defines these relationships between tables in the DataSet.

For example, consider a DataSet that contains two related tables: a Customers table and an Orders table. In the Customers table, each customer is represented only once and is identified by a CustomerID field that is unique. In the Orders table, the customer who placed the order is identified by the CustomerID field, but can appear more than once if a customer has placed multiple orders. This is an example of a one-to-many relationship, and you would use a DataRelation object to define that relationship.

Additionally, a DataSet contains an ExtendedProperties collection, which is used to store custom information about the DataSet.


The Data Provider

The link to the database is created and maintained by the Data Provider. A Data Provider is not actually a single component, but a set of related components that work together to provide data in an efficient, performance-driven manner. The Microsoft .NET Framework currently ships with two data providers: the SQL Server .NET Data Provider, which is designed specifically to work with Microsoft SQL Server 7.0 or later, and the OleDb .NET Data Provider, which connects with other types of databases. Each Data Provider consists of similar versions of the following generic component classes:

The Connection object provides the connection to the database.

The Command object is used to execute a command against a data source. It can execute either non-query commands, such as INSERT, UPDATE, or DELETE, or return a DataReader with the results of a SELECT command.

The DataReader object provides a forward-only, read-only, connected recordset.

The DataAdapter object populates a disconnected DataSet or DataTable with data and performs updates.

Data access in ADO.NET is facilitated as follows: A Connection object establishes a connection between the application and the database. This connection can be accessed directly by a Command object or by a DataAdapter object.

The Command object provides direct execution of a command to the database. If the command returns more than a single value, the Command object returns a DataReader to provide the data. This data can be directly processed by application logic. Alternatively, you can use the DataAdapter to fill a DataSet object. Updates to the database can be achieved either through the Command object or through the DataAdapter.

The generic classes that make up the Data Providers are summarized in the following sections.


The Connection Object

The Connection object represents the actual connection to the database. Microsoft Visual Studio .NET provides two types of Connection classes: the SqlConnection object, which is designed specifically to connect to Microsoft SQL Server 7.0 or later, and the OleDbConnection object, which can provide connections to a wide range of database types. The Connection object contains all of the information required to open a channel to the database in the ConnectionString property. The Connection object also provides methods that facilitate data transactions.



The Command Object

The Command object is also represented by two corresponding classes: SqlCommand and OleDbCommand. You can use the Command objects to execute commands to a database across a data connection. The Command objects can be used to execute stored procedures on the database, execute SQL commands, or return complete tables directly. Command objects provide three methods that are used to execute commands on the database:

ExecuteNonQuery.

Executes commands that have no return values, such as INSERT, UPDATE, or DELETE.

ExecuteScalar.

Returns a single value from a database query.
ExecuteReader.

Returns a result set by way of a DataReader object.


The DataReader Object


The DataReader object provides a forward-only, read-only, connected stream recordset from a database. Unlike other components of the Data Provider, DataReader objects cannot be directly instantiated. Rather, the DataReader is returned as the result of a Command object's ExecuteReader method.

The SqlCommand.ExecuteReader method returns a SqlDataReader object, and the OleDbCommand.ExecuteReader method returns an OleDbDataReader object. The DataReader can provide rows of data directly to application logic when you do not need to keep the data cached in memory. Because only one row is in memory at a time, the DataReader provides the lowest overhead in terms of system performance, but requires the exclusive use of an open Connection object for the lifetime of the DataReader.


The DataAdapter Object

The DataAdapter is the class at the core of ADO.NET's disconnected data access. It is essentially the middleman, facilitating all communication between the database and a DataSet. The DataAdapter fills a DataTable or DataSet with data from the database when the Fill method is called. After the memory-resident data has been manipulated, the DataAdapter can transmit changes to the database by calling the Update method. The DataAdapter provides four properties that represent database commands:

SelectCommand.

Contains the command text or object that selects the data from the database. This command is executed when the Fill method is called, and fills a DataTable or a DataSet.

InsertCommand.

Contains the command text or object that inserts a row into a table.

DeleteCommand.

Contains the command text or object that deletes a row from a table.

UpdateCommand.

Contains the command text or object that updates the values of a database.
When the Update method is called, changes in the DataSet are copied back to the database, and the appropriate InsertCommand, DeleteCommand, or UpdateCommand is executed.

Filtering Data

Binding, Viewing, and Filtering Data

Viewing data is a vital part of many applications. Data binding allows you to associate records in a data source with controls on a form, allowing them to be browsed and updated. In this lesson, you will learn how to use ADO.NET to bind data to controls in your application and how to manage currency of records in your application.

Data binding refers to a relationship between a data provider and a data consumer. A data provider is a source for data and is linked to a data consumer, which receives data and processes or displays it.

A traditional example of data binding is the relationship between a data-bound control such as a TextBox and a data source. The control displays the value of the column in the data source to which it is bound at the current row. As the current row changes, the value displayed by the control changes.

Data Providers

In the .NET Framework, any object that implements the IList interface can be a data provider. This not only includes ADO.NET objects, such as DataSets, DataTables, and DataColumns, but also more mundane objects such as arrays or collections. DataViews, which are discussed later in this lesson, are a customizable view of data that can also act as a data provider.

Data providers also manage currency of data. In previous data access technologies, a cursor was used to manage data currency. As the cursor moved, the current record changed and bound controls were updated. Because data access in ADO.NET is fundamentally disconnected, there is no concept of a database cursor. Rather, each data source has an associated CurrencyManager object that keeps track of the current record. CurrencyManager objects are managed through a form's BindingContext object. Data currency is discussed in detail later in this lesson.


Data Consumers


The data consumer represents the control with bound properties. In the .NET Framework, you can bind any run-time accessible property of any control to a data source. For example, you could bind the Size property of a control to a database record, or the location, or any other run-time accessible property of the control.

Data binding has a wide variety of uses. A typical scenario that involves data binding is a data entry form. Several controls, such as TextBox, CheckBox, ListBox, and so on are bound to relevant columns of a DataSet. New records are manually entered by typing values for each column into the appropriate controls displayed on the form. When a record is complete, it is added to the DataSet, which can then be used to update the database.

There are two types of data binding: simple binding and complex binding. A simple-bound control binds one record at a time to a control. For example, a Label control can be simply bound to a column in a DataTable. In such a case, it would display the member of that column for the current record.

Complex binding, on the other hand, allows multiple records to be bound to a single control. Controls such as ListBox or ComboBox can be complex bound to a single column in a DataTable or DataView. At run time, these controls would display all members of that column rather than only the current row. Complex-bound controls are usually involved in displaying choices and allowing specific rows of data to be chosen. Controls such as the DataGrid are capable of even more complex binding and can be bound to all column and rows of a particular DataTable or DataView, or even to a DataSet.

Creating a Simple-Bound Control


You can create a simple-bound control through the DataBindings property. The DataBindings property is an instance of the ControlBindingsCollection class that keeps track of and organizes which controls are bound to what data sources. At design time, the DataBindings property is displayed as a node in the Properties window. This node expands to list the properties that are most commonly data bound.


Data Binding at Run Time


You might want to change the data source that a control is bound to at run time. Or, you might not know at design time what data source a particular control might be bound to. In a third scenario, you might want to bind your control to an array or collection that is not instantiated until run time, in which case, you must set the binding in code.

Data binding for a control is managed through the control's DataBindings property, which is an instance of a ControlBindingsCollection object. At run time, you can add, remove, or clear data binding information by setting the appropriate member of the DataBindings collection.

You can bind a property to a data source by using the DataBindings.Add method. This creates a new binding association and adds it to the DataBindings collection. The Add method takes three parameters: the property name you want to bind, as a String; the data source you want to bind to, as an object; and the data member of the data source you want to bind the property to.


The CurrencyManager

The CurrencyManager object keeps track of the current record for a particular data source. There can be multiple data sources in an application at one time, and each data source maintains its own CurrencyManager. Because there can be multiple data sources represented on a single form at any given time, each form manages the CurrencyManager objects associated with those data sources through a central object called the BindingContext.

The BindingContext organizes and exposes the CurrencyManager objects associated with each data source. Thus, you can use the BindingContext property of each form to manage the position of the current record for each data source. You access a particular currency manager by supplying the BindingContext property with the data source object whose CurrencyManager you want to retrieve.

To navigate bound data in a Windows Form
Set the Position property for the appropriate BindingContext member.

Because the .NET Framework will not allow you to set the Position property to a value less than zero or greater than the upper bound of the collection, there is no possibility of an error occurring if you attempt to move before or after the end of the records.

You might, however, want to incorporate program logic to provide visual cues to users to let them know when the end or beginning of a group of records is reached. The following example demonstrates how to use the PositionChanged event of the CurrencyManager to disable back and forward buttons when the end of the record list is reached.