Connecting To Your DataWithout the ability to store data and retrieve data not held locally in memory our programs would be fairly useless. Searching, updating, and processing data is one of the fundamental tasks of software. The contemporary approach is to use a Relational Database Management System such as:
In order to store and retrieve data that your program will need. This is not to say that using normal "flat" databases (read: text files) is totally outmoded and altogether appropriate, but using an RDMBS is standard and advised when a project of even moderate size is undertaken. If you use an RDMBS, you have the highest likelihood of your program being portable and scalable. What a developer needs is a mechanism that allows for programmatic access to data held within an RDBMS. In the .NET Framework, we use an updated DNA solution (ADO) called ADO.NET, which stands for ActiveX Data Objects. Like everything else in the .NET Framework class library, ADO.NET is nothing more than a group of types, all of which reside in the System.Data namespace. These types are used by client applications that need to work with stored data, and they allow access to that data in various useful ways. The most common need is to work with relational data, in Oracle or SQL Server, and is largely what ADO.NET focuses on. ADO.NET's also adds support for working with XML documents. Data ProvidersApplications utilizing ADO.NET rely on .NET Data providers which enable access to a particular data source. Programs you write, which implement ADO.NET, act at a the client. The client, written as managed code running on the CLR, can use various .NET data providers to access stored data. Each .NET data provider is also written as managed code, but a data provider might also rely on software that doesn't use the .NET Framework to accomplish its task.
The .NET Framework class library includes two .NET data providers:
There is also a third option, called the ODBC provider. The Open Database Connectivity (ODBC) API is another of the many data access interfaces Microsoft has provided for Windows developers, and applications that use it rely on ODBC drivers to get at stored data. ODBC is even older than ADO, so many, many ODBC drivers exist. The ODBC .NET data provider provides a wrapper around an ODBC driver, allowing managed code to use it for accessing data. Also, it's worth pointing out that .NET Framework applications can still use the traditional ADO interface via COM interoperability support. ADO.NET Types and FunctionalityEach .NET data provider is implemented as a group of types (from the FCL). The primary types that implement the SQL provider are located in the System.Data.SqlClient namespace, while those that implement the OLE DB provider are in System.Data.OleDb. Regardless of which provider you choose, each one offers an analogous set of classes. The fundamental objects supported by any .NET data provider are as follows:
Each of these classes are actually generic rather than actual class
names defined in the .NET Framework class library (DataSet being
an exception). The DataReader for the SQL .NET data provider, for
example, is implemented by the SqlDataReader class, while that
for the OLE DB .NET data provider is implemented by the
OleDbDataReader class. Accessing Data DirectlyMake The ConnectionAll actions within the ADO.NET data access framework share some common steps regardless of what approach is used to access data. An ADO.NET client application first relies on a connection to the DBMS. The application can explicitly open a connection by calling the Open method of a Connection object. To open a connection to SQL Server, for example, the client invokes this method on an instance of the SqlConnection class. To open a connection to some other database using the OLE DB .NET data provider, the client invokes the Open method on an instance of the OleDbConnection class. In either case, the client must first set the class's ConnectionString property, indicating which database it's interested in and other information. Make The CommandOnce a connection exists, a client can issue queries and other commands on it using a Command object. An application can create a Command object by invoking a Connection object's CreateCommand method. Once again, different data providers use different Command object classes: The SQL provider uses SqlCommand, while the OLE DB provider uses OleDbCommand. Whichever is used, both allow specifying a SQL query for a Command object by setting the object's CommandText property. Execute The CommandOnce a Command object exists, a client can choose one of several methods this object provides to execute the command it contains. Those methods are as follows:
Free ResourcesWhen your program is finished, your ADO.NET client must invoke either
the Direct Access Using DataReaderDirect access to data relies on DataReader objects. DataReaders are fast, and they don't use much memory, because only one row of data at a time is made accessible (although more may be cached). An application using a DataReader can read a query's results only one row at a time, and can move forward only through those results—no random access is allowed. To read a row, the application calls a DataReader's
The example above gives a feel for how this whole process works. Retrieving Data in SetsA DataSet instance is an in-memory cache for data. Most commonly, that data is the set of rows resulting from one or more SQL queries. Unlike Connection, Command, and DataReader, each of which has a unique class for each .NET data provider, there is only one DataSet class used by all .NET data providers. DataSets are very general things, so they're useful in many different situations. For instance, if a client application wishes to examine data in an arbitrary way, such as scrolling back and forth through the rows resulting from a SQL query, a a DataSet is the best tool for the job. DataSets are also useful for combining data from different data sources, such as two separate queries, or different types of data, such as the result of a query on a relational DBMS and the contents of an XML document. DataSets are also useful if data needs to be sent across a network, because DataSets are serializable. A DataSet can also be passed around and used independently—it's just a managed object with state and methods like any other—but it cannot itself maintain an open connection to any underlying database.
Each DataSet can contain zero or more DataTable objects, which are instances of a class defined in System.Data. Each DataTable can contain the result of some query or perhaps something else. A DataSet can also maintain relationships among DataTables using DataRelation objects. For example, a column in one table might contain a foreign key for another table, a relationship that could be modeled by a DataRelation. These relationships can also be used to navigate through the contents of a DataSet. A simple DataSet, one that contains the result of just a single SQL query, might have only one DataTable and no DataRelations. DataSets can be quite complex, however, so an application that needed to maintain the results of several queries in memory could stuff them all into one DataSet, each in its own DataTable. Data Access with DataSetsDataSet objects can be created using a .NET data provider's
DataAdapter object. The SQL .NET data provider uses the class
SqlDataAdapter, while the OLE DB .NET data provider uses
OleDbDataAdapter. Both classes provide a similar set of properties
and methods for creating and working with DataSets. Some
important properties of a DataAdapter are:
This image generally describes the steps needed to populate a dataset. This can also be seen in the following code snippet:
The DataSet created the preceding fictitious example contains just one DataTable. It is also possible to populate a DataSet from other databases and data sources. To add other DataTables with different contents to this same DataSet, the Fill method could be called again once the SelectCommand property of the DataAdapter had been changed to contain a different query. Alternatively, another DataAdapter could be used on a different data source.
Updating Via DataSetIt's also possible to update/modify information in a DBMS using a DataSet. Once one or more DataTables have been created in the DataSet, the information in those tables can be changed by the client. DataAdapter's Update method will cause the DataAdapter to examine the changes in any DataSet or DataTable passed into this call and then modify the underlying database to reflect those changes. These modifications are made using the commands stored in the DataAdapter's InsertCommand, UpdateCommand, and DeleteCommand properties described earlier. Because these commands can be stored procedures as well as dynamically executed statements, these update operations can be reasonably efficient.
What if the database changes while you are working?
There is no way for the DataSet itself to maintain a lock on the underlying data from which its contents were derived. As a result, it's possible that the data in the database has changed since some of its information was copied into the DataSet. Whether updates succeed if the underlying data has changed depends on what commands are placed into the InsertCommand, UpdateCommand, and DeleteCommand properties of the DataAdapter. If Inserts, Updates and Deletes are done automatically using a CommandBuilder object, then automatically generated update commands will fail if any of the affected data has changed since it was originally read. Modifying the DataSetThe data within a DataSet is grouped into one or more DataTables such that your program can access and modify this data. Applications need to work with data in diverse ways, so DataTables provide several options for accessing and modifying the data they contain. Two classes are commonly used to work with information in a DataTable:
Sub SelectEach DataTable provides a Add New RecordsTo add data to a DataTable, an application can create a new
DataRow object, populate it, and insert it into the table. To create
the new DataRow, the application can call a DataTable's Tracking ChangesThe state of a DataRow changes as that DataRow is
modified. The current state is kept in a property of the DataRow
called RowState, and several possibilities are defined. Changes
made to a DataRow can be made permanent by calling the |