|
Comments
Did you read today's front page stories & breaking news?
SYS-CON.TV
|
Features All Data, All the Time…
All Data, All the Time…
Jan. 1, 2000 12:00 AM
In this "Data Access" column, I will discuss topics relating to the Microsoft .NET data access stack, known as ADO.NET. This installment starts with an overview of the different pieces of the stack and finishes with an interesting development in the world of SQL Server, .NET, and XML Web services.
The ADO.NET data access APIs consist of three types of classes:
An ADO.NET Crash Course
Executing Database Commands in Connected Mode
The simplest starter program for reading a resultset (the results of a SQL SELECT statement) containing two columns from a SQL Server table (in C#) would look like Listing 1. The program uses the SqlClient data provider's Connection class (which includes a constructor that takes a database connection string) to open a connection to SQL Server. The Command class encapsulates SQL statement functionality and includes a constructor that takes a SQL command string. We use this to execute the SQL; the results are returned in a DataReader class. This class has methods that allow us to move forward through the rows in the resultset, along with methods that allow us to access the data in each row by using zero-based column ordinals. We'll discuss using data providers with different data sources in more detail in future columns. In addition to Microsoft's data providers, third parties can build data providers. For example, DataDirect Technologies has shipped native providers for Oracle and Sybase. You can also build your own. I built a simple data provider that's available for download at http://staff.develop.com/bobb.
Disconnected Data and the DataSet Class
Listing 2 shows a simple program that reads data into a DataSet, updates the DataSet, and flushes the updates back to the database. Note that you don't have to open the connection directly when you're using a DataAdapter because the DataAdapter.Fill method opens the connection, reads the rows, and then closes the connection automatically. Although using a DataSet is similar in concept to using the disconnected ADO Recordset class, a DataSet can contain multiple tables and the XML integration is infinitely more flexible. It's instructive to look at the DataSet as an ADO Recordset on steroids. You can use DataSets with data from any data provider, synthesize tables and data using the APIs directly, or fill the DataSet using XML. Using DataSets alone or in conjunction with the XmlDataDocument class (a hybrid of DataSet and XmlDocument) allows you to transparently mix and switch between XML and relational data. Here's an example that writes a DataSet as XML:
DataSet ds = new DataSet(); I'll look at some of the intricacies of the DataSet and related classes in future columns.
SQL Server, XML, and Web Services
The SQLXML Configuration Tool
SQLXML 3.0 makes exposing results from a stored procedure, user-defined function, or SQLXML template query (a SQL or XPath query bracketed by XML) as easy as configuring it in the SQLXML IIS configuration utility. No server-side code need be written. You even have a few choices of how to return your SQL results: as a .NET DataSet, an array of .NET DataSets, or an array of XmlElement. The XmlElement is a .NET class in the managed XML stack that can represent an XML document or a document fragment. When you're returning arrays, you represent this in your .NET client program as an array of type Object, because your stored procedure may return errors of type SqlMessage if errors occur in execution. I'll explain more about the array of objects when we write the client. You also have a choice of whether your SQL resultsets are generated in "XML RAW" format or "XML NESTED" format. XML RAW format returns each row in the resultset as an XML element named row; columns are returned in attribute normal form (i.e., each column is an attribute of the row element). XML NESTED format returns hierarchically nested XML elements named after the tables used in the SQL SELECT statement. There may be multiple levels of hierarchy, depending on the SQL statement. You also have the option of configuring your Web service methods to return a SOAP Fault element instead of a SqlMessage element if errors occur. To configure the SQLXML IIS utility to return SOAP, you must configure a virtual name for the SOAP Web service after mapping a SQLXML virtual directory to a SQL Server and a specific database. You use the Virtual Names tab to accomplish this. You can add one or more SOAP virtual names per virtual directory if you want. Although the SOAP virtual name can be anything, a common convention is to name it "soap" (see Figure 1). Once you've configured the SOAP virtual name, you add access to your stored procedures, user-defined functions, or templates using the configuration button. The XML output format options mentioned in the previous paragraph can be specified on a per-method basis. The SQLXML configuration tool even generates the WSDL (Web Service Description Language) that's used to describe your Web service parameters to the outside world automatically when you save the configuration. The endpoint that exposes this WSDL document is a combination of the virtual directory and the SOAP virtual name with "?WSDL" appended so for example, if the name of the virtual directory is "pubs" and the SOAP virtual name is "soap," the WSDL may be obtained at http://webserver/pubs/soap?WSDL. Figure 2 shows how to configure a template. In this case, the stored procedure byroyalty is mapped to a Web service endpoint, named AuthorsByRoyaltyAsDataSets, using the raw row formatting style. This Web service returns errors as SqlMessage types rather than as native SOAP errors.
Writing the Client
I mentioned earlier that I would explain what "array of objects" means
as a Web services return value. This is best illustrated by inspecting the
WSDL generated by the configuration tool. The types section of the WSDL file
contains schemas for four generic types:
These types are defined in three different XSD schemas exposed inside the WSDL document. A list of all the schemas for all of the types is beyond the scope of this article, but as an example, here's the schema for the SqlXml and SqlRowSet complex types. Note that the actual DataSet or XmlElement result is represented as XSD type xsd:any:
<!-- returns a DataSet --> If we return an array of DataSet or XmlElement it may also contain the SqlMessage complex type if errors occur, so the entire series of results, known as a SqlResultStream type, appears to the client as an array of .NET type Object. In that case, the correct type of any part of the SqlResultStream can be determined by using Object.GetType as shown in the sample program or by prior knowledge of the result by the programmer. Web services aficionados may be concerned with the loose typing implied by the xsd:any types in the SqlRowSet and SqlXml complex types. It's necessary for specific reasons. First, the DataSet itself is a generic container class; you can deduce the schema of a specific DataSet instance only from the inline schema in the SqlRowSet. Second, the WSDL generator in the SQLXML configuration utility can't obtain metadata from the database that describes stored procedures' resultsets. Stored procedures may obtain different results and even different numbers of results on a case-by-case basis. But how will these types be accessed by non-.NET clients that don't have definitions of the XmlElement or DataSet classes? Doesn't this amount to returning every result as generic, untyped XML and forcing the client to figure out what's in there? Although the SQLXML configuration utility doesn't know what can be obtained from the stored procedure, UDF, or template returns, the Transact SQL programmer of these XML Web services does know. For non-.NET clients that require strong typing, you can hand-code alternative WSDL and make it available at an alternate endpoint. If your client can handle inline schema, this may be as easy as exposing a custom schema that specifies a complex type your stored procedure produces. In the case of SqlXml, you can code an alternate (strongly typed) complex type. For example, the result returned in the example above could be coded as:
<!‹- this schema contains the This complex type should be usable by any client, not only those written in ADO.NET.
Direct Programmatic Access
Next Time
Reader Feedback: Page 1 of 1
Latest Cloud Developer Stories
Subscribe to the World's Most Powerful Newsletters
Subscribe to Our Rss Feeds & Get Your SYS-CON News Live!
|
SYS-CON Featured Whitepapers
Most Read This Week
Breaking Cloud Computing News |
|||||||||||||||||||||||||||||||||||||||||||||||||