Data Access with ADO.NET by Andrew Troelsen
Total Page:16
File Type:pdf, Size:1020Kb
APPENDIX Data Access with ADO.NET by Andrew Troelsen This is Chapter 13 of C#and the .NET Platform (Apress, 2001). From this point on, all references to other chapters and appendixes are to those from Troelsen and not to chapters and appendixes within Distributed .NET Programming in C#. Unless you are a video game developer by trade, you are probably interested in data base manipulation. As you would expect, the .NET platform defines a number of types (in a handful of related namespaces) that allow you to interact with local and remote data stores. Collectively speaking, these namespaces are known as ADO.NET, which as you will see is a major overhaul of the classic ADO object model. This chapter begins by examining some core types defined in the System.Data namespace-specifically DataColumn, DataRow, and DataTable. These classes allow you to define and manipulate a local in-memory table of data. Next, you spend a good deal of time learning about the centerpiece of ADO.NET, the DataSet. As you will see, the DataSet is an in-memory representa tion of a collection of interrelated tables. During this discussion, you will learn how to programmatically model table relationships, create custom views from a given table, and submit queries against your in-memory DataSet. After discussing how to manipulate a DataSet in memory, the remainder of this chapter illustrates how to obtain a populated DataSet from a Database Man agement System (DBMS) such as MS SQL Server, Oracle, or MS Access. This entails an examination of .NET "managed providers" and the OleDbDataAdapter and SqlDataAdapter types. The Need for ADO.NET The very first thing you must understand when learning ADO.NET is that it is not simply the latest and greatest version of classic ADO. While it is true that there is some symmetry between the two systems (e.g., each has the concept of"connec tion'' and "command" objects), some familiar types (e.g., the Recordset) no longer exist. Furthermore, there are a number of new ADO.NET types that have no direct equivalent under classic ADO (e.g., the DataSet). In a nutshell, ADO. NET is a new database access technology specifically geared at facilitating the development of disconnected systems using the .NET platform. N-tier applications (especially Web-based applications) are fast becom ing the norm, rather than the exception, for most new development efforts. 395 Appendix Unlike classic ADO, which was primarily designed for tightly coupled client/ server systems, ADO.NET greatly extends the notion of the primitive ADO disconnected recordset with a new creature named the DataSet. This type repre sents a local copy of any number of related tables. Using the DataSet, the client is able to manipulate and update its contents while disconnected from the data source and submit the modified data back for processing using a related "data adapter." Another major difference between classic ADO and ADO.NET is that ADO.NET has full support for XML data representation. In fact, the data obtained from a data store is internally represented, and transmitted, as XML. Given that XML is transported between layers using standard HTTP, ADO.NET is not limited by firewall constraints. As you might be aware, classic ADO makes use of the COM marshaling pro tocol to move data between tiers. While this was appropriate in some situations, COM marshaling poses a number oflimitations. Specifically, most firewalls are configured to reject COM RPC packets, which makes moving data between machines tricky. Perhaps the most fundamental difference between classic ADO and ADO. NET is that ADO.NET is a managed library of code and therefore plays by all the same rules as any managed library. The types that comprise ADO.NET use the CLR memory management protocol, adhere to the same programming model, and work with many languages. Therefore, the types (and their members) are accessed in the same exact manner, regardless of which .NET-aware language you use. ADO.NET: The Big Picture The types that compose ADO.NET work together for a common goal: populate a DataSet, disconnect from the data store, and return the DataSet to the caller. A DataSet is a very interesting data type, given that it represents a local collection of tables (as well as the relationships between these tables) used by the client appli cation. In some respects, this may remind you of the classic ADO disconnected recordset. The key difference is that a disconnected recordset represents a single table of data, whereas ADO.NET DataSets can model a collection of related tables. In fact, it is completely possible to have a client-side DataSet that repre sents the entire remote database. Once you have obtained a DataSet, you can perform queries against the local tables to obtain specific subsets of information as well as navigate between related tables programmatically. As you would expect, you can add new rows to a given table in the DataSet as well as remove, filter, or update existing records. Once the modifications have been made, the client then submits the modified DataSet back to the data store for processing. 396 Data Access with ADO.NET An obvious question at this point is "How do I get the DataSet?" Under the ADO.NET model, DataSets are populated through the use of a managed provider, which is a collection of classes that implement a set of core interfaces defined in the System.Data namespace ; specifically IDbCommand, IDbDataAdapter, IDbConnection, and IDataReader (see Figure 13-l). Client I The in-memory I I (disconnected) DataSet Data is transmitted between layers as XML EJ lrDbDataAdapterl IDbC011111and IDataReader I ~.. ,,,,, ,,.,,,,, plements these interfaces provide access to a IDbConnection specific type of data store r::::: ::::; Some Data Store ...... _... Figure 13-1 . Clients interacting with managed providers ADO.NET ships with two managed providers out of the box. First is the SQL provider, which provides highly optimized interactions with data stored in MS SQL Server (7.0 or higher). If the data you desire is not in an SQL Server data file, you can use the OleDb provider, which allows access to any data store that sup ports the OLE DB protocol. Be aware, however, that the OleDb provider uses native OLE DB (and therefore requires COM Interop) to enable data access. As you might suspect, this is always a slower process than talking to a data store in its native tongue. Other vendors will soon begin shipping custom managed providers for their proprietary data stores. Until then, the OleDb provider does the trick. Understanding ADO.NET Namespaces Like other aspects of the .NET universe, ADO. NET is defined in a handful of related namespaces. Table 13-1 gives a quick rundown of each. 397 Appendix Table 13-1. ADO.NET Namespaces ADO.NET NAMESPACE MEANING IN LIFE System. Data This is the core names pace of ADO.NET. It defines types that represent tables, rows, columns, constraints, and DataSets. This namespace does not define types to connect to a data source. Rather, it defines the types that represent the data itself. System.Data.Common This namespace contains the types shared between managed providers. Many of these types function as base classes to the concrete types defined by the OleDb and SqlClient managed providers. System.Data.OleDb This namespace defines the types that allow you to connect to an OLE DB-compliant data source, submit SQL queries, and fill DataSets. The types in this namespace have a look and feel similar (but not identical) to that of classic ADO. System.Data.SqlClient This namespace defmes the types that constitute the SOL managed provider. Using these types, you can talk directly to Microsoft SQL Server and avoid the level of indirection associated with the OleDb equivalents. System.Data.Sql'Iypes These types represent native data types used in Microsoft SQL Server. Although you are always free to use the corresponding CLR data types, the SqlTypes are optimized to work with SQL Server. All of these ADO.NET namespaces are in a single assembly named System.Data.dll (Figure 13-2). Thus, like in any project referencing external assemblies, you must be sure to set a reference to this .NET binary. Of all the ADO.NET namespaces, System.Data is the lowest common denom inator. You simply cannot build ADO.NET applications without specifying this namespace in your data access applications. In addition, to establish a connec tion with a data store, you also need to specify a using directive for the System.Data.OleDb or System.Data.SqlClient namespaces. The exact reasons for this are discussed soon. For now, get to know some of the core types defined in System.Data. The Types of System.Data As mentioned, this namespace contains types that represent the data you obtain from a data store, but not the types that make the literal connection. In addition to a number of database-centric exceptions (NoNullAllowedException, 398 Data Access with ADO. NET f D: · WINNT · Microsoft.NET\ framework\ v 1.0.2615 \S~ • Fie View Help ~.... System.Data ... System.Data.CodeOen i! EkI -··• ! S···· • System.Data.Common i ! i Iii ···• System.Data.ObjectPool I ' 1 Bl··-· System.Data.OieDb ~ : l Iii····• System.Data.SqiCiient I : 1 Iii·-·• System.Data.SqiTypes I ; l Iii AcceptRejectRule i f ! Iii- Aggregate i : l , .... AggregateNode i 1!1·- AggregateType j ~ .... BinaryNode .assembly System.Data { Figure 13-2. The System.Data.dll assembly RowNotlnTableException, MissingPrimaryKeyException, and the like), these types are little more than 00 representations of common database primitives (tables, rows, columns, constraints, and so on). Table 13-2lists some of the core types, grouped by related functionality.