2. Contents
• What is ADO.Net?
• What happened to ADO?
• The ADO.Net object structure
• Connecting
• Commanding
• Readers and DataSets
3. What is ADO.Net?
• The data access classes for the
.Net framework
• Designed for highly efficient data
access
• Support for XML and disconnected
record sets
4. And the .Net framework?
• A standard cross language
interface
• Encapsulation of services, classes
and data types
• Uses XML for data representation
5. Where does ADO sit?
VB C# C++ Jscript …
Common Language Specification
Visual Studio .NET
ASP.Net Windows Forms
ADO.Net XML.Net
Base Class Library
Common Language Runtime (CLR)
Windows COM+ Services
6. What happened to ADO?
• ADO still exists.
• ADO is tightly coupled to client
server architectures
• Needs COM marshalling to pass
data between tiers
• Connections and locks are typically
persisted
7. ADO / ADO.Net Comparisons
Feature ADO ADO.Net
In memory Recordset object Dataset object
data storage Mimics single table Contains DataTables
Data Reads Sequential Sequential or non-
sequential
Data OLE/DB via the Managed provider
Sources Connection object calls the SQL APIs
8. ADO / ADO.Net Comparisons
Feature ADO ADO.Net
Disconnected Limited support, Strong support, with
data suitable for R/O updating
Passing COM marshalling DataSet support for
datasets XML passing
Scalability Limited Disconnected access
provides scalability
9. .NET Data Providers
SQL .NET
Data Provider
SQL SERVER
OLE DB .NET
Client Data Provider
OLE DB
Other DB
Provider
ODBC .NET ODBC
Data Provider Driver Other DB
10. Data Provider Functionality
Client .Net Data Provider
Connection Command
Rows
DataReader
DataSet
DataAdapter database
11. ADO.Net object model
Fill
DataAdapter DataSet
Update
UpdateCommand
DeleteCommand
SelectCommand
InsertCommand
Errors Collection
Command
Connection Parameters
Data Source
13. Using Namespaces
• VB.Net
Imports System.Data
Imports System.Data.SqlClient
Dim sqlAdp as SqlDataAdapter
• C#
using System.Data;
using System.Data.SqlClient;
SqlDataAdapter sqlAdp= new
SqlDataAdapter();
15. Connecting to SQL
• using System.Data.SqlClient;
string sConnectionString =
"Initial Catalog=Northwind;
Data Source=localhost;
Integrated Security=SSPI;";
SqlDataAdapter sqlAdp= new
SqlDataAdapter(sConnectionString);
sqlAdp.Close();
sqlAdp.Dispose();
16. Connection Pooling
• ADO.Net pools connections.
When you close a connection it is released back into a
pool.
• SqlConnection conn = new SqlConnection();
conn.ConnectionString =
"Integrated Security=SSPI;Initial Catalog=northwind";
conn.Open(); // Pool A is created.
• SqlConnection conn = new SqlConnection();
conn.ConnectionString =
"Integrated Security=SSPI;Initial Catalog=pubs";
conn.Open();
// Pool B is created because the connection strings differ.
• SqlConnection conn = new SqlConnection();
conn.ConnectionString =
"Integrated Security=SSPI;Initial Catalog=northwind";
conn.Open(); // The connection string matches pool A.
18. Using the command object
• SqlCommand
Multiple constructors
• New()
• New(cmdText)
• New(cmdText, connection)
• New(cmdText, connection,
transaction)
19. Using the command object
• string sSelectQuery =
"SELECT * FROM Categories ORDER BY CategoryID";
string sConnectionString =
"Initial Catalog=Northwind;
Data Source=localhost;
Integrated Security=SSPI;";
SqlConnection objConnect = new SqlConnection(sConnectString);
SqlCommand objCommand = new SqlCommand(sSelectQuery,
objConnect);
/*
• objCommand.CommandTimeout = 15;
objCommand.CommandType = CommandType.Text;
• */
objConnect.Open();
SqlDataReader drResults;
drResults = objCommand.ExecuteReader()
drResults.Close();
objConnect.Dispose();
20. Command Methods
• .ExecuteReader() - Returns DataReader
• .ExecuteNonQuery() - Returns # of
Rows Affected
• .ExecuteXMLReader() - Returns
XMLReader Object to Read XML
documentation
• .ExecuteScaler() - Returns a Single
Value e.g. SQL SUM function.
21. The DataReader object
• DataReader objects are highly
optimised for fast, forward only
enumeration of data from a data
command
• A DataReader is not disconnected
22. The DataReader object
• Access to data is on a per record
basis.
• Forward only
• Read only
• Does support multiple recordsets
23. Creating a data reader
SqlDataReader sqlReader;
sqlReader =
sqlCommand.ExecuteReader();
while (sqlReader.Read())
{
// process, sqlReader("field")
}
sqlReader.Dispose();
24. Other Methods
• GetString(), GetInt() etc.
• GetSqlString(), GetSqlInt32() etc.
• GetValues()
• IsDBNull()
• GetSchemaTable()
25. DataSets
• In-memory representation of data
contained in a database/XML
• Operations are performed on the
DataSet, not the data source
• Can be created programmatically,
using a DataAdapter or XML
schema and document (or any
mixture)
26. Creating DataSets
• Setup SqlConnection
• Setup a SqlDataAdapter
• Create a DataSet
• Call the .Fill() method on the DA
27. DataAdapters
• Pipeline between DataSets and
data sources
• Geared towards functionality
rather than speed
• Disconnected by design
• Supports select, insert, delete,
update commands and methods
29. Using the DataAdapter
SQLDataAdapter sqlDA =
new SqlDataAdapter();
sqlDA.SelectCommand =
new SqlCommand ("select * from
authors“, sqlConnection);
DataSet sqlDS = new
DataSet("authorsTable");
sqlDA.Fill(sqlDS, "authorsTable");
30. DataAdapters
• For speed and efficiency you
should set your own
InsertCommand, UpdateCommand
and DeleteCommand
• Call GetChanges to seperates the
updates, adds and deletes since
the last sync. Then sync each
type.
31. DataTables
• A DataSet contains one or more
DataTables.
• Fields are held within the
DataTable.
• And in DataRows, DataColumns.
32. Sets, Tables and Rows
DataSet
DataTable
DataTable
DataRow
DataRow
33. Using DataTables
With a DataTable we can
• Insert, modify and update
• Search
• Apply views
• Compare
• Clear
• Clone and Copy
34. DataRelations
• New to ADO.Net
• Tables within a DataSet can now
have relationships, with integrity.
• Supports cascading updates and
deletes.
35. DataViews
• Like a SQL view
• Single, or multiple tables
• Normally used with GUI
applications via Data Binding.
36. References
• ADO.Net Programmer’s Reference
Bilbija, Dickenson et al.
Wrox Press
• http://oberon.idunno.org/sql/
• My email :
desai8@uwindsor.ca
You can use ADO in .Net applications, but you lose the speed, XML and transactional benefits of ADO.Net ADO.Net also provides access to SQL, Oracle and Jet OLE/DB providers. You can use the SQL OLE/DB provider for SQL 6.5, but the native .Net provider for SQL offers Tabular Data Stream access to SQL 7 and SQL 2000 instead of passing through OLE/DB. This offers a massive speed increase, and support for the native SQL data types.
Data Storage: ADO recordsets gather one or more tables into a single recordset object, which then mimics a single table. ADO.Net datasets contain one or more discreet DataTable objects, keeping individual tables separate Data reads: ADO scans dataRows sequentially into a recordset ADO.Net follows the paths set by the data relationships, which can be non-sequential Data Sources: ADO uses the connection object which communicates via OLE/DB ADO.Net can use an optimised managed provider for SQL (and Oracle) which talks at the database API level
Disconnected Data ADO provides a limited botch job, which is really only effective for read only datasets ADO.Net provides strong support using the DataAdapter object, which manages communication between the source and the dataset. This also provides synchronisation between the DataSet and the source. Passing datasets ADO users COM to pass data sets, which limits the data types you can pass around, and doesn’t work well through firewalls. It was also prone to memory leaks in certain versions of MDAC. ADO.Net allows the DataSet object to write to and load from an XML file, which contains the schema and data. This is obviously easier to pass around. Scalability ADO’s connects and locks were typically persistent and kept open for the length of a data operation. As user numbers went up the response time and memory overheads also increased. ADO.Nets support for disconnected data access allows you to remove a connection as quickly as possible, manipulate data “off line” then synchronise back as necessary, providing less memory overhead on the server, and cutting down connection times. This is the ideal environment for limited database resources.
ADO.Net provides Managed Data Access . This means ADO.Net classes conform to the .Net framework standards, they are type safe and utilise the primitive types and base objects provided by the framework. Memory management and garbage collection are handled by the CLR which controls the way managed objects behave and run. The role of a managed data provide is analogous to that of the OLE DB provider in ADO. They take care of Open a connection to the data source (using the connection object) Get a stream of data from the data source (read only data provided by the DataReader object, updatable provided by the DataAdapter object) Synchronise changes via the DataAdapter Raise errors The DataSet object is held in memory, server side and is disconnected, but provides the same level of information as the original data source, i.e. the data and the schema. It can also serialise (write to disk) using XML data documents.
Access to the ADO.Net objects is via Namespaces, the .Net equivalent of type libraries. System.Data provides the base classes for ADO.Net. System.Data.Common builds on this to give the managed data providers. System.Data.SqlClient provides the optimised SQL provider for SQL 7 and greater, System.Data.OleDB provides the OLE/DB provider support, for Oracle, SQL 6.5 and Jet. System.Data.SqlTypes provide classes that present SQL’s data type, which provides a type safe way to access these types. System.XML and System.XML.Schema provide XML and XSD (XML Schema Definition Language) processing. Through out the presentation I will be using the SQL specific namespaces and objects.
Note that child namespaces are not automatically included, you must include the SqlClient before you can use the SqlDataAdapter objects.
SqlConnection : Represents an open connect to a SQL server SqlCommand : Represents a TSQL statement or a stored procedure to execute against a SqlConnection SqlDataReader : Provides a fast, forward only, read only method of retrieving rows SqlDataAdapter: Represents a set of data commands and a connection, which are used to fill a DataSet and update the server SqlParameter: Represents a parameter for a SqlCommand SqlParameterCollection: Represents a collection of SqlParameter objects, and their mapping to DataSet columns SqlError: Collects a single error or warning from SQL server SqlErrorCollection : Holds multiple SqlError objects, thrown by the SqlDataAdapter SqlException : The exception thrown (for structured error handling) when the server returns an error, warning or exception SqlTransaction : Represents a T-SQL transaction to be made to a SQL database SqlDbType: an enumeration to represent each type of data available on a SQL server
A SQL connection represents a unique session to a SQL server. If the object goes out of scope, it is not closed. You must explicitly close and dispose of the connection object. As with all ADO.Net objects closing and disposing as soon as possible is recommended. (The dispose method calls the close method, but the close() call is here for clarity). The connection string format should be familiar to anyone that has used ADO, the syntax is identical. The code fragment here shoes the SqLAdapter object. There is also a SqlConnection object
The ADO.Net provider pools connections. Once created, connection pools are not destroyed until the active process ends. A connection pool is created for each unique connection string. When a pool is created, multiple connection objects are created and added so that the minimum pool size requirement is reached. Connections are added to the pool as needed, up to the maximum pool size. When a SqlConnection object is requested, it is obtained from the pool if a usable connection is available. To be usable, the connection must currently be unused, have a matching transaction context or not be associated with any transaction context, and have a valid link to the server. If the maximum pool size has been reached and no usable connection is available, the request is queued. The object pooler satisfies these requests by reallocating connections as they are released back into the pool. If the time-out period (determined by the Connect Timeout connection string property) elapses before a connection object can be obtained, an error occurs. You must always close the Connection when you are finished using it. This can be done using either the Close or Dispose methods of the Connection object. Connections that are not explicitly closed are not added or returned to the pool.
All commands issued against a connection are done through a SqlCommand object. The command object can be used to return DateReader objects (optimised, forward only objects), NonQuery (simply returning the number of affected rows the command actioned on), Scaler (the first column, of the first row of the first result set – suitable for commands that return sums and other aggregate functions), XML readers or as the basis for a SQLDataAdapter, which in turn populates a DataSet.
For people not used to an OO environment (the VBScript and VB6 programmers out there) the idea of multiple constructors, or multiple ways to call an object’s method is confusing. This facility is called overloading . You don’t need to worry about how it’s done, just be aware it’s there to make use of. As there are multiple constructors, there are multiple ways to create and use a SQL command. No one is the correct one, choose the one that is useful for your application.
The sample above shows a basic method of constructing a command object. Note that you can open the connection after you have set up the command object. Once the command and the connection objects are open you can call the data “renderer” you require, in this case a simple DataReader. The command object has various properties which can be used to optimise the command before it’s executed. Don’t forget to close and dispose of everything as soon as possible.
The execute methods, as discussed before return a suitable object, according to their name The CreateParameter method allows the creation of parameter objects which allow passing of “checked” parameters into a stored procedure The Prepare method allows caching and compilation of SQL statements on the server, giving faster execution if the command is called more than once. This is provided by calling the sp_prepexec on SQL.
Now that we have our command setup, with it’s parameters, we need to actually get the data back. As I said before, DataReader objects are highly optimised for fast, forward only enumeration of data from a data command. However, unlike the other ADO objects, it is not disconnected. Therefore it should be disposed of as soon as it’s use is finished.
Unlike ADO’s recordset object, the DataReader object only holds one record in memory at a time As the access is forward only, this means we cannot perform anything complicated, like sorts on the DataReader, or even skipping to a particular record. We cannot even tell how many records there are in a DataReader. So, if you want, for example, a count, use a multiple select
The first read call starts the connection and reads a record.
The data reader provides other “quick” methods. There are two ways to get values back, as the .Net framework managed types, and methods to record strongly typed SQL data types. The SQL data types are generally quicker for manipulation as less conversion happens. GetValues() takes an object array as its parameter. This can give speed increases for multiple column data, as the data is pushed into a local array in one call, rather than querying the object on a field by field basis. IsDBNull() is the same as doing a comparison to the null (c#) / Nothing (VB) value. GetSchemaTable() is for those of us writing generic readers. It returns a DataTable object that contains the schema information for the recordset returned.
What do I mean by in-memory representation? If we query data from a table, the dataset will be a portion of memory formatted exactly like that table, with columns having the same name, data type and so on, as well as containing the resulting records. When you change values in a dataset, it is the in memory representation that is changed, not the database tables. However there are methods to synchronise your changes to the database You can create DataSets programmatically, created tables, columns, setting widths and so on by hand, by using a DataAdapter to read from a database, or by loading XML into it. You can mix these around, so, for example, load an XML document representing an order, then, in the same dataset, retrieve data from a database matching the items in the order. I am going to concentrate on populating DataSets from DataAdapters, as this is the method used when querying SQL.
Avoid autogenerating the commands. Your own SQL will be faster. Fields are passed in order and you can use primary keys, or a where field1=parameter1 and …. For all fields to recognise your data rows.
This code illustrates the creation of a DataAdapter, and the filling of a DataSet from that data adapter. We create a DataAdpater (there is only on constructor) Then we set the SelectCommand. This can be a string, or an already generated SQLCommand object (as discussed earlier), and in this example, we specify a connection to use. If the connect hasn’t been opened yet, calling Fill on the DataAdapter will open the connection, read the data and close it, if the connection is already open, the DataAdapter will use the connection and leave it open. We then set the MissingSchemaAction field to AddWithKey. This is the normal action you want to happen. If a table has a primary key, the data adapter will use this key to populate the table. Thus, if fill is called twice, it will no duplicate records. We then create a a data set, which contains a table called authorsTable, and we populate it by calling the DataAdapter’s fill method, specifying the dataset and the table name within that data set. If I call fill again, just specifying the DataSet, a new table will be created, as we haven’t given a table name. There are numerous varieties on the Fill method, you should get to know them all, and use whatever one you feel is suitable.
So, we’ve populated a DataSet, using a DataAdapter, but what’s in there. DataTables. DataTables are the ADO.net equivalent of a generic database table. It contains columns, rows, constraints and even relationships between tables within the same DataSet. Date Tables then contain DataRows
So, we’ve populated a DataSet, using a DataAdapter, but what’s in there. DataTables, and in each DataTable, DataRows. Note that we can set constraints between the various tables in a DataSet using the DataSet methods,
Insert, modify and update all act on the dataColumns, within the datarows within the table. Search can be case sensitive, or not, depending on the CaseSensitive property of the table. It uses the select method where you can specify a SQL style where commmand (“au_name like ‘S%’”) for example. We can create our own views on tables using the DataView object (not covered in this presentation) Compare allows comparisons between 2 table objects. Clear empties all the records. Clone mirrors the schema, copy mirrors the schema and table contents
DataRelations are the ADO.net equivalent of Data Shaping in ADO 2.6 Too big to cover