FeatureAll Data, All the Time…An introduction to .NET data accessand SQLXML Web servicesSERVERBY BOB BEAUCHEMINHOMEDESKTOPEVERYWHEREin this “Data Access” column,I will discuss topicsrelating to the Micros<strong>of</strong>t.NET data access stack,known as ADO.NET. This installmentstarts with an overview <strong>of</strong> thedifferent pieces <strong>of</strong> the stack and finisheswith an interesting developmentin the world <strong>of</strong> SQL Server,.NET, and XML Web services.The ADO.NET data access APIs<strong>con</strong>sist <strong>of</strong> three types <strong>of</strong> classes:1. Classes for executing databasecommands in <strong>con</strong>nected mode:This is accomplished by a series<strong>of</strong> data providers that run inmanaged code as much as possible.Each <strong>of</strong> these data providersimplements approximately thesame set <strong>of</strong> interfaces andexposes an analogous class hierarchy.2. Dis<strong>con</strong>nected data access classes:A series <strong>of</strong> classes in theSystem.Data namespace that<strong>con</strong>sists <strong>of</strong> a complex in-memorycollection class (called theDataSet), which approximatesrelational database semantics,and supporting classes such asthe DataTable, DataRow, andDataRelation.3. Classes for XML integration:DataSets, with help from a fewadditional classes in the System.Xmlnamespace (althoughthey “live” in the System.Data.dll),integrate the XML and relationaldata access worlds. It’s also possibleto allow for data interchangewith heterogeneous platforms,such as Unix <strong>sys</strong>tems and IBMmainframes, using XML Web services.An ADO.NET Crash CourseWe’ll start with a crash course inADO.NET <strong>con</strong>sisting <strong>of</strong> some simpleprograms.Executing Database Commandsin Connected ModeData providers include a SQLServer–specific provider (theSqlClient data provider) and adata provider, known as the OleDbdata provider, which is a bridgefor OLE DB data sources. Justbefore .NET shipped in February,Micros<strong>of</strong>t released a bridgeprovider for ODBC data sourcesfor download from the Web. So, ifyou use SQL Server or have anOLE DB provider or ODBC driverfor your database, you can use the.NET data access classes directlywith your data.The simplest starter program forreading a resultset (the results <strong>of</strong> aSQL SELECT statement) <strong>con</strong>tainingtwo columns from a SQL Server table(in C#) would look like Listing 1.The program uses the SqlClientdata provider’s Connection class(which includes a <strong>con</strong>structor thattakes a database <strong>con</strong>nection string)to open a <strong>con</strong>nection to SQL Server.The Command class encapsulatesSQL statement functionality andincludes a <strong>con</strong>structor that takes aSQL command string. We use this toexecute the SQL; the results arereturned in a DataReader class. Thisclass has methods that allow us tomove forward through the rows inthe resultset, along with methodsthat allow us to access the data ineach row by using zero-based columnordinals.We’ll discuss using dataproviders with different datasources in more detail in futurecolumns. In addition to Micros<strong>of</strong>t’sdata providers, third parties canbuild data providers. For example,DataDirect Technologies hasshipped native providers for Oracleand Sybase. You can also build yourown. I built a simple data providerthat’s available for download athttp://staff.develop.com/bobb.Dis<strong>con</strong>nected Dataand the DataSet ClassThe DataSet class is a specializedcollection class that includes a collectionthat follows the relationaldata model. If you’re used to dealingwith tables, columns, rows, andrelationships, you’ll be right athome with DataSet. DataSets can beused as an in-memory cache or adata source for ASP.NET Web Forms<strong>con</strong>trols, or they can be directlybound and manipulated byWindows Forms <strong>con</strong>trols. DataSetsinteract with data providers througha data provider’s DataAdapter class.Listing 2 shows a simple programthat reads data into a DataSet,updates the DataSet, and flushesthe updates back to the database.Note that you don’t have to open the<strong>con</strong>nection directly when you’reusing a DataAdapter because theDataAdapter.Fill method opens the<strong>con</strong>nection, reads the rows, andthen closes the <strong>con</strong>nection automatically.Although using a DataSet is similarin <strong>con</strong>cept to using the dis<strong>con</strong>nectedADO Recordset class, aDataSet can <strong>con</strong>tain multiple tables16Preview Issue • dot<strong>net</strong>developers<strong>journal</strong>.com
and the XML integration is infinitelymore flexible. It’s instructive to lookat the DataSet as an ADO Recordseton steroids.You can use DataSets with datafrom any data provider, synthesizetables and data using the APIsdirectly, or fill the DataSet usingXML. Using DataSets alone or in<strong>con</strong>junction with the XmlDataDocumentclass (a hybrid <strong>of</strong> DataSetand XmlDocument) allows you totransparently mix and switchbetween XML and relational data.Here’s an example that writes aDataSet as XML:DataSet ds = new DataSet();SqlDataAdapter da = newSqlDataAdapter("select au_id, au_lname fromauthors","server=myserver;uid=myuser;pwd=mypwd;database=pubs");da.Fill(ds, "authors");ds.WriteXml("c:\\authors.xml");ds.WriteXmlSchema("c:\\authors.xsd");I’ll look at some <strong>of</strong> the intricacies<strong>of</strong> the DataSet and related classes infuture columns.SQL Server, XML,and Web ServicesNow that we’ve covered thebasics <strong>of</strong> the ADO.NET data accessstack, let’s turn to another featurethat helps integrate Micros<strong>of</strong>t’s SQLServer database, XML documents,.NET programming, and XML Webservices. This extension to SQLServer, known as SQLXML, is availableon the Web for download.The SQLXML Configuration ToolThe SQLXML 3.0 version addedthe capability to expose data fromSQL Server and perform databasemaintenance through Web services.This means that database operationscan be accomplished from anyplatform that supports Web services,without needing SQL Serverclient libraries, OLE DB providers,or ODBC drivers to be installed oneach user’s workstation. This isimplemented through an IIS ISAPI(Inter<strong>net</strong> Services API) applicationthat supports sending and receivingmessages using SOAP (Simpledot<strong>net</strong>developers<strong>journal</strong>.com • Preview IssueObject Access Protocol). The SOAPprotocol (and Web services in general)is designed to provide interoperabilitybetween heterogeneousplatforms. You don’t need to usethem when you have a direct <strong>con</strong>nectionto SQL Server and can useSQL Server’s native TDS (TabularData Stream) protocol to communicate.However, if you have a Webservice that passes DataSets aroundas XML, there’s no automatic way toprovide .NET DataSets to VB6clients. VB6 could in this case be<strong>con</strong>sidered a heterogeneous platformas well.SQLXML 3.0 makes exposingresults from a stored procedure,user-defined function, or SQLXMLtemplate query (a SQL or XPathquery bracketed by XML) as easy as<strong>con</strong>figuring it in the SQLXML IIS<strong>con</strong>figuration utility. No server-sidecode need be written. You even havea few choices <strong>of</strong> how to return yourSQL results: as a .NET DataSet, anarray <strong>of</strong> .NET DataSets, or an array<strong>of</strong> XmlElement. The XmlElement isa .NET class in the managed XMLstack that can represent an XMLdocument or a document fragment.When you’re returning arrays, yourepresent this in your .NET clientprogram as an array <strong>of</strong> type Object,because your stored procedure mayreturn errors <strong>of</strong> type SqlMessage iferrors occur in execution. I’llexplain more about the array <strong>of</strong>objects when we write the client.You also have a choice <strong>of</strong>whether your SQL resultsets aregenerated in “XML RAW” format or“XML NESTED” format. XML RAWformat returns each row in theresultset as an XML element namedrow; columns are returned in attributenormal form (i.e., each columnis an attribute <strong>of</strong> the row element).XML NESTED format returns hierarchicallynested XML elementsnamed after the tables used in theSQL SELECT statement. There maybe multiple levels <strong>of</strong> hierarchy,depending on the SQL statement.You also have the option <strong>of</strong> <strong>con</strong>figuringyour Web service methods toreturn a SOAP Fault element instead<strong>of</strong> a SqlMessage element if errorsoccur.To <strong>con</strong>figure the SQLXML IISutility to return SOAP, you must<strong>con</strong>figure a virtual name for theSOAP Web service after mapping aSQLXML virtual directory to a SQLServer and a specific database. Youuse the Virtual Names tab to accomplishthis. You can add one or moreSOAP virtual names per virtualdirectory if you want. Although theSOAP virtual name can be anything,a common <strong>con</strong>vention is to name it“soap” (see Figure 1).Once you’ve <strong>con</strong>figured theSOAP virtual name, you add accessto your stored procedures, userdefinedfunctions, or templatesusing the <strong>con</strong>figuration button. TheXML output format options mentionedin the previous paragraphcan be specified on a per-methodbasis. The SQLXML <strong>con</strong>figurationtool even generates the WSDL (WebService Description Language) that’sused to describe your Web serviceparameters to the outside worldautomatically when you save the<strong>con</strong>figuration. The endpoint thatexposes this WSDL document is acombination <strong>of</strong> the virtual directoryand the SOAP virtual name with“?WSDL” appended – so for example,if the name <strong>of</strong> the virtual directoryis “pubs” and the SOAP virtualname is “soap,” the WSDL may beobtained at http://webserver/pubs/soap?WSDL.Figure 2 shows how to <strong>con</strong>figurea template. In this case, the storedprocedure byroyalty is mapped to aWeb service endpoint, namedAuthorsByRoyaltyAsDataSets, usingthe raw row formatting style. ThisWeb service returns errors asSqlMessage types rather than asnative SOAP errors.Writing the ClientNow that we’ve “written” the Webservice – by virtue <strong>of</strong> having a storedprocedure, UDF (user-defined function),or SQLXML template alreadyin place (wasn’t that easy?) – let’swrite the client. Writing a .NET clientis fairly automatic (as it should be),no matter how we’ve chosen toreturn the results. In any VS.NETclient project, click on references inthe Project Explorer pane andchoose “Add Web Reference.” Pointthe Add Web Reference dialog at theWSDL file, and click “Add Reference.”A Web service proxy class is automaticallygenerated. This proxy class will<strong>con</strong>tain methods that can be used to17