Você está na página 1de 272

2541B: Core Data Access

with Microsoft Visual


Studio 2005

Toolkit

Information in this document, including URL and other Internet Web site references, is subject to
change without notice. Unless otherwise noted, the example companies, organizations, products,
domain names, e-mail addresses, logos, people, places, and events depicted herein are fictitious,
and no association with any real company, organization, product, domain name, e-mail address,
logo, person, place, or event is intended or should be inferred. Complying with all applicable
copyright laws is the responsibility of the user. Without limiting the rights under copyright, no part
of this document may be reproduced, stored in or introduced into a retrieval system, or transmitted
in any form or by any means (electronic, mechanical, photocopying, recording, or otherwise), or
for any purpose, without the express written permission of Microsoft Corporation.
The names of manufacturers, products, or URLs are provided for informational purposes only and
Microsoft makes no representations and warranties, either expressed, implied, or statutory,
regarding these manufacturers or the use of the products with any Microsoft technologies. The
inclusion of a manufacturer or product does not imply endorsement of Microsoft of the
manufacturer or product. Links are provided to third party sites. Such sites are not under the
control of Microsoft and Microsoft is not responsible for the contents of any linked site or any link
contained in a linked site, or any changes or updates to such sites. Microsoft is not responsible for
webcasting or any other form of transmission received from any linked site. Microsoft is providing
these links to you only as a convenience, and the inclusion of any link does not imply endorsement
of Microsoft of the site or the products contained therein.
Microsoft may have patents, patent applications, trademarks, copyrights, or other intellectual
property rights covering subject matter in this document. Except as expressly provided in any
written license agreement from Microsoft, the furnishing of this document does not give you any
license to these patents, trademarks, copyrights, or other intellectual property.
2006 Microsoft Corporation. All rights reserved.
Microsoft, Active Directory, ActiveSync, ActiveX, FrontPage, Hotmail, IntelliSense,
JScript, MSDN, Outlook, PowerPoint, Visual Basic, Visual C#, Visual Studio,
Visual Web Developer, Win32, Windows, and Windows Server are either registered trademarks or
trademarks of Microsoft Corporation in the United States and/or other countries.
All other trademarks are property of their respective owners.

Workshop: 2541B
Part Number: X12-11889
Released: 2/2006

END-USER LICENSE AGREEMENT FOR OFFICIAL MICROSOFT LEARNING PRODUCTS STUDENT EDITION
PLEASE READ THIS END-USER LICENSE AGREEMENT (EULA) CAREFULLY. BY USING THE MATERIALS AND/OR
USING OR INSTALLING THE SOFTWARE THAT ACCOMPANIES THIS EULA (COLLECTIVELY, THE LICENSED
CONTENT), YOU AGREE TO THE TERMS OF THIS EULA. IF YOU DO NOT AGREE, DO NOT USE THE LICENSED
CONTENT.
1.
GENERAL. This EULA is a legal agreement between you (either an individual or a single entity) and Microsoft Corporation
(Microsoft). This EULA governs the Licensed Content, which includes computer software (including online and electronic
documentation), training materials, and any other associated media and printed materials. This EULA applies to updates, supplements,
add-on components, and Internet-based services components of the Licensed Content that Microsoft may provide or make available to
you unless Microsoft provides other terms with the update, supplement, add-on component, or Internet-based services component.
Microsoft reserves the right to discontinue any Internet-based services provided to you or made available to you through the use of the
Licensed Content. This EULA also governs any product support services relating to the Licensed Content except as may be included in
another agreement between you and Microsoft. An amendment or addendum to this EULA may accompany the Licensed Content.
2.
GENERAL GRANT OF LICENSE. Microsoft grants you the following rights, conditioned on your compliance with all the
terms and conditions of this EULA. Microsoft grants you a limited, non-exclusive, royalty-free license to install and use the Licensed
Content solely in conjunction with your participation as a student in an Authorized Training Session (as defined below). You may
install and use one copy of the software on a single computer, device, workstation, terminal, or other digital electronic or analog device
("Device"). You may make a second copy of the software and install it on a portable Device for the exclusive use of the person who is the
primary user of the first copy of the software. A license for the software may not be shared for use by multiple end users. An
Authorized Training Session means a training session conducted at a Microsoft Certified Technical Education Center, an IT Academy,
via a Microsoft Certified Partner, or such other entity as Microsoft may designate from time to time in writing, by a Microsoft Certified
Trainer (for more information on these entities, please visit www.microsoft.com). WITHOUT LIMITING THE FOREGOING, COPYING
OR REPRODUCTION OF THE LICENSED CONTENT TO ANY SERVER OR LOCATION FOR FURTHER REPRODUCTION OR
REDISTRIBUTION IS EXPRESSLY PROHIBITED.
3.

DESCRIPTION OF OTHER RIGHTS AND LICENSE LIMITATIONS


3.1

Use of Documentation and Printed Training Materials.

3.1.1
The documents and related graphics included in the Licensed Content may include technical inaccuracies
or typographical errors. Changes are periodically made to the content. Microsoft may make improvements and/or changes in any of
the components of the Licensed Content at any time without notice. The names of companies, products, people, characters and/or data
mentioned in the Licensed Content may be fictitious and are in no way intended to represent any real individual, company, product or
event, unless otherwise noted.
3.1.2
Microsoft grants you the right to reproduce portions of documents (such as student workbooks, white
papers, press releases, datasheets and FAQs) (the Documents) provided with the Licensed Content. You may not print any book
(either electronic or print version) in its entirety. If you choose to reproduce Documents, you agree that: (a) use of such printed
Documents will be solely in conjunction with your personal training use; (b) the Documents will not republished or posted on any
network computer or broadcast in any media; (c) any reproduction will include either the Documents original copyright notice or a
copyright notice to Microsofts benefit substantially in the format provided below; and (d) to comply with all terms and conditions of
this EULA. In addition, no modifications may made to any Document.

Form of Notice:
Copyright undefined.
2006. Reprinted with permission by Microsoft Corporation. All rights reserved.
Microsoft and Windows are either registered trademarks or trademarks of Microsoft Corporation in
the US and/or other countries. Other product and company names mentioned herein may be the
trademarks of their respective owners.
3.2
Use of Media Elements. The Licensed Content may include certain photographs, clip art, animations, sounds, music,
and video clips (together "Media Elements"). You may not modify these Media Elements.
3.3
Use of Sample Code. In the event that the Licensed Content include sample source code (Sample Code), Microsoft
grants you a limited, non-exclusive, royalty-free license to use, copy and modify the Sample Code; if you elect to exercise the foregoing
rights, you agree to comply with all other terms and conditions of this EULA, including without limitation Sections 3.4, 3.5, and 6.
3.4
Permitted Modifications. In the event that you exercise any rights provided under this EULA to create modifications
of the Licensed Content, you agree that any such modifications: (a) will not be used for providing training where a fee is charged in
public or private classes; (b) indemnify, hold harmless, and defend Microsoft from and against any claims or lawsuits, including
attorneys fees, which arise from or result from your use of any modified version of the Licensed Content; and (c) not to transfer or
assign any rights to any modified version of the Licensed Content to any third party without the express written permission of
Microsoft.

3.5
Reproduction/Redistribution Licensed Content. Except as expressly provided in this EULA, you may not reproduce or
distribute the Licensed Content or any portion thereof (including any permitted modifications) to any third parties without the express
written permission of Microsoft.
4.
RESERVATION OF RIGHTS AND OWNERSHIP. Microsoft reserves all rights not expressly granted to you in this EULA.
The Licensed Content is protected by copyright and other intellectual property laws and treaties. Microsoft or its suppliers own the
title, copyright, and other intellectual property rights in the Licensed Content. You may not remove or obscure any copyright,
trademark or patent notices that appear on the Licensed Content, or any components thereof, as delivered to you. The Licensed
Content is licensed, not sold.
5.
LIMITATIONS ON REVERSE ENGINEERING, DECOMPILATION, AND DISASSEMBLY. You may not reverse
engineer, decompile, or disassemble the Software or Media Elements, except and only to the extent that such activity is expressly
permitted by applicable law notwithstanding this limitation.
6.
LIMITATIONS ON SALE, RENTAL, ETC. AND CERTAIN ASSIGNMENTS. You may not provide commercial hosting
services with, sell, rent, lease, lend, sublicense, or assign copies of the Licensed Content, or any portion thereof (including any permitted
modifications thereof) on a stand-alone basis or as part of any collection, product or service.
7.
CONSENT TO USE OF DATA. You agree that Microsoft and its affiliates may collect and use technical information
gathered as part of the product support services provided to you, if any, related to the Licensed Content. Microsoft may use this
information solely to improve our products or to provide customized services or technologies to you and will not disclose this
information in a form that personally identifies you.
8.
LINKS TO THIRD PARTY SITES. You may link to third party sites through the use of the Licensed Content. The third
party sites are not under the control of Microsoft, and Microsoft is not responsible for the contents of any third party sites, any links
contained in third party sites, or any changes or updates to third party sites. Microsoft is not responsible for webcasting or any other
form of transmission received from any third party sites. Microsoft is providing these links to third party sites to you only as a
convenience, and the inclusion of any link does not imply an endorsement by Microsoft of the third party site.
9.
ADDITIONAL LICENSED CONTENT/SERVICES. This EULA applies to updates, supplements, add-on components, or
Internet-based services components, of the Licensed Content that Microsoft may provide to you or make available to you after the date
you obtain your initial copy of the Licensed Content, unless we provide other terms along with the update, supplement, add-on
component, or Internet-based services component. Microsoft reserves the right to discontinue any Internet-based services provided to
you or made available to you through the use of the Licensed Content.
10.
U.S. GOVERNMENT LICENSE RIGHTS. All software provided to the U.S. Government pursuant to solicitations issued on
or after December 1, 1995 is provided with the commercial license rights and restrictions described elsewhere herein. All software
provided to the U.S. Government pursuant to solicitations issued prior to December 1, 1995 is provided with Restricted Rights as
provided for in FAR, 48 CFR 52.227-14 (JUNE 1987) or DFAR, 48 CFR 252.227-7013 (OCT 1988), as applicable.
11.
EXPORT RESTRICTIONS. You acknowledge that the Licensed Content is subject to U.S. export jurisdiction. You agree to
comply with all applicable international and national laws that apply to the Licensed Content, including the U.S. Export Administration
Regulations, as well as end-user, end-use, and destination restrictions issued by U.S. and other governments. For additional
information see <http://www.microsoft.com/exporting/>.
12.
TRANSFER. The initial user of the Licensed Content may make a one-time permanent transfer of this EULA and Licensed
Content to another end user, provided the initial user retains no copies of the Licensed Content. The transfer may not be an indirect
transfer, such as a consignment. Prior to the transfer, the end user receiving the Licensed Content must agree to all the EULA terms.
13.
NOT FOR RESALE LICENSED CONTENT. Licensed Content identified as Not For Resale or NFR, may not be sold
or otherwise transferred for value, or used for any purpose other than demonstration, test or evaluation.
14.
TERMINATION. Without prejudice to any other rights, Microsoft may terminate this EULA if you fail to comply with the
terms and conditions of this EULA. In such event, you must destroy all copies of the Licensed Content and all of its component parts.
15.
DISCLAIMER OF WARRANTIES. TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, MICROSOFT
AND ITS SUPPLIERS PROVIDE THE LICENSED CONTENT AND SUPPORT SERVICES (IF ANY) AS IS AND WITH ALL
FAULTS, AND MICROSOFT AND ITS SUPPLIERS HEREBY DISCLAIM ALL OTHER WARRANTIES AND CONDITIONS,
WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY (IF ANY) IMPLIED
WARRANTIES, DUTIES OR CONDITIONS OF MERCHANTABILITY, OF FITNESS FOR A PARTICULAR PURPOSE, OF
RELIABILITY OR AVAILABILITY, OF ACCURACY OR COMPLETENESS OF RESPONSES, OF RESULTS, OF WORKMANLIKE
EFFORT, OF LACK OF VIRUSES, AND OF LACK OF NEGLIGENCE, ALL WITH REGARD TO THE LICENSED CONTENT,
AND THE PROVISION OF OR FAILURE TO PROVIDE SUPPORT OR OTHER SERVICES, INFORMATION, SOFTWARE, AND
RELATED CONTENT THROUGH THE LICENSED CONTENT, OR OTHERWISE ARISING OUT OF THE USE OF THE
LICENSED CONTENT. ALSO, THERE IS NO WARRANTY OR CONDITION OF TITLE, QUIET ENJOYMENT, QUIET
POSSESSION, CORRESPONDENCE TO DESCRIPTION OR NON-INFRINGEMENT WITH REGARD TO THE LICENSED
CONTENT. THE ENTIRE RISK AS TO THE QUALITY, OR ARISING OUT OF THE USE OR PERFORMANCE OF THE
LICENSED CONTENT, AND ANY SUPPORT SERVICES, REMAINS WITH YOU.
16.
EXCLUSION OF INCIDENTAL, CONSEQUENTIAL AND CERTAIN OTHER DAMAGES. TO THE MAXIMUM
EXTENT PERMITTED BY APPLICABLE LAW, IN NO EVENT SHALL MICROSOFT OR ITS SUPPLIERS BE LIABLE FOR ANY
SPECIAL, INCIDENTAL, PUNITIVE, INDIRECT, OR CONSEQUENTIAL DAMAGES WHATSOEVER (INCLUDING, BUT NOT

LIMITED TO, DAMAGES FOR LOSS OF PROFITS OR CONFIDENTIAL OR OTHER INFORMATION, FOR BUSINESS
INTERRUPTION, FOR PERSONAL INJURY, FOR LOSS OF PRIVACY, FOR FAILURE TO MEET ANY DUTY INCLUDING OF
GOOD FAITH OR OF REASONABLE CARE, FOR NEGLIGENCE, AND FOR ANY OTHER PECUNIARY OR OTHER LOSS
WHATSOEVER) ARISING OUT OF OR IN ANY WAY RELATED TO THE USE OF OR INABILITY TO USE THE LICENSED
CONTENT, THE PROVISION OF OR FAILURE TO PROVIDE SUPPORT OR OTHER SERVICES, INFORMATION, SOFTWARE,
AND RELATED CONTENT THROUGH THE LICENSED CONTENT, OR OTHERWISE ARISING OUT OF THE USE OF THE
LICENSED CONTENT, OR OTHERWISE UNDER OR IN CONNECTION WITH ANY PROVISION OF THIS EULA, EVEN IN
THE EVENT OF THE FAULT, TORT (INCLUDING NEGLIGENCE), MISREPRESENTATION, STRICT LIABILITY, BREACH OF
CONTRACT OR BREACH OF WARRANTY OF MICROSOFT OR ANY SUPPLIER, AND EVEN IF MICROSOFT OR ANY
SUPPLIER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. BECAUSE SOME STATES/JURISDICTIONS
DO NOT ALLOW THE EXCLUSION OR LIMITATION OF LIABILITY FOR CONSEQUENTIAL OR INCIDENTAL DAMAGES,
THE ABOVE LIMITATION MAY NOT APPLY TO YOU.
17.
LIMITATION OF LIABILITY AND REMEDIES. NOTWITHSTANDING ANY DAMAGES THAT YOU MIGHT INCUR
FOR ANY REASON WHATSOEVER (INCLUDING, WITHOUT LIMITATION, ALL DAMAGES REFERENCED HEREIN AND
ALL DIRECT OR GENERAL DAMAGES IN CONTRACT OR ANYTHING ELSE), THE ENTIRE LIABILITY OF MICROSOFT
AND ANY OF ITS SUPPLIERS UNDER ANY PROVISION OF THIS EULA AND YOUR EXCLUSIVE REMEDY HEREUNDER
SHALL BE LIMITED TO THE GREATER OF THE ACTUAL DAMAGES YOU INCUR IN REASONABLE RELIANCE ON THE
LICENSED CONTENT UP TO THE AMOUNT ACTUALLY PAID BY YOU FOR THE LICENSED CONTENT OR US$5.00. THE
FOREGOING LIMITATIONS, EXCLUSIONS AND DISCLAIMERS SHALL APPLY TO THE MAXIMUM EXTENT PERMITTED
BY APPLICABLE LAW, EVEN IF ANY REMEDY FAILS ITS ESSENTIAL PURPOSE.
18.
APPLICABLE LAW. If you acquired this Licensed Content in the United States, this EULA is governed by the laws of the
State of Washington. If you acquired this Licensed Content in Canada, unless expressly prohibited by local law, this EULA is governed
by the laws in force in the Province of Ontario, Canada; and, in respect of any dispute which may arise hereunder, you consent to the
jurisdiction of the federal and provincial courts sitting in Toronto, Ontario. If you acquired this Licensed Content in the European
Union, Iceland, Norway, or Switzerland, then local law applies. If you acquired this Licensed Content in any other country, then local
law may apply.
19.
ENTIRE AGREEMENT; SEVERABILITY. This EULA (including any addendum or amendment to this EULA which is
included with the Licensed Content) are the entire agreement between you and Microsoft relating to the Licensed Content and the
support services (if any) and they supersede all prior or contemporaneous oral or written communications, proposals and
representations with respect to the Licensed Content or any other subject matter covered by this EULA. To the extent the terms of any
Microsoft policies or programs for support services conflict with the terms of this EULA, the terms of this EULA shall control. If any
provision of this EULA is held to be void, invalid, unenforceable or illegal, the other provisions shall continue in full force and effect.
Should you have any questions concerning this EULA, or if you desire to contact Microsoft for any reason, please use the address
information enclosed in this Licensed Content to contact the Microsoft subsidiary serving your country or visit Microsoft on the World
Wide Web at http://www.microsoft.com.
Si vous avez acquis votre Contenu Sous Licence Microsoft au CANADA :
DNI DE GARANTIES. Dans la mesure maximale permise par les lois applicables, le Contenu Sous Licence et les services de
soutien technique (le cas chant) sont fournis TELS QUELS ET AVEC TOUS LES DFAUTS par Microsoft et ses fournisseurs,
lesquels par les prsentes dnient toutes autres garanties et conditions expresses, implicites ou en vertu de la loi, notamment, mais
sans limitation, (le cas chant) les garanties, devoirs ou conditions implicites de qualit marchande, dadaptation une fin usage
particulire, de fiabilit ou de disponibilit, dexactitude ou dexhaustivit des rponses, des rsultats, des efforts dploys selon
les rgles de lart, dabsence de virus et dabsence de ngligence, le tout lgard du Contenu Sous Licence et de la prestation des
services de soutien technique ou de lomission de la une telle prestation des services de soutien technique ou lgard de la
fourniture ou de lomission de la fourniture de tous autres services, renseignements, Contenus Sous Licence, et contenu qui sy
rapporte grce au Contenu Sous Licence ou provenant autrement de lutilisation du Contenu Sous Licence. PAR AILLEURS, IL NY
A AUCUNE GARANTIE OU CONDITION QUANT AU TITRE DE PROPRIT, LA JOUISSANCE OU LA POSSESSION
PAISIBLE, LA CONCORDANCE UNE DESCRIPTION NI QUANT UNE ABSENCE DE CONTREFAON CONCERNANT
LE CONTENU SOUS LICENCE.
EXCLUSION DES DOMMAGES ACCESSOIRES, INDIRECTS ET DE CERTAINS AUTRES DOMMAGES.

DANS LA MESURE

MAXIMALE PERMISE PAR LES LOIS APPLICABLES, EN AUCUN CAS MICROSOFT OU SES FOURNISSEURS NE SERONT
RESPONSABLES DES DOMMAGES SPCIAUX, CONSCUTIFS, ACCESSOIRES OU INDIRECTS DE QUELQUE NATURE QUE
CE SOIT (NOTAMMENT, LES DOMMAGES LGARD DU MANQUE GAGNER OU DE LA DIVULGATION DE
RENSEIGNEMENTS CONFIDENTIELS OU AUTRES, DE LA PERTE DEXPLOITATION, DE BLESSURES CORPORELLES, DE
LA VIOLATION DE LA VIE PRIVE, DE LOMISSION DE REMPLIR TOUT DEVOIR, Y COMPRIS DAGIR DE BONNE FOI OU
DEXERCER UN SOIN RAISONNABLE, DE LA NGLIGENCE ET DE TOUTE AUTRE PERTE PCUNIAIRE OU AUTRE PERTE

DE QUELQUE NATURE QUE CE SOIT) SE RAPPORTANT DE QUELQUE MANIRE QUE CE SOIT LUTILISATION DU
CONTENU SOUS LICENCE OU LINCAPACIT DE SEN SERVIR, LA PRESTATION OU LOMISSION DE LA UNE
TELLE PRESTATION DE SERVICES DE SOUTIEN TECHNIQUE OU LA FOURNITURE OU LOMISSION DE LA
FOURNITURE DE TOUS AUTRES SERVICES, RENSEIGNEMENTS, CONTENUS SOUS LICENCE, ET CONTENU QUI SY
RAPPORTE GRCE AU CONTENU SOUS LICENCE OU PROVENANT AUTREMENT DE LUTILISATION DU CONTENU
SOUS LICENCE OU AUTREMENT AUX TERMES DE TOUTE DISPOSITION DE LA U PRSENTE CONVENTION EULA OU
RELATIVEMENT UNE TELLE DISPOSITION, MME EN CAS DE FAUTE, DE DLIT CIVIL (Y COMPRIS LA NGLIGENCE),
DE RESPONSABILIT STRICTE, DE VIOLATION DE CONTRAT OU DE VIOLATION DE GARANTIE DE MICROSOFT OU DE
TOUT FOURNISSEUR ET MME SI MICROSOFT OU TOUT FOURNISSEUR A T AVIS DE LA POSSIBILIT DE TELS
DOMMAGES.
LIMITATION DE RESPONSABILIT ET RECOURS.

MALGR LES DOMMAGES QUE VOUS PUISSIEZ SUBIR POUR

QUELQUE MOTIF QUE CE SOIT (NOTAMMENT, MAIS SANS LIMITATION, TOUS LES DOMMAGES SUSMENTIONNS ET
TOUS LES DOMMAGES DIRECTS OU GNRAUX OU AUTRES), LA SEULE RESPONSABILIT OBLIGATION INTGRALE
DE MICROSOFT ET DE LUN OU LAUTRE DE SES FOURNISSEURS AUX TERMES DE TOUTE DISPOSITION DEU LA
PRSENTE CONVENTION EULA ET VOTRE RECOURS EXCLUSIF LGARD DE TOUT CE QUI PRCDE SE LIMITE AU
PLUS LEV ENTRE LES MONTANTS SUIVANTS : LE MONTANT QUE VOUS AVEZ RELLEMENT PAY POUR LE
CONTENU SOUS LICENCE OU 5,00 $US. LES LIMITES, EXCLUSIONS ET DNIS QUI PRCDENT (Y COMPRIS LES
CLAUSES CI-DESSUS), SAPPLIQUENT DANS LA MESURE MAXIMALE PERMISE PAR LES LOIS APPLICABLES, MME SI
TOUT RECOURS NATTEINT PAS SON BUT ESSENTIEL.
moins que cela ne soit prohib par le droit local applicable, la prsente Convention est rgie par les lois de la province dOntario,
Canada. Vous consentez Chacune des parties la prsente reconnat irrvocablement la comptence des tribunaux fdraux et
provinciaux sigeant Toronto, dans de la province dOntario et consent instituer tout litige qui pourrait dcouler de la prsente
auprs des tribunaux situs dans le district judiciaire de York, province dOntario.
Au cas o vous auriez des questions concernant cette licence ou que vous dsiriez vous mettre en rapport avec Microsoft pour quelque
raison que ce soit, veuillez utiliser linformation contenue dans le Contenu Sous Licence pour contacter la filiale de succursale Microsoft
desservant votre pays, dont ladresse est fournie dans ce produit, ou visitez crivez : Microsoft sur le World Wide Web
http://www.microsoft.com

Unit 1: Connecting to Databases and Reading Data

vii

Contents
Unit 1: Connecting to Databases and Reading Data......................................1
Database Tables and Stored Procedures ..................................................................1
Web Site Projects in Visual Studio 2005.................................................................3
Shared Code Folders in ASP.NET Web Sites .........................................................5
How to Connect to a Database by Using ADO.NET...............................................6
How to Store a Connection String in an Application Configuration File ..............12
How to Encrypt Configuration Sections in ASP.NET 2.0 by Using RSA.............13
How to Retrieve a Connection String from an Application Configuration File ....16
How to Execute Simple Database Queries ............................................................17
How to Handle Connection Events........................................................................19
How to Handle Connection Exceptions.................................................................22
How to Configure Connection Pooling..................................................................29
How to Monitor Connection Pooling by Using SQL Server Profiler ....................34

Unit 2: Querying and Updating Databases by Using Commands...............35


Database Tables and Stored Procedures ................................................................35
How to Execute Query Commands .......................................................................39
Data Binding in ASP.NET 2.0...............................................................................49
How to: Retrieve Data from a Business Object in an ASP.NET 2.0 Web page ....50
How to Execute Parameterized Commands...........................................................52
How to View Database Objects by Using Server Explorer ...................................58
How to Execute Update Commands ......................................................................60
Collations...............................................................................................................65
How to Localize Dates, Times, and Numeric Values in ASP.NET 2.0.................69

Unit 3: Performing Transactional Operations...............................................73


Database Tables and Stored Procedures ................................................................73
How to Manage Local Transactions by Using ADO.NET ....................................77
How to Manage Transactions in the Data Tier ......................................................80
How to View Information About a Transaction by Using ADO.NET ..................82
How to Manage Distributed Transactions by Using ADO.NET............................84

Unit 4: Performing Disconnected Operations Programmatically ...............89


Database Tables and Stored Procedures ................................................................89
How to Create DataSets and DataTables ...............................................................95
How to Define DataTable Constraints and Relations ..........................................101
How to Create and Configure a Data Adapter .....................................................105
How to Load and Save Data in a DataSet............................................................109
How to View and Modify Data in a DataTable ...................................................117
How to Navigate Relations Between DataTables ................................................122
How to Use Row States and Row Versions.........................................................127
How to Handle DataTable Events and Errors......................................................131
How to Merge DataSet Contents .........................................................................137
How to Create and Use DataViews .....................................................................141
How to Handle DataView Events........................................................................145

viii

Unit 1: Connecting to Databases and Reading Data

Unit 5: Performing Disconnected Operations by Using Visual Studio


2005 Wizards................................................................................................. 147
Database Tables and Stored Procedures ..............................................................147
How to Add TableAdapters to a DataSet.............................................................152
How to Add Database Objects to a DataSet ........................................................156
How to Create DataRelations with the Dataset Designer ....................................158
How to Fill a Typed DataSet by Using a TableAdapter ......................................159
How to Save a Typed DataSet by Using a TableAdapter ....................................161
How to Display a Typed DataSet in a Windows Form........................................165
How to Add Code to a DataSet............................................................................169
How to Use the Data Source Configuration Wizard............................................170
How to Edit, Add, and View Queries in a TableAdapter ....................................173

Unit 6: Performing XML Operations on Disconnected Data ..................... 177


Database Tables and Stored Procedures ..............................................................177
How to Save a Dataset as XML Data ..................................................................182
How to Nest DataRelations..................................................................................184
How to Load a Dataset from XML ......................................................................188
How to Save Dataset Schema Information as an XML Schema..........................192
How to Load Dataset Schema Information from an XML Schema.....................194

Unit 7: Reading and Writing XML Data ....................................................... 197


How to Create an XmlWriter...............................................................................197
How to Write an XML Document .......................................................................200
How to Write a Message to a Microsoft Message Queue....................................205
How to Read a Message from a Microsoft Message Queue ................................210
How to Create an XmlReader..............................................................................216
How to Read an XML Document ........................................................................221
How to Send an E-Mail Message ........................................................................226

Unit 8: Processing XML Data by Using DOM ............................................. 233


How to Load an XML Document into a DOM Tree............................................233
How to Read XML Data by Using DOM ............................................................236
How to Create a Document in a DOM Tree ........................................................250
How to Modify XML Data by Using DOM ........................................................254
How to Save a DOM Tree to an XML Document ...............................................263

Index

Unit 1: Connecting to Databases and


Reading Data
Database Tables and Stored Procedures
Introduction
In the first phase of development of the Product Intranet Web site, you will use
the Production.Product table in the Adventure Works database.
This resource document describes the Production.Product table and a stored
procedure that queries data in the table.

Production.Product Table
The Production.Product table in the Adventure Works database contains
information about products sold by the Adventure Works organization. The
following table describes the schema of the Production.Product database
table.
Column name

SQL data type

Description

ProductID

int

Primary key column

Name

nvarchar(50)

Name of the product

ProductNumber

nvarchar(25)

Unique product number

MakeFlag

bit

0 = Product is purchased
1 = Product is made in-house

FinishedGoodsFlag

bit

0 = Product is not scalable


1 = Product is scalable

Color

nvarchar(15)

Product color

SafetyStockLevel

smallint

Minimum inventory quantity

ReorderPoint

smallint

Inventory level that triggers a


purchase order or work order

StandardCost

money

Standard cost of the product

ListPrice

money

Selling price

Size

nvarchar(5)

Product size

SizeUnitMeasureCode

nchar(3)

Unit of measure for size


Foreign key to the
UnitMeasureCode column in the
UnitMeasure table

Unit 1: Connecting to Databases and Reading Data


(continued)
Column name

SQL data type

Description

WeightUnitMeasureCode

nchar(3)

Unit of measure for weight


Foreign key to the
UnitMeasureCode column
in the UnitMeasure table

Weight

decimal(8, 2)

Product weight

DaysToManufacture

int

Number of days required


to manufacture the product

ProductLine

nchar(2)

R = Road
M = Mountain
T = Touring
S = Standard

Class

nchar(2)

H = High
M = Medium
L = Low

Style

nchar(2)

W = Womens
M = Mens
U = Universal

ProductSubcategoryID

int

Foreign key to the


ProductSubcategoryID
column in the
ProductSubcategory table

ProductModelID

int

Foreign key to the


ProductModelID column
in the ProductModel table

SellStartDate

datetime

Date the product was


available for sale

SellEndDate

datetime

Date the product was no


longer available for sale

DiscontinuedDate

datetime

Date the product was


discontinued

rowguid

uniqueidentifier

The ROWGUIDCOL
number that uniquely
identifies the record (for
merge replication)

ModifiedDate

datetime

Date and time the record


was last updated

You will use the following stored procedure to query data in the
Production.Product table:

uspGetProductCountWithChecks. Counts the number of products in the


Production.Product table, and raises informational errors if any products
have a list price of 0 or are past their sell-end date. This stored procedure
does not have any parameters.

Unit 1: Connecting to Databases and Reading Data

Web Site Projects in Visual Studio 2005


This document is extracted from an article that you can find at the following
location:
In the Microsoft Visual Studio 2005 documentation at

Types of Web Sites in Visual Web Developer


mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.VisualSt
udio.v80.en/dv_vwdcon/html/0d9f5c30-8448-455b-8378c6d329286689.htm

Web Site Projects in Visual Studio 2005


You can use Visual Studio to create and work with ASP.NET Web sites (Web
applications) in a variety of configurations: local IIS, file system, FTP, and
remote sites.

Local IIS Web Sites


Local IIS Web sites run using a copy of IIS that is installed on your computer.
When you create a local IIS Web site, the pages and folders for your site are
stored in a folder under the default IIS folder for Web sites (Inetpub\wwwroot).
Visual Studio also creates the appropriate IIS configuration so that the Web site
is recognized by IIS as an application.
To create a local IIS Web site, you need administrator privileges on the
computer.
Alternatively, you can create an IIS virtual directory in Visual Studio. In that
case, the pages and folders for your Web site can be in any accessible folder,
and a virtual directory in your local copy of IIS points to the file location.

File System Web Sites


In a file system Web site, you can create and edit files in any folder you like,
whether on your local computer or in a folder on another computer that you
access via network share. You do not require IIS running on your computer.
Instead, you can test pages by using the Visual Studio Web server.
The Visual Studio Web server works only locally. It cannot serve pages to
another computer and is therefore suitable for testing pages only locally.
If you create a file system Web site, you can create an IIS virtual directory later
that points to the pages in the Web site.

Unit 1: Connecting to Databases and Reading Data

FTP Web Sites


Visual Studio allows you to open and edit Web sites that are available on an
FTP server. This is a typical scenario if your Web site is located on a hosting
site.
You can connect from within Visual Studio to any FTP server on which you
have Read/Write privileges. You can then create and edit Web pages on that
server. If the FTP server is configured with ASP.NET and an IIS virtual root
that points to the FTP directory, you can also run your pages from the server to
test them.

Remote Web Sites


A remote Web site is a site that uses IIS but is on another computer that you can
access over a local area network. The remote computer must have IIS installed
and be configured with Microsoft FrontPage 2002 Server Extensions from
Microsoft. When you create a remote Web site, the pages and folders for your
site are stored under the default IIS folder on the remote computer. When you
run the pages, they are served using IIS on the remote computer.
This is the model used in Visual Studio .NET 2002 and Visual Studio .NET
2003.

Unit 1: Connecting to Databases and Reading Data

Shared Code Folders in ASP.NET Web Sites


This document is extracted from an article that you can find at the following
location:
In the Microsoft Visual Studio 2005 documentation at

Shared Code Folders in Web Sites


mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.VisualSt
udio.v80.en/dv_aspnetcon/html/40130404-8509-4efa-b4db36ebaaba5b62.htm

Shared Code Folders in ASP.NET Web Sites


If your Web application includes code that you want to share between pages,
you can keep the code in one of two special folders underneath the root of your
Web application, the Bin folder and the App_Code folder.

Bin Folder
You can store compiled assemblies in the Bin folder, and other code (such as
code for pages) anywhere in the Web application automatically references it. A
typical example is that you have the compiled code for a custom class. You can
copy the compiled assembly to the Bin folder of your Web application and the
class is then available to all pages.
Assemblies in the Bin folder do not need to be registered. The presence of a .dll
file in the Bin folder is sufficient for ASP.NET to recognize it. If you change
the .dll and write a new version of it to the Bin folder, ASP.NET detects the
update and uses the new version of the .dll for new page requests from then on.

App_Code Folder
You can store source code in the App_Code folder, and it will be automatically
compiled at run time. The resulting assembly is accessible to any other code in
the Web application. The App_Code folder therefore works much like the Bin
folder, except that you can store source code in it instead of compiled code. The
App_Code folder and its special status in an ASP.NET Web application makes
it possible to create custom classes and other source-code-only files and use
them in your Web application without having to compile them independently.
The App_Code folder can contain source code files written as traditional class
files, that is, files with a .vb extension, .cs extension, and so on. However, it can
also include files that are not explicitly in a specific programming language.
Examples include .wsdl (Web service discovery language) files and XML
schema (.xsd) files. ASP.NET can compile these files into assemblies.
The App_Code folder can contain as many files and subfolders as you need.
You can organize your source code in any way that you find convenient, and
ASP.NET will still compile all of the code into a single assembly that is
accessible to other code anywhere in the Web application.

Unit 1: Connecting to Databases and Reading Data

How to Connect to a Database by Using ADO.NET


This document is adapted from articles that you can find at the following
locations:
In the Microsoft Visual Studio 2005 documentation at

Connecting to SQL Server Using ADO.NET


mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.VisualSt
udio.v80.en/WD_ADONET/html/eb9dcedc-fe0a-48c5-abdb2739fe04b9c5.htm

Connecting to an OLE DB Data Source Using ADO.NET


mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.VisualSt
udio.v80.en/WD_ADONET/html/7afe0cb6-6f38-450a-a3372f1caabee86d.htm

Connecting to an ODBC Data Source Using ADO.NET


mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.VisualSt
udio.v80.en/WD_ADONET/html/57836e79-ef63-4922-afa87216c7338e1b.htm

Connecting to an Oracle Data Source Using ADO.NET


mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.VisualSt
udio.v80.en/WD_ADONET/html/b54889c7-4a2b-4b5a-81b6d784ef769ed8.htm

How to: Define a Connection String


To connect to a database by using Microsoft ADO.NET, you must provide a
connection string to identify the database. The values that you include in a
connection string depend on which Microsoft .NET Framework data provider
you use. The following table describes several common parameters of
connection strings. The table contains only a partial list of values, and not all
data providers support all of these parameters.
Parameter

Description

Provider

Use this parameter to set or return the name of the OLE


DB provider for the connection (.NET Framework Data
Provider for OLE DB only).

Connection Timeout or
Connect Timeout

The length of time in seconds to wait for a connection to


the server before the data source terminates the attempt
and returns an error. The default timeout is 15 seconds.

Initial Catalog or Database

The name of the database.

Data Source

The name or network address of the data source


instance.

Unit 1: Connecting to Databases and Reading Data

(continued)
Parameter

Description

Integrated Security or
Trusted_Connection

If this parameter is false, you must specify


the User ID and Password in the
connection string. If true, the data source
uses the current Microsoft Windows
account credentials for authentication. The
default value is false.
Recognized values are true, false, yes, no,
and sspi (strongly recommended), which
is equivalent to true.

User ID

The data source login account to use if


you are not using integrated security.

Password

The data source login account password to


use if you are not using integrated
security.

Persist Security Info

If this parameter is false, the data source


does not return security-sensitive
information, such as the password, if the
connection is open or has ever been in an
open state. The default value is false.

How to: Connect to SQL Server by Using ADO.NET


The .NET Framework Data Provider for Microsoft SQL Server provides
connectivity to SQL Server version 7.0 or later. To connect to SQL Server by
using the .NET Framework Data Provider for SQL Server, use the
SqlConnection class from the System.Data.SqlClient namespace.

To connect to SQL Server by using ADO.NET


1. Define a connection string that identifies the database to which you want to
connect.
2. Create a SqlConnection object. Pass the connection string as an argument
into the SqlConnection constructor.
3. Call the Open method on the SqlConnection object.
4. Use the database connection in your application.
5. Call the Close method or the Dispose method on the SqlConnection object.
You can use a using statement (a Using statement in Microsoft Visual
Basic) to ensure that the SqlConnection object is disposed even if an
exception is thrown while using the connection.

Unit 1: Connecting to Databases and Reading Data

The following examples show how to use a SqlConnection object to connect to


the Adventure Works database on the local instance of SQL Server 2005 by
using Windows authentication. The code ensures that the connection does not
return security-sensitive information.
[Visual Basic]
Dim connectionString As String = _
"Data Source=(local); Initial Catalog=Adventureworks; " & _
"Integrated Security=SSPI; Persist Security Info=False"
Dim connection as New SqlConnection(connectionString)
connection.Open()
Using connection
' Use the database connection.
End Using
[C#]
string connectionString =
"Data Source=(local); Initial Catalog=Adventureworks; " +
"Integrated Security=SSPI; Persist Security Info=False";
SqlConnection connection = new SqlConnection(connectionString);
connection.Open();
using (connection)
{
// Use the database connection.
}

How to: Connect to an OLE DB Data Source by Using ADO.NET


The .NET Framework Data Provider for OLE DB provides connectivity to data
sources through an appropriate OLE DB provider and to SQL Server version
6.x or earlier through the OLE DB Provider for SQL Server.
To connect to an OLE DB data source by using the .NET Framework Data
Provider for OLE DB, use the OleDbConnection class from the
System.Data.OleDb namespace.

To connect to an OLE DB data source by using ADO.NET


1. Define a connection string that identifies the database to which you want to
connect. The connection string must include a Provider parameter to
specify which OLE DB provider to use to establish the connection.
2. Create an OleDbConnection object. Pass the connection string as an
argument into the OleDbConnection constructor.
3. Call the Open method on the OleDbConnection object.
4. Use the database connection in your application.
5. Call the Close method or the Dispose method on the OleDbConnection
object. You can use a using statement to ensure that the OleDbConnection
object is disposed.

Unit 1: Connecting to Databases and Reading Data

The following examples show how to use an OleDbConnection object to


connect to the Northwind database on the local instance of SQL Server 6.5. The
following examples use Windows authentication and ensure that the connection
does not return security-sensitive information.
[Visual Basic]
Dim connectionString As String = _
"Provider=SQLOLEDB; Data Source=(local); Initial Catalog=Northwind; " & _
"Integrated Security=SSPI; Persist Security Info=False"
Dim connection as New OleDbConnection(connectionString)
connection.Open()
Using connection
' Use the database connection.
End Using
[C#]
string connectionString =
"Provider=SQLOLEDB; Data Source=(local); Initial Catalog=Northwind; " +
"Integrated Security=SSPI; Persist Security Info=False";
OleDbConnection connection = new OleDbConnection(connectionString);
connection.Open();
using (connection)
// Use the database connection. }

How to: Connect to an ODBC Data Source by Using ADO.NET


The .NET Framework Data Provider for ODBC provides connectivity to data
sources through an appropriate ODBC driver.
To connect to an ODBC data source by using the .NET Framework Data
Provider for ODBC, use the OdbcConnection class from the
System.Data.Odbc namespace.

To connect to an ODBC data source by using ADO.NET


1. Define a connection string that identifies the database to which you want to
connect. The connection string can include a Driver parameter to specify
the database driver and data source name. Alternatively, the connection
string can include a DSN parameter to specify the ODBC Data Source
Name (DSN) of the data source.
2. Create an OdbcConnection object. Pass the connection string as an
argument into the OdbcConnection constructor.
3. Call the Open method on the OdbcConnection object.
4. Use the database connection in your application.
5. Call the Close method or the Dispose method on the OdbcConnection
object. You can use a using statement to ensure that the OdbcConnection
object is disposed.

10

Unit 1: Connecting to Databases and Reading Data

The following examples show how to use an OdbcConnection object to


connect to the Northwind Microsoft Office Access database that is located in
the C:\mydb folder. The examples use Windows authentication.
[Visual Basic]
Dim connectionString As String = _
"Driver={Microsoft Access Driver (*.mdb)}; DBQ=c:\mydb\nwind.mdb; " & _
"Trusted_Connection=yes"
Dim connection as New OdbcConnection(connectionString)
connection.Open()
Using connection
' Use the database connection.
End Using
[C#]
string connectionString =
"Driver={Microsoft Access Driver (*.mdb)}; DBQ=c:\\mydb\\nwind.mdb; " +
"Trusted_Connection=yes";
OdbcConnection connection = new OdbcConnection(connectionString);
connection.Open();
using (connection)
{
// Use the database connection.
}

How to: Connect to an Oracle Data Source by Using ADO.NET


The .NET Framework Data Provider for Oracle provides connectivity to data
sources through an appropriate ODBC driver.
To connect to an Oracle data source by using the .NET Framework Data
Provider for Oracle, use the OracleConnection class from the
System.Data.OracleClient namespace.

To connect to an Oracle data source by using ADO.NET


1. Define a connection string that identifies the database to which you want to
connect.
2. Create an OracleConnection object. Pass the connection string as an
argument into the OracleConnection constructor.
3. Call the Open method on the OracleConnection object.
4. Use the database connection in your application.
5. Call the Close method or the Dispose method on the OracleConnection
object. You can use a using statement to ensure that the OracleConnection
object is disposed.

Unit 1: Connecting to Databases and Reading Data

The following examples show how to use an OracleConnection object to


connect to an Oracle data source named MyOracleServer. The examples use
Windows authentication.
[Visual Basic]
Dim connectionString As String = _
"Data Source=MyOracleServer;Integrated Security=yes"
Dim connection as New OracleConnection(connectionString)
connection.Open()
Using connection
' Use the database connection.
End Using
[C#]
string connectionString =
" Data Source=MyOracleServer;Integrated Security=yes";
OracleConnection connection = new OracleConnection(connectionString);
connection.Open();
using (connection)
{
// Use the database connection.
}

11

12

Unit 1: Connecting to Databases and Reading Data

How to Store a Connection String in an Application


Configuration File
How to: Store a Connection String in an Application Configuration
File
You can either store connection strings in an application configuration file, or
you can hard-code them directly in your application. If you store connection
strings in the configuration file, you can modify them easily in the future,
without having to edit the source code and recompile the application.

To store a connection string in an application configuration file


1. Open the Web.config configuration file.
2. Inside the <configuration> root element, add an element named
<connectionStrings>.
3. Inside the <connectionStrings> element, add a nested element named
<add> for each connection string that you want to define in the
configuration file.
4. For each <add> element, define a name attribute to specify a programmatic
name for the connection string. Also, define a connectionString attribute to
specify connection string information.
The following example shows how to define a connection string for the
Adventure Works database on the local instance of Microsoft SQL Server
2005. The example uses Microsoft Windows authentication and ensures that
the connection does not return security-sensitive information.
<connectionStrings>
<add
name="AdventureWorks"
connectionString="Data Source=(local); Initial Catalog=AdventureWorks;
Integrated Security=SSPI; Persist Security Info=False" />
</connectionStrings>

Unit 1: Connecting to Databases and Reading Data

13

How to Encrypt Configuration Sections in ASP.NET 2.0 by


Using RSA
Overview
Configuration files such as Web.config are often used to hold sensitive
information, including user names, passwords, database connection strings, and
encryption keys.
The following table describes the configuration sections that contain sensitive
information, and which you typically need to encrypt.
Configuration section

Description

<appSettings>

Contains custom application settings

<connectionStrings>

Contains connection strings

<identity>

Contains impersonation credentials

<sessionState>

Contains the connection string for the out-of-process


session state provider

Encrypting and decrypting data incur performance overhead. To keep this


overhead to a minimum, encrypt only those sections of your configuration file
that store sensitive data.
The Microsoft .NET Framework versions 1.0 and 1.1 had limited support for
configuration file encryption. However, .NET Framework version 2.0
introduces a Protected Configuration feature that you can use to encrypt
sensitive configuration file data by using a command line tool. The following
two Protected Configuration providers are provided in the .NET Framework
version 2.0, although you can also implement custom providers:

RSAProtectedConfigurationProvider. This is the default provider and


uses the RSA public key encryption to encrypt and decrypt data.

DPAPIProtectedConfigurationProvider. This provider uses the Microsoft


Windows Data Protection API (DPAPI) to encrypt and decrypt data.

Microsoft ASP.NET automatically decrypts configuration sections when


processing them; therefore, you do not need to write any additional decryption
code.

14

Unit 1: Connecting to Databases and Reading Data

How to: Encrypt a Connection String in Web.Config by Using RSA


RSAProtectedConfigurationProvider is the default encryption provider, and
it supports machine-level and user-level key containers for key storage. The
Microsoft Windows Server operating system makes machine-level key
containers available to all users, but a user-level key container is available to
that user only.
The choice of container depends largely on whether your application shares a
server with other applications and whether sensitive data must be kept private
for each application.
Use a machine-level key container in the following situations:

Your application runs on its own dedicated server with no other


applications.

You have multiple applications on the same server and you want those
applications to be able to share sensitive information and the same
encryption key.

Use a user-level key container if you run your application in a shared hosting
environment and you want to make sure that your application's sensitive data is
not accessible to other applications on the server. In this situation, each
application should have a separate identity and the resources for the application
such as files and databasesshould be restricted to that identity.

To encrypt a connection string in Web.config by using RSA with a


machine-level key container

1. Create a Web site project in Microsoft Visual Studio 2005.


2. Add a Web.config configuration file to the project.
3. Add a sample connection string, similar to the following example:
<connectionStrings>
<add
name="MyLocalSQLServer"
connectionString="Initial Catalog=AdventureWorks; Data source=(local);
Integrated Security=SSPI;"
providerName="System.Data.SqlClient"/>
</connectionStrings>

4. Run the following command from an SDK Command Prompt to encrypt the
connectionStrings section for the Web.config file that is located in the
specified Web site folder:
aspnet_regiis

-pef

"connectionStrings"

"Web-site-folder "

Unit 1: Connecting to Databases and Reading Data

15

5. Review the Web.config file, and examine the changes. Your modified
Web.config file, with the connectionStrings section encrypted, should be
similar to the following example:
...
<protectedDataSections>
<add name="connectionStrings" provider="RsaProtectedConfigurationProvider"
inheritedByChildren="false" />
</protectedDataSections>
...
<connectionStrings>
<EncryptedData Type="http://www.w3.org/2001/04/xmlenc#Element"
xmlns="http://www.w3.org/2001/04/xmlenc#">
<EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#tripledes-cbc" />
<KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
<EncryptedKey Recipient="" xmlns="http://www.w3.org/2001/04/xmlenc#">
<EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-1_5" />
<KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
<KeyName>Rsa Key</KeyName>
</KeyInfo>
<CipherData>
<CipherValue>cbJNxB4OmxwzGVVn2R7b1+YsRtlik95RhsTnA6zMjkW/ApBl4q/3+HrE3NRykcoyVgFsGK
O2aRLkyQvnzEt2nwptwGsonDNOhbrNLa4wGDXXq5YNnEUQmhAdPlaQQt8HAQ0/hhQOgjUib6SnlWLzlH5Zl
pBGiuy4zr6EahbBztA=</CipherValue>
</CipherData>
</EncryptedKey>
</KeyInfo>
<CipherData>
<CipherValue>WkEoLFnva9rjH4faZu50eyJHV3a7+7mj3JnO9mUMaaZX78dv1N/Q3TJ092ZGZ9HKJtomU6
dhL9K5P6LHMvbrILDsB/4vdoaHeepAoKkHc5d3Nva27mltxvq+IT0KaAtj3O6EGsrllUWX4rBeq18w6eyQq
ZqW3eHM1HJq6PlcA9K2y3HenrY06BoKzosOHo9OPUpHK2kNoOxXg1XvP0AuBFAj7UUjXT4QeoGIi15T3JT/
YerHJ/mhZw15dHBfbTN36d60yBdHRTBKrvQQ78H9zzT15lfmJTrX</CipherValue>
</CipherData>
</EncryptedData>
</connectionStrings>
...

6. To revert the connectionStrings section to clear text, run the following


command from the command prompt:
aspnet_regiis

-pdf

"connectionStrings"

"Web-site-folder "

16

Unit 1: Connecting to Databases and Reading Data

How to Retrieve a Connection String from an Application


Configuration File
This document is adapted from an article that you can find at the following
location:
In the Microsoft Visual Studio 2005 documentation at

ConfigurationManager Class
mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.NETDE
VFX.v20.en/cpref/html/T_System_Configuration_ConfigurationManager.ht
m

How to: Retrieve a Connection String from an Application


Configuration File
Each connection string that is stored in an application configuration file has its
own assigned name. In an application, you can access a connection string by its
programmatic name.

To retrieve a connection string from an application configuration file


1. Import the System.Configuration namespace.
2. Use the ConfigurationManager.ConnectionStrings property to get a
collection of connection strings from the application configuration file.
3. Index into the collection of connection strings by using the programmatic
name of the connection string you want to access.
4. Use the ConnectionString property to get the connection string
information.
The following example shows how to retrieve a connection string named
AdventureWorks from the application configuration file.
[Visual Basic]
Imports System.Configuration
Dim connectionString as String = _
ConfigurationManager.ConnectionStrings("AdventureWorks").ConnectionString
[C#]
using System.Configuration;
string connectionString =
ConfigurationManager.ConnectionStrings["AdventureWorks"].ConnectionString;

Unit 1: Connecting to Databases and Reading Data

17

How to Execute Simple Database Queries


How to: Execute a Simple Database Query
The SqlCommand class in the Microsoft .NET Framework Data Provider for
Microsoft SQL Server has four methods that you can use to execute SQL
statements:

ExecuteScalar. Executes a query that returns a single scalar value.

ExecuteReader. Executes a query that returns a result set.

ExecuteNonQuery. Executes a data update statement or a catalog update


statement.

ExecuteXmlReader. Executes a query that returns an Extensible Markup


Language (XML) result set.

The .NET Framework Data Providers for OLE DB, ODBC, and Oracle provide
command classes similar to the SqlCommand class for .NET Framework Data
Providers for SQL Server:

OleDbCommand. OLE DB command class

OdbcCommand. ODBC command class

OracleCommand. Oracle command class

Each of these command classes supports the ExecuteScalar, ExecuteReader,


and ExecuteNonQuery methods as described for the .NET Framework Data
Provider for SQL Server. However, the ExecuteXmlReader method is only
available in the SqlCommand class; the OleDbCommand, OdbcCommand,
and OracleCommand classes do not support the ExecuteXmlReader method.

To execute a simple database query


1. Create a connection object, such as a SqlConnection object.
2. Create a command object, such as a SqlCommand object. In the
constructor, specify either the SQL statement that you want to execute or the
name of the stored procedure that you want to call. Also, specify the
connection object that you created in step 1.
3. If you want to execute an SQL statement, set the CommandType property
of the command object to the CommandType.Text enumeration value. If
you want to call a stored procedure, set the CommandType property of the
command object to the CommandType.StoredProcedure enumeration
value.
4. Call the Open method on the connection object.
5. Call the ExecuteScalar method on the command object. Assign the result to
a suitably typed variable.
6. Call the Close method on the connection object.

18

Unit 1: Connecting to Databases and Reading Data

The following examples show how to execute a query to determine the number
of products in the Adventure Works database on the local SQL Server 2005
instance.
[Visual Basic]
Dim connection as New SqlConnection(aConnectionString)
Dim command as New SqlCommand( _
"SELECT COUNT(*) FROM Production.Product", _
connection)
command.CommandType = CommandType.Text
connection.Open()
Dim count as Integer = CInt(command.ExecuteScalar())
connection.Close()
[C#]
SqlConnection connection = new SqlConnection(aConnectionString);
SqlCommand command = new SqlCommand(
"SELECT COUNT(*) FROM Production.Product",
connection);
command.CommandType = CommandType.Text;
connection.Open();
int count = (int)command.ExecuteScalar();
connection.Close();

Unit 1: Connecting to Databases and Reading Data

19

How to Handle Connection Events


This document is adapted from an article that you can find at the following
location:
In the Microsoft Visual Studio 2005 documentation at

Working with Connection Events


mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.VisualSt
udio.v80.en/WD_ADONET/html/5a29de74-acfc-4134-8616829dd7ce0710.htm

How to: Handle Connection Events


ADO.NET connection objects have two events that you can use to retrieve
informational messages from a data source or to determine if the state of a
connection has changed. The following table lists the events of the Connection
object.
Event

Description

InfoMessage

Occurs when a data source returns an informational


message. Informational messages are messages from a
data source that do not result in an exception being
thrown.

StateChange

Occurs when a connection changes from the closed state


to the open state or from the open state to the closed
state.

To handle InfoMessage events


If an error occurs at the data source, the data provider throws an exception.
However, if the data source returns an informational message, the data provider
raises an InfoMessage event instead. In the case of Microsoft SQL Server,
any message that has a severity of 10 or less is informational, and you can
capture such messages by handling the InfoMessage event.
The InfoMessage event receives an InfoMessageEventArgs parameter. The
InfoMessageEventArgs object has an Errors property that contains a
collection of messages from the data source. You can query the Error objects
in this collection for the error number and message text, as well as the source of
the error. The .NET Framework Data Provider for SQL Server also includes
details about the database, stored procedure, and line number that the message
came from.
The following examples show how to add an event handler for the InfoMessage
event.

20

Unit 1: Connecting to Databases and Reading Data

[Visual Basic]
Dim aConnection As New SqlConnection(aConnectionString)
AddHandler aConnection.InfoMessage, AddressOf OnInfoMessage

Public Shared Sub OnInfoMessage(sender As Object, args As SqlInfoMessageEventArgs)


For Each err As SqlError In args.Errors
Console.WriteLine("The {0} has received a severity {1}, " & _
"state {2}, error number {3}, " & _
"line number {4} of procedure {5} on server {6}\n" & _
"message: {7}", _
err.Source, _
err.Class, _
err.State, _
err.Number, _
err.LineNumber, _
err.Procedure, _
err.Server, _
err.Message)
Next
End Sub
[C#]
SqlConnection aConnection = new SqlConnection(aConnectionString);
aConnection.InfoMessage += OnInfoMessage;

public static void OnInfoMessage(object sender, SqlInfoMessageEventArgs args)


{
foreach (SqlError err in args.Errors)
{
Console.WriteLine("The {0} has received a severity {1}, " +
"state {2}, error number {3}, " +
"line number {4} of procedure {5} on server {6}\n" +
"message: {7}",
err.Source,
err.Class,
err.State,
err.Number,
err.LineNumber,
err.Procedure,
err.Server,
err.Message);
}
}

Unit 1: Connecting to Databases and Reading Data

To handle StateChange events


The StateChange event receives a StateChangeEventArgs parameter. The
StateChangeEventArgs object has two properties that you can use to
determine the change in state of the connection:

The OriginalState property is a ConnectionState enumeration that


indicates the state of the connection before it changed.

The CurrentState property is a ConnectionState enumeration that


indicates the state of the connection after it changed.

The following examples show how to add an event handler for the
StateChange event.
[Visual Basic]
Dim aConnection As New SqlConnection(aConnectionString)
AddHandler aConnection.StateChange, AddressOf OnStateChange

Public Shared Sub OnStateChange(sender As Object, args As StateChangeEventArgs)


Console.WriteLine( _
"The current Connection state has changed from {0} to {1}.", _
args.OriginalState, _
args.CurrentState)
End Sub
[C#]
SqlConnection aConnection = new SqlConnection(aConnectionString);
aConnection.StateChange += OnStateChange;

public static void OnStateChange(object sender, StateChangeEventArgs args)


{
Console.WriteLine(
"The current Connection state has changed from {0} to {1}.",
args.OriginalState,
args.CurrentState);
}

21

22

Unit 1: Connecting to Databases and Reading Data

How to Handle Connection Exceptions


This document is adapted from articles that you can find at the following
locations:
In the Microsoft Visual Studio 2005 documentation at

SqlException Class
mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.NETDE
VFX.v20.en/cpref/html/T_System_Data_SqlClient_SqlException.htm

OleDbException Class
mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.NETDE
VFX.v20.en/cpref/html/T_System_Data_OleDb_OleDbException.htm

OdbcException Class
mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.NETDE
VFX.v20.en/cpref/html/T_System_Data_Odbc_OdbcException.htm

OracleException Class
mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.NETDE
VFX.v20.en/cpref/html/T_System_Data_OracleClient_OracleException.ht
m

How to: Handle Connection Exceptions for the .NET Framework


Data Provider for SQL Server
The Microsoft .NET Framework Data Provider for Microsoft SQL Server
throws a SqlException when it encounters an error generated from the server.
SqlException objects always contain at least one instance of the SqlError
class.
The severity level indicates the origin of the error:
Severity

Description

Action

1-10

Informational, indicates problems


caused by mistakes in information
entered by the user.

The connection remains


open so you can continue
working.

11-16

Indicates an error that is generated by


the user.

You can correct these


errors.

17-19

Software or hardware errors.

You can continue working,


but you might not be able
to execute a particular
statement. The connection
remains open.

20-25

Software or hardware errors.

The server closes the


connection.

Unit 1: Connecting to Databases and Reading Data

23

The following examples show how to handle a SqlException and display error
information on the console.
[Visual Basic]
Try
' Data-related code.
Catch ex As SqlException
Console.WriteLine("Exception severity {0}", ex.Class)
Console.WriteLine("Message: {0}", ex.Message)
DisplaySqlErrors(ex.Errors)
End Try

Public Shared Sub DisplaySqlErrors(ByVal errors As SqlErrorCollection)


For Each err As SqlError In errors
Console.WriteLine("The {0} has received a severity {1}, " & _
"state {2}, error number {3}, " & _
"line number {4} of procedure {5} on server {6}\n" & _
"message: {7}", _
err.Source, _
err.Class, _
err.State, _
err.Number, _
err.LineNumber, _
err.Procedure, _
err.Server, _
err.Message)
Next
End Sub

24

Unit 1: Connecting to Databases and Reading Data

[C#]
try
{
// Data-related code.
}
catch (SqlException ex)
{
Console.WriteLine("Exception severity {0}", ex.Class);
Console.WriteLine("Message: {0}", ex.Message);
DisplaySqlErrors(ex.Errors);
}

public static void DisplaySqlErrors(SqlErrorCollection errors)


{
foreach (SqlError err in errors)
{
Console.WriteLine("The {0} has received a severity {1}, " +
"state {2}, error number {3}, " +
"line number {4} of procedure {5} on server {6}\n" +
"message: {7}",
err.Source,
err.Class,
err.State,
err.Number,
err.LineNumber,
err.Procedure,
err.Server,
err.Message);
}
}

How to: Handle Connection Exceptions for the .NET Framework


Data Provider for OLE DB
The .NET Framework Data Provider for OLE DB throws an OleDbException
when it encounters an error generated from the server. OleDbException objects
have an ErrorCode property that indicates the HRESULT of the error and an
Errors property that contains at least one instance of the OleDbError class.
The following examples show how to handle an OleDbException and display
error information on the console.

Unit 1: Connecting to Databases and Reading Data


[Visual Basic]
Try
' Data-related code.
Catch ex As OleDbException
Console.WriteLine("Error code {0}", ex.ErrorCode)
DisplayOleDbErrors(ex.Errors)
End Try

Public Shared Sub DisplayOleDbErrors(ByVal errors As OleDbErrorCollection)


For Each err As OleDbError In errors
Console.WriteLine("Source {0}, " & _
"native error number {1}, " & _
"ANSI SQL standard error code {2}\n" & _
"message: {3}", _
err.Source, _
err.NativeError, _
err.SQLState, _
err.Message)
Next
End Sub
[C#]
try
{
// Data-related code.
}
catch (OleDbException ex)
{
Console.WriteLine("Error code {0}", ex.ErrorCode);
DisplayOleDbErrors(ex.Errors);
}

public static void DisplayOleDbErrors(OleDbErrorCollection errors)


{
foreach (OleDbError err in errors)
{
Console.WriteLine("Source {0}, " +
"native error number {1}, " +
"ANSI SQL standard error code {2}\n" +
"message: {3}",
err.Source,
err.NativeError,
err.SQLState,
err.Message);
}
}

25

26

Unit 1: Connecting to Databases and Reading Data

How to: Handle Connection Exceptions for the .NET Framework


Data Provider for ODBC
The .NET Framework Data Provider for ODBC throws an OdbcException
when it encounters an error generated by an ODBC data source.
OdbcException objects have a Source property that indicates the name of the
ODBC driver that generated the error and an Errors property that contains at
least one instance of the OdbcError class.
The following examples show how to handle an OdbcException and display
error information on the console.
[Visual Basic]
Try
' Data-related code.
Catch ex As OdbcException
Console.WriteLine("ODBC driver {0}", ex.Source)
DisplayOdbcErrors(ex.Errors)
End Try

Public Shared Sub DisplayOdbcErrors(ByVal errors As OdbcErrorCollection)


For Each err As OdbcError In errors
Console.WriteLine("Source {0}, " & _
"native error number {1}, " & _
"ANSI SQL standard error code {2}\n" & _
"message: {3}", _
err.Source, _
err.NativeError, _
err.SQLState, _
err.Message)
Next
End Sub

Unit 1: Connecting to Databases and Reading Data


[C#]
try
{
// Data-related code.
}
catch (OdbcException ex)
{
Console.WriteLine("ODBC driver {0}", ex.Source);
DisplayOdbcErrors(ex.Errors);
}

public static void DisplayOdbcErrors(OdbcErrorCollection errors)


{
foreach (OdbcError err in errors)
{
Console.WriteLine("Source {0}, " +
"native error number {1}, " +
"ANSI SQL standard error code {2}\n" +
"message: {3}",
err.Source,
err.NativeError,
err.SQLState,
err.Message);
}
}

27

28

Unit 1: Connecting to Databases and Reading Data

How to: Handle Connection Exceptions for the .NET Framework


Data Provider for Oracle
The .NET Framework Data Provider for Oracle throws an OracleException
when it encounters an error or warning generated by an Oracle database.
OracleException objects have a Code property that gets the code portion of the
error as an integer and a Message property that gets a description of the error.
The following examples show how to handle an OracleException and display
error information on the console.
[Visual Basic]
Try
' Data-related code.
Catch ex As OracleException
Console.WriteLine("Code {0}", ex.Code)
Console.WriteLine("Message: {0}", ex.Message)
End Try
[C#]
try
{
// Data-related code.
}
catch (OracleException ex)
{
Console.WriteLine("Code {0}", ex.Code);
Console.WriteLine("Message: {0}", ex.Message);
}

Unit 1: Connecting to Databases and Reading Data

29

How to Configure Connection Pooling


This document is adapted from articles that you can find at the following
locations:
In the Microsoft Visual Studio 2005 documentation at

Connection Pooling for the .NET Framework Data Provider for SQL Server
mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.VisualSt
udio.v80.en/WD_ADONET/html/7e51d44e-7c4e-4040-9332f0190fe36f07.htm

Connection Pooling for the .NET Framework Data Provider for OLE DB
mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.VisualSt
udio.v80.en/WD_ADONET/html/f971988f-c575-4319-bc939787a9d32795.htm

Connection Pooling for the .NET Framework Data Provider for ODBC
mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.VisualSt
udio.v80.en/WD_ADONET/html/75adf206-3379-4c52-85f79054dc396730.htm

Connection Pooling for the .NET Framework Data Provider for Oracle
mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.VisualSt
udio.v80.en/WD_ADONET/html/55ef5714-edd6-4767-81fbb9cf0c831169.htm

How to: Configure Connection Pooling for the .NET Framework Data
Provider for SQL Server
The Microsoft .NET Framework Data Provider for Microsoft SQL Server
provides connection pooling automatically for your Microsoft ADO.NET client
application. You can also supply several connection string modifiers to control
connection pooling behavior.
When an application opens a connection, the connection pooler creates a
connection pool based on an exact matching algorithm that associates the pool
with the connection string in the connection. The connection pooler associates
each connection pool with a distinct connection string and security context.
When an application opens a new connection, if the connection string is not an
exact match to an existing pool, the connection pooler creates a new pool. The
connection pooler creates multiple connection objects and adds them to the new
pool so that the minimum pool size requirement is satisfied.

30

Unit 1: Connecting to Databases and Reading Data

When an application requests a SqlConnection object, the connection pooler


obtains the connection 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 connection pooler queues the request. The
connection pooler satisfies these requests by reallocating connections when an
application calls Close or Dispose on a SqlConnection, to release the
connection back into the pool. If the request cannot be satisfied prior to the
connection timeout (15 seconds default or as specified in the connection string),
an exception will be thrown.
The connection pooler removes a connection from the pool after it has been idle
for an extended period or if the pooler detects that the connection with the
server has been severed. If the pooler detects a connection that is no longer
connected to the server, the pooler marks the connection as invalid. The pooler
periodically scans connection pools for objects that have been released to the
pool and are marked as invalid. The pooler permanently removes these
connections from the pool.
The ConnectionString property of the SqlConnection object supports
connection string key/value pairs that can be used to adjust the behavior of the
connection pooling logic. The following table describes the ConnectionString
values you can use to adjust connection pooling behavior.
Property name

Default

Description

Connection Lifetime

When the connection pooler returns a


connection to the pool, its creation time
is compared with the current time, and
the connection is destroyed if that time
span (in seconds) exceeds the value
specified by Connection Lifetime.
This is useful in clustered
configurations to force load balancing
between a running server and a server
just brought online.

Connection Reset

true

Determines whether the database


connection is reset when being
removed from the pool. For Microsoft
SQL Server version 7.0, setting to false
avoids making an additional server
round trip when obtaining a connection,
but you must be aware that the
connection state, such as database
context, is not being reset.

Enlist

true

When this value is set to true, the


connection pooler automatically enlists
the connection in the current
transaction context of the creation
thread if a transaction context exists.

Max Pool Size

100

The maximum number of connections


allowed in the pool.

Min Pool Size

The minimum number of connections


maintained in the pool.

Unit 1: Connecting to Databases and Reading Data

31

(continued)
Property name

Default

Description

Pooling

true

When this value is set to


true, the connection pooler
draws the connection from
the appropriate pool, or if
necessary, creates and adds
it to the appropriate pool.

How to: Configure Connection Pooling for the .NET Framework Data
Provider for OLE DB
The .NET Framework Data Provider for OLE DB automatically pools
connections by using OLE DB session pooling. You can use the
ConnectionString property of the OleDbConnection object to enable or
disable connection pooling. For example, the following connection string
disables OLE DB session pooling and automatic transaction enlistment.
Provider=SQLOLEDB; OLE DB Services=-4; Data Source=localhost; Integrated
Security=SSPI;

For more information about OLE DB session pooling or resource pooling, as


well as how to disable pooling by overriding OLE DB provider service defaults,
see the OLE DB Programmer's Reference on the MSDN Library Web site.

How to: Configure Connection Pooling for the .NET Framework Data
Provider for ODBC
The ODBC Driver Manager manages connection pooling for the .NET
Framework Data Provider for ODBC. To enable or disable connection pooling,
use the ODBC Data Source Administrator in Control Panel or the
Administrative Tools folder. On the Connection Pooling tab, specify
connection pooling parameters for each ODBC driver installed. If you configure
connection pooling for a specific ODBC driver, all applications that use that
ODBC driver are affected.
For more information about ODBC connection pooling, see the ODBC
Programmers Reference on the MSDN Library Web site.

How to: Configure Connection Pooling for the .NET Framework Data
Provider for Oracle
The .NET Framework Data Provider for Oracle provides connection pooling
automatically for your ADO.NET client application. You can also supply
several connection string modifiers to control connection pooling behavior.
When an application opens a connection, the connection pooler creates a
connection pool based on an exact matching algorithm that associates the pool
with the connection string in the connection. The connection pooler associates
each connection pool with a distinct connection string. The connection pooler
creates multiple connection objects and adds them to the new pool, so that the
minimum pool size requirement is satisfied.

32

Unit 1: Connecting to Databases and Reading Data

When an application requests an OracleConnection object, the connection


pooler obtains the connection 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 connection pooler queues the request. The
connection pooler satisfies these requests by reallocating connections when an
application calls Close or Dispose on OracleConnection to release the
connection back into the pool.
The connection pooler removes a connection from the pool after it has been idle
for an extended period or if the pooler detects that the connection with the
server has been severed. If the pooler detects a connection that is no longer
connected to the server, the pooler marks the connection as invalid. The pooler
periodically scans connection pools for objects that have been released to the
pool and are marked as invalid. The pooler permanently removes these
connections from the pool.
The ConnectionString property of the OracleConnection object supports
connection string key/value pairs that can be used to adjust the behavior of the
connection pooling logic. The following table describes the ConnectionString
values you can use to adjust connection pooling behavior.
Property name

Default

Description

Connection Lifetime

When the connection


pooler returns a connection
to the pool, its creation
time is compared with the
current time, and the
connection is destroyed if
that time span (in seconds)
exceeds the value specified
by Connection Lifetime.

Connection Reset

true

Determines whether the


database connection is
reset when being removed
from the pool. For SQL
Server 7.0, setting to false
avoids making an
additional server round trip
when obtaining a
connection, but you must
be aware that the
connection state, such as
database context, is not
being reset.

Enlist

true

When this value is set to


true, the connection pooler
automatically enlists the
connection in the current
transaction context of the
creation thread if a
transaction context exists.

Unit 1: Connecting to Databases and Reading Data

33

(continued)
Property name

Default

Description

Max Pool Size

100

The maximum number of


connections allowed in the
pool.

Min Pool Size

The minimum number of


connections maintained in
the pool.

Pooling

true

When this value is set to


true, the connection pooler
draws the connection from
the appropriate pool, or if
necessary, creates and adds
it to the appropriate pool.

34

Unit 1: Connecting to Databases and Reading Data

How to Monitor Connection Pooling by Using SQL Server


Profiler
How to: Monitor Connection Pooling by Using SQL Server Profiler
SQL Server Profiler is a graphical user interface that you can use to monitor an
instance of the Microsoft SQL Server Database Engine or Analysis Server.
For example, you can use SQL Server Profiler to monitor when applications
open and close connections to determine the effect of connection pooling.

To open SQL Server Profiler and start a trace on the local SQL Server
2005 instance

1. Click Start, point to All Programs, point to Microsoft SQL Server 2005
CTP, point to Performance Tools, and then click SQL Server Profiler.
2. On the File menu, click New Trace.
3. In the Connect to Server dialog box, enter or confirm the following details,
and then click Connect:
User interface element

Value

Server type

Database Engine

Server name

(local)

Authentication

Microsoft Windows Authentication

4. In the Trace Properties dialog box, click Run.

To stop a SQL Server Profiler trace and close SQL Server Profiler
1. On the SQL Profiler toolbar, click the Stop Selected Trace button.
2. Close the SQL Profiler window.

Unit 2: Querying and Updating


Databases by Using Commands
Database Tables and Stored Procedures
Introduction
In the current phase of development of the Product Intranet Web site, you will
use the Production.Product and Production.ProductSuggestion tables in the
Adventure Works database.
This resource document describes the Production.Product and
Production.ProductSuggestion tables and related stored procedures.

Production.Product Table
The Production.Product table in the Adventure Works database contains
information about products sold by the Adventure Works organization. The
following table describes the schema of the Production.Product table.
Column name

SQL data type

Description

ProductID

int

Primary key column

Name

nvarchar(50)

Name of the product

ProductNumber

nvarchar(25)

Unique product number

MakeFlag

bit

0 = Product is purchased
1 = Product is made in-house

FinishedGoodsFlag

bit

0 = Product is not scalable


1 = Product is scalable

Color

nvarchar(15)

Product color

SafetyStockLevel

smallint

Minimum inventory quantity

ReorderPoint

smallint

Inventory level that triggers a


purchase order or work order

StandardCost

money

Standard cost of the product

ListPrice

money

Selling price

Size

nvarchar(5)

Product size

SizeUnitMeasureCost

nchar(3)

Unit of measure for size

WeightUnitMeasureCost

nchar(3)

Unit of measure for weight

Weight

decimal(8, 2)

Product weight

DaysToManufacture

int

Number of days required to


manufacture the product

36

Unit 2: Querying and Updating Databases by Using Commands


(continued)
Column name

SQL data type

Description

ProductLine

nchar(2)

R = Road
M = Mountain
T = Touring
S = Standard

Class

nchar(2)

H = High
M = Medium
L = Low

Style

nchar(2)

W = Womens
M = Mens
U = Universal

ProductSubcategoryID

int

Foreign key to the


ProductSubcategoryID
column in the
ProductSubcategory table

ProductModelID

int

Foreign key to the


ProductModelID column
in the ProductModel table

SellStartDate

datetime

Date the product was


available for sale

SellEndDate

datetime

Date the product was no


longer available for sale

DiscontinuedDate

datetime

Date the product was


discontinued

rowguid

uniqueidentifier

ROWGUIDCOL number
that uniquely identifies the
record (for merge
replication)

ModifiedDate

datetime

Date and time the record


was last updated

You will use the following stored procedures to query data in the
Production.Product table:

uspGetProductCountWithChecks

uspGetProductPrices

uspGetProductCountWithChecks
This stored procedure counts the number of products in the
Production.Product table and raises informational errors if any products have
a list price of 0 or are past their sell-end date. This stored procedure does not
have any parameters.

Unit 2: Querying and Updating Databases by Using Commands

37

uspGetProductPrices
This stored procedure returns a result set that contains the name and list price of
all products in a specified price range. The stored procedure has three
parameters, as described in the following table.
Parameter name

SQL data type

Description

@prmLowerPrice

decimal

Lower price to use in the query

@prmUpperPrice

decimal

Upper price to use in the query

@prmAveragePriceInRange

decimal

Output parameter, indicates the


average price of products in the
specified price range

Production.ProductSuggestion Table
The Production.ProductSuggestion table in the Adventure Works database
contains suggestions from employees about new products that the organization
might consider selling. The following table describes the schema of the
Production.ProductSuggestion table.
Column name

SQL data type

Description

ProductID

int

Primary key column

Description

nvarchar(50)

Product description

ListPrice

money

Suggested selling price

SuggestionTimestamp

datetime

Date and time the record was


inserted or modified

You will use the following stored procedures to create and drop the
Production.ProductSuggestion table and to query and modify its data:

uspCreateProductSuggestionTable

uspDropProductSuggestionTable

uspGetProductSuggestions

uspInsertProductSuggestion

uspUpdateProductSuggestion

uspDeleteProductSuggestion

uspCreateProductSuggestionTable
This stored procedure attempts to create the Production.ProductSuggestion
table in the Adventure Works database. The stored procedure has a single
parameter, as described in the following table.
Parameter name

SQL data type

Description

@prmSuccess

bit

Output parameter, indicates whether


the stored procedure created the table
successfully

38

Unit 2: Querying and Updating Databases by Using Commands

uspDropProductSuggestionTable
This stored procedure attempts to drop the Production.ProductSuggestion
table in the Adventure Works database. The stored procedure has a single
parameter, as described in the following table.
Parameter name

SQL data type

Description

@prmSuccess

bit

Output parameter, indicates whether


the stored procedure dropped the table
successfully

uspGetProductSuggestions
This stored procedure returns a result set of product suggestions whose
description matches a specified filter. The stored procedure has a single
parameter, as described in the following table.
Parameter name

SQL data type

Description

@prmFilter

nvarchar(50)

Filter to use in the query

uspInsertProductSuggestion
This stored procedure inserts a product suggestion into the
Production.ProductSuggestion table. The stored procedure has three
parameters, as described in the following table.
Parameter name

SQL data type

Description

@prmDescription

nvarchar(50)

Product description

@prmListPrice

money

Suggested selling price

@prmSuggestionTimestamp

datetime

Date and time the record is being


inserted

uspUpdateProductSuggestion
This stored procedure updates an existing product suggestion in the
Production.ProductSuggestion table. The stored procedure has four
parameters, as described in the following table.
Parameter name

SQL data type

Description

@prmProductID

int

Product ID of the product


suggestion to be updated

@prmDescription

nvarchar(50)

New product description

@prmListPrice

money

New suggested selling price

@prmSuggestionTimestamp

datetime

Date and time the record is being


updated

uspDeleteProductSuggestion
This stored procedure deletes an existing product suggestion in the
Production.ProductSuggestion table. The stored procedure has a single
parameter, as described in the following table.
Parameter name

SQL data type

Description

@prmProductID

int

Product ID of the product


suggestion to be deleted

Unit 2: Querying and Updating Databases by Using Commands

39

How to Execute Query Commands


This document is adapted from articles that you can find at the following
locations:
In the Microsoft Visual Studio 2005 documentation at

Obtaining a Single Value from a Database


mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.VisualSt
udio.v80.en/WD_ADONET/html/b38526cd-a62a-48cb-822ae91dfa68e02d.htm

Retrieving Data Using the DataReader


mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.VisualSt
udio.v80.en/WD_ADONET/html/97afc121-fb8b-465b-bab36d844420badb.htm

IDbCommand.ExecuteScalar Method
mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.NETDE
VFX.v20.en/cpref/html/M_System_Data_IDbCommand_ExecuteScalar.htm

IDbCommand.ExecuteReader Method ()
mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.NETDE
VFX.v20.en/cpref/html/M_System_Data_IDbCommand_ExecuteReader.ht
m

IDbCommand.ExecuteReader Method (CommandBehavior)


mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.NETDE
VFX.v20.en/cpref/html/M_System_Data_IDbCommand_ExecuteReader_1_
5b81613a.htm

SqlCommand.ExecuteXmlReader Method
mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.NETDE
VFX.v20.en/cpref/html/M_System_Data_SqlClient_SqlCommand_Execute
XmlReader.htm

40

Unit 2: Querying and Updating Databases by Using Commands

How to: Execute a Query that Returns a Scalar Result


Microsoft ADO.NET command objects have an ExecuteScalar method, which
enables you to execute a query that returns a scalar (single) result.

To execute a query that returns a scalar (single) result


1. Open a database connection.
2. Create and initialize a command object. For example, if you want to query
data from a Microsoft SQL Server database, create a SqlCommand
object.
3. Call the ExecuteScalar method on the command object.
4. Convert the return value from ExecuteScalar into an appropriate data type.
5. Dispose the command object.
6. Close the database connection.
The following examples show how to execute a query that determines the
average price of products in the Adventure Works database. The examples
assume that the query does not return a NULL result.
[Visual Basic]
Using connection as New SqlConnection(aConnectionString)
connection.Open()
Using command as New SqlCommand( _
"SELECT AVG(ListPrice) FROM Production.Product", _
connection)
command.CommandType = CommandType.Text
Dim count as Decimal = CDec(command.ExecuteScalar())
' Use query result.
End Using
End Using

' Dispose command object


' Dispose (close) connection object

Unit 2: Querying and Updating Databases by Using Commands

41

[C#]
using (SqlConnection connection = new SqlConnection(aConnectionString))
{
connection.Open();
using (SqlCommand command = new SqlCommand(
"SELECT AVG(ListPrice) FROM Production.Product",
connection))
{
command.CommandType = CommandType.Text;
decimal count = (decimal)command.ExecuteScalar();
// Use query result.
}
}

// Dispose command object.


// Dispose (close) connection object.

How to: Execute a Query that Returns a Single Row


If you want to obtain multiple values from a database, you can call the
ExecuteScalar method several times on a command object, to return each value
one at a time. A more efficient approach is to call the ExecuteReader method
once on a command object, to execute a SQL statement that returns a collection
of values in a single row result.
The ExecuteReader method returns an instance of a class that implements the
IDataReader interface. Each of the data reader classes provided by the
Microsoft .NET Framework Data Providers has a GetValues method, which
returns an array of column values for the current row.

To obtain a single row from a database


1. Open a database connection.
2. Create and initialize a command object. For example, if you want to query
data from a SQL Server database, create a SqlCommand object.
3. Call the ExecuteReader method on the command object. Assign the return
value from this method to a data reader variable, such as a SqlDataReader
variable.
4. Call the Read method on the data reader object to move to the first (and
only) row in the result set.
5. Call the GetValues method on the data reader object. Pass an object array as
a parameter to retrieve the scalar results of the query.
6. Convert each element in the array to an appropriate data type, if necessary.
7. Close the data reader object.
8. Dispose the command object.
9. Close the database connection.

42

Unit 2: Querying and Updating Databases by Using Commands

The following examples show how to execute a query that returns three values:
the number of products in the Adventure Works database, the minimum product
price, and the maximum product price. The examples place the results into a
three-element array named results.
[Visual Basic]
Using connection as New SqlConnection(aConnectionString)
connection.Open()
Using command as New SqlCommand( _
"SELECT COUNT(*), MIN(ListPrice), MAX(ListPrice) " & _
"FROM Production.Product", connection)
command.CommandType = CommandType.Text
Dim results() As Object
ReDim results(2)
Dim reader As SqlDataReader = command.ExecuteReader()
reader.Read()
reader.GetValues(results)
reader.Close()
' Use query results.
End Using
End Using

' Dispose command object.


' Dispose (close) connection object.

[C#]
using (SqlConnection connection = new SqlConnection(aConnectionString))
{
connection.Open();
using (SqlCommand command = new SqlCommand(
"SELECT COUNT(*), MIN(ListPrice), MAX(ListPrice) " +
"FROM Production.Product", connection))
{
command.CommandType = CommandType.Text;
object[] results = new object[3];
SqlDataReader reader = command.ExecuteReader();
reader.Read();
reader.GetValues(results);
reader.Close();
// Use query results.
}
}

// Dispose command object.


// Dispose (close) connection object.

Unit 2: Querying and Updating Databases by Using Commands

43

How to: Execute a Query that Returns a Result Set


ADO.NET command objects have an ExecuteReader method, which enables
you to execute a query that returns one or more result sets. You process the
result set by using a data reader object, such as a SqlDataReader object.

To execute a query that returns a result set


1. Open a database connection.
2. Create and initialize a command object. For example, if you want to query
data from a SQL Server database, create a SqlCommand object.
3. Call the ExecuteReader method on the command object. You can pass a
CommandBehavior enumeration parameter into the method, if appropriate,
to control how the command is executed. The following table lists some of
the common values for the CommandBehavior enumeration parameter.
You can use a bitwise combination of the values to achieve the desired
effect.
CommandBehavior value

Description

CommandBehavior.CloseConnection

Use this value if you want the database


connection to be closed automatically,
as soon as the data reader object is
closed.
This is useful if you return to a data
reader from a method in a middle-tier
business object to a user interface
component. When the user interface
component closes the data reader, the
database connection is closed at the
same time.

CommandBehavior.SingleResult

Use this value if the command returns a


single result set, to optimize
performance.

CommandBehavior.SingleRow

Use this value if the command returns a


single row, to optimize performance.

Assign the return value from the ExecuteReader method to a data reader
variable, such as a SqlDataReader.
4. Write a loop to call the Read method on the data reader, to read one row at a
time from the result set. Keep looping until the Read method returns false,
to indicate that there are no more rows in the result set.

44

Unit 2: Querying and Updating Databases by Using Commands

5. Each time round the loop, get the value for each column that you want to
process in the current row. Data readers provide type-safe methods such as
GetString and GetInt32, which get the value of a particular column in the
current row. Specify the column you want by its ordinal position in the
query result, starting at 0.
6. If the query command returns multiple result sets, call the NextResult
method on the data reader object, to advance to the next result set. Then
repeat steps 4 and 5, to iterate through the rows in the result set.
7. After the loop, dispose or close the data reader object.
8. Dispose the command object.
9. Close the database connection.
The following example shows how to implement a middle-tier business method
to return a SqlDataReader object. The business method queries the Adventure
Works database for all products and returns a SqlDataReader object. A user
interface component retrieves a data reader from a business method and iterates
through the result set to display the name and price of each product on the
console. The user interface component closes the data reader when it reaches
the end of the result set, which also closes the database connection because the
CommandBehavior.CloseConnection behavior was specified when the
command was executed.

Unit 2: Querying and Updating Databases by Using Commands


[Visual Basic]
' Middle-tier class, which contains a business method that returns a data reader.
Public Class MyProductsDAC
Public Shared Function GetProductReader() As SqlDataReader
Dim connection as New SqlConnection(aConnectionString)
connection.Open()
Dim sql As String = "SELECT Name, ListPrice FROM Production.Product"
Dim reader As SqlDataReader
Using command As New SqlCommand(sql, connection)
command.CommandType = CommandType.Text
reader = command.ExecuteReader(CommandBehavior.SingleResult Or _
CommandBehavior.CloseConnection)
End Using

' Dispose command object.

Return reader
End Function
End Class

' User-interface component, which calls the middle-tier business method.


Public Class MyUserInterfaceComponent
Public Sub MyMethod()
Using reader As SqlDataReader = MyProductsDAC.GetProductReader()
While (reader.Read())
Console.WriteLine("Product name: {0}", reader.GetString(0))
Console.WriteLine("Product price: {0}", reader.GetDecimal(1))
End While
End Using
End Sub
End Class

' Dispose data reader, which also closes the connection.

45

46

Unit 2: Querying and Updating Databases by Using Commands

[C#]
// Middle-tier class, which contains a business method that returns a data reader.
public class MyProductsDAC
{
public static SqlDataReader GetProductReader()
{
SqlConnection connection = new SqlConnection(aConnectionString);
connection.Open();
string sql = "SELECT Name, ListPrice FROM Production.Product";
SqlDataReader reader;
using (SqlCommand command = new SqlCommand(sql, connection))
{
command.CommandType = CommandType.Text;
reader = command.ExecuteReader(CommandBehavior.SingleResult |
CommandBehavior.CloseConnection);
}

// Dispose command object.

return reader;
}
}

// User-interface component, which calls the middle-tier business method.


public class MyUserInterfaceComponent
{
public void MyMethod()
{
using (SqlDataReader reader = MyProductsDAC.GetProductReader())
{
while (reader.Read())
{
Console.WriteLine("Product name: {0}", reader.GetString(0));
Console.WriteLine("Product price: {0}", reader.GetDecimal(1));
}
}
// Dispose data reader, which also closes the connection.
}
}

Unit 2: Querying and Updating Databases by Using Commands

47

How to: Execute a SQL Query that Returns an XML Result Set from
a SQL Server Database
The SqlCommand class in the .NET Framework Data Provider for SQL Server
has an ExecuteXmlReader method, which enables you to execute a query that
returns an Extensible Markup Language (XML) result set. You process the
result set by using an XmlReader object.

To execute a query that returns an XML result set from a SQL Server
database

1. Open a database connection.


2. Create and initialize a SqlCommand object.
3. Call the ExecuteXmlReader method on the command object. Assign the
return value from the ExecuteReader method to an XmlReader variable.
4. Write a loop to call the Read method on the XmlReader object, to read one
node at a time from the XML result set. Keep looping until the Read
method returns false, to indicate that there are no more nodes in the result
set.
5. Use methods and properties on the XmlReader object to retrieve node
values from the XML result set.
6. After the loop, close the XmlReader object.
7. Dispose the command object.
8. Close the database connection.
The following examples show how to execute a query that returns an XML
result set.
[Visual Basic]
Using connection as New SqlConnection(aConnectionString)
connection.Open()
Using command as New SqlCommand( _
"SELECT Name, ListPrice " & _
"FROM Production.Product FOR XML AUTO", _
connection)
command.CommandType = CommandType.Text
Dim reader As XmlReader = command.ExecuteXmlReader()
While (reader.Read())
' Process XML node.
End While
reader.Close()
End Using
End Using

' Dispose command object.


' Dispose (close) connection object.

48

Unit 2: Querying and Updating Databases by Using Commands

[C#]
using (SqlConnection connection = new SqlConnection(aConnectionString))
{
connection.Open();
using (SqlCommand command = new SqlCommand(
"SELECT Name, ListPrice " +
"FROM Production.Product FOR XML AUTO",
connection))
{
command.CommandType = CommandType.Text;
XmlReader reader = command.ExecuteXmlReader();
while (reader.Read())
{
// Process XML node.
}
reader.Close();
}
}

// Dispose command object.


// Dispose (close) connection object.

Unit 2: Querying and Updating Databases by Using Commands

49

Data Binding in ASP.NET 2.0


This document is adapted from articles that you can find at the following
locations:
In the Microsoft Visual Studio 2005 documentation at

Binding to Business Objects


mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.VisualSt
udio.v80.en/dv_vwdcon/html/952fabb5-4c93-46cb-9a99-54d23bf6356c.htm

How to: Bind to Data in a Templated Control (Visual Studio)


mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.VisualSt
udio.v80.en/dv_vwdcon/html/a7a77fb4-fa6d-48d7-86b6-8b5954cb1735.htm

50

Unit 2: Querying and Updating Databases by Using Commands

How to: Retrieve Data from a Business Object in an


ASP.NET 2.0 Web page
You can use the ObjectDataSource control in a Microsoft ASP.NET Web page
to retrieve data from a business object. When you define the
ObjectDataSource control, you associate the control with a particular method
on a class or an object.
When the user runs the Web page, ASP.NET invokes the specified method to
obtain data for ObjectDataSource. You can bind an ObjectDataSource
control to a user interface control, such as a GridView. The user interface
control displays the data from the data source control.
ASP.NET assumes that the business object is stateless and therefore must be
instantiated and destroyed with each data operation. The business object must
have a default constructor with no parameters. The ObjectDataSource calls
this constructor by default to create the business object instance on each page
round trip.

To bind an ObjectDataSource control to a business object


1. In Solution Explorer, open your Web page in the Designer window. Click
the Source tab at the bottom of the Designer window.
2. On your Web page, define an ObjectDataSource control.
3. Define the following attributes on the ObjectDataSource control:
Attribute

Description

ID

Define an ID attribute to identify the control on the


Web page.

runat="server"

Define a runat="server" attribute, as required by


all ASP.NET server-side controls.

TypeName

Define a TypeName attribute to specify the


namespace and class that contains the method you
want to associate with the ObjectDataSource
control.

SelectMethod

Define a SelectMethod attribute to specify the


name of the method that returns data to the
ObjectDataSource control.

InsertMethod,
UpdateMethod,
DeleteMethod

If you want the ObjectDataSource control to be


able to update data as well as to query it, define
InsertMethod, UpdateMethod, and DeleteMethod
attributes, as necessary.

Unit 2: Querying and Updating Databases by Using Commands

51

The following example defines an ObjectDataSource control with an ID of


productListReader. The control retrieves data from a static (Shared in
Microsoft Visual Basic) method named GetProductListReader in a class
named ProductsDAC. The ProductsDAC class is located in the
ProductIntranet.DataAccess namespace.
<asp:ObjectDataSource
ID="productListReader"
runat="server"
SelectMethod="GetProductListReader"
TypeName="ProductIntranet.DataAccess.ProductsDAC">
</asp:ObjectDataSource>

How to: Bind an ASP.NET Control to a Data Source Control


You can bind an ASP.NET user interface control, such as a GridView,
DetailsView, FormView, DataList, or Repeater control, to a data source
control, such as an ObjectDataSource or SqlDataSource control. When the
user runs the Web page, the user interface control displays the data from the
data source control.

To bind a GridView control to an ObjectDataSource control


1. In Solution Explorer, open your Web page in the Designer window. Click
the Source tab at the bottom of the Designer window.
2. On your Web page, define a data source control, such as an
ObjectDataSource control, and make a note of its ID attribute.
3. Define a GridView control on your Web page. Set the DataSource attribute
of the GridView control to the ID of the data source control.
The following example binds a GridView control to an ObjectDataSource
control that has an ID of productListReader.
<asp:GridView runat="server" id="GridView1" DataSourceID="productListReader">
</asp:GridVIew>

52

Unit 2: Querying and Updating Databases by Using Commands

How to Execute Parameterized Commands


This document is adapted from articles that you can find at the following
locations:
In the Microsoft Visual Studio 2005 documentation at

IDbCommand.Parameters Property
mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.NETDE
VFX.v20.en/cpref/html/P_System_Data_IDbCommand_Parameters.htm

Input and Output Parameters, and Return Values


mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.VisualSt
udio.v80.en/WD_ADONET/html/7d8e9a46-1af6-4a02-bf61969d77ae07e0.htm

ParameterDirection Enumeration
mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.NETDE
VFX.v20.en/cpref/html/T_System_Data_ParameterDirection.htm

How to: Execute Parameterized Commands


SQL statements and stored procedures can receive input-only parameters,
output-only parameters, and bidirectional parameters. Stored procedures can
also return a value.
You can use a command object to execute parameterized SQL statements and
parameterized stored procedures.

To execute a parameterized SQL statement


1. Open a database connection.
2. Create and initialize a command object. For example, if you are using the
Microsoft .NET Framework Data Provider for SQL Server, create a
SqlCommand object.
3. Create a parameter object, such as a SqlParameter object, for each input
parameter required by the SQL statement. Specify the name, type, size, and
value for each parameter, and add it to the Parameters collection of the
command object.
4. Execute the command by calling the ExecuteScalar, ExecuteReader,
ExecuteXmlReader, or ExecuteNonQuery method, as appropriate for the
type of SQL statement.
5. Use the return value obtained by executing the command.
6. Dispose the command object.
7. Close the database connection.

Unit 2: Querying and Updating Databases by Using Commands

53

The following examples show how to execute a SQL statement that searches for
products by name. The SQL statement requires an NVARCHAR(50)
parameter, which specifies a partial product name to search for. The SQL
statement returns a result set, which contains the name and price of all products
that match the search string. The application code iterates through the result set,
and displays the product information on the console.

[Visual Basic]
Dim sql as String = "SELECT Name, ListPrice FROM Production.Product " & _
"FROM Production.Product " & _
"WHERE Name LIKE @NamePart"
Using connection as New SqlConnection(aConnectionString)
connection.Open()
Using command as New SqlCommand(sql, connection)
command.CommandType = CommandType.Text
command.Parameters.Add("@NamePart", SqlDbType.NVarChar, 50).Value = aName
Using reader As SqlDataReader = command.ExecuteReader()
While (reader.Read())
Console.WriteLine("Product name: {0}", reader.GetString(0))
Console.WriteLine("Product price: {0}", reader.GetDecimal(1))
End While
End Using
End Using
End Using

' Dispose data reader.


' Dispose command object.
' Dispose (close) connection object.

54

Unit 2: Querying and Updating Databases by Using Commands

[C#]
string sql = "SELECT Name, ListPrice " +
"FROM Production.Product " +
"WHERE Name LIKE @NamePart";
using (SqlConnection connection = new SqlConnection(aConnectionString))
{
connection.Open();

using (SqlCommand command = new SqlCommand(sql, connection))


{
command.CommandType = CommandType.Text;
command.Parameters.Add("@NamePart", SqlDbType.NVarChar, 50).Value = aName;
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
Console.WriteLine("Product name: {0}", reader.GetString(0));
Console.WriteLine("Product price: {0}", reader.GetDecimal(1));
}
}
// Dispose data reader.
}
// Dispose command object.
// Dispose (close) connection object.

To execute a parameterized stored procedure


1. Open a database connection.
2. Create and initialize a command object. For example, if you are using the
.NET Framework Data Provider for SQL Server, create a SqlCommand
object.
3. Create a parameter object, such as a SqlParameter object, for each
parameter required by the stored procedure. If you want to access the return
value from the stored procedure, create a parameter to represent the return
value.
4. Specify the name and data type for each parameter. The names and data
types must be consistent with the parameters in the stored procedure
definition. For return values, use a parameter name such as
"@RETURN_VALUE".
5. For each input parameter object, assign an appropriate value to the Value
property.
6. For each output parameter object, set the Direction property to
ParameterDirection.Output.
7. If you have a return value parameter object, set the Direction property to
ParameterDirection.ReturnValue.
8. Add each parameter object to the Parameters collection of the command
object.
9. Execute the command by calling the ExecuteScalar, ExecuteReader,
ExecuteXmlReader, or ExecuteNonQuery method, as appropriate for the
type of SQL statement.

Unit 2: Querying and Updating Databases by Using Commands

55

10. Retrieve any output parameters and return value parameters from the
command object by indexing into the Parameters collection to retrieve
parameters by name or ordinal position. Note that if you called
ExecuteReader to obtain a data reader, you must close the data reader first,
before you can access output parameters and return value parameters.
11. Dispose the command object.
12. Close the database connection.
The following examples show how to execute a parameterized stored procedure
in a SQL Server database. The stored procedure takes an input parameter and an
output parameter and returns a result set. The stored procedure also returns an
integer value.
The application code creates and initializes parameter objects for the input
parameter, the output parameter, and the return value parameter and adds them
to the command. The application code invokes the command, iterates through
the result set, and then retrieves the output parameter and the return value.

[Stored Procedure]
CREATE PROCEDURE dbo.uspGetProductsAbovePrice
(
@prmPrice decimal,
@prmAverage decimal OUTPUT
)
AS
BEGIN
DECLARE @varCount int
SELECT
@prmAverage=AVG(ListPrice), @varCount=COUNT(*)
FROM
Production.Product
WHERE
ListPrice > @prmPrice
SELECT
Name, ListPrice
FROM
Production.Product
WHERE
ListPrice > @prmPrice
RETURN @varCount
END

56

Unit 2: Querying and Updating Databases by Using Commands

[Visual Basic]
Using connection As New SqlConnection(aConnectionString)
connection.Open()
Using command As New SqlCommand("uspGetProductsAbovePrice", connection)
command.CommandType = CommandType.StoredProcedure
' Create and initialize @prmPrice input parameter.
command.Parameters.Add("@prmPrice", SqlDbType.Money).Value = aPrice
' Create and initialize @prmAverage output parameter.
command.Parameters.Add("@prmAverage", SqlDbType.Money).Direction = _
ParameterDirection.Output
' Create and initialize @RETURN_VALUE return value parameter.
command.Parameters.Add("@RETURN_VALUE", SqlDbType.Int).Direction = _
ParameterDirection.ReturnValue
' Call stored procedure, and iterate through result set.
Using reader As SqlDataReader = command.ExecuteReader()
While (reader.Read())
Console.WriteLine("Product name: {0}", reader.GetString(0))
Console.WriteLine("Product price: {0}", reader.GetDecimal(1))
End While
End Using

' Dispose (close) data reader object.

' Retrieve parameter named @prmAverage from the parameters collection.


Dim average As Decimal
average = CDec(command.Parameters("@prmAverage").Value)
Console.WriteLine("Average price above threshold: {0}", average)
' Retrieve parameter named @RETURN_VALUE from the parameters collection.
Dim count As Integer
count = CInt(command.Parameters("@RETURN_VALUE").Value)
Console.WriteLine("Number of products above threshold: {0}", count)
End Using
End Using

' Dispose command object.


' Dispose (close) connection object.

Unit 2: Querying and Updating Databases by Using Commands


[C#]
using (SqlConnection connection = new SqlConnection(aConnectionString))
{
connection.Open();
using (SqlCommand command = new SqlCommand("uspGetProductsAbovePrice",
connection))
{
command.CommandType = CommandType.StoredProcedure;
// Create and initialize @prmPrice input parameter.
command.Parameters.Add("@prmPrice", SqlDbType.Money).Value = aPrice;
// Create and initialize @prmAverage output parameter.
command.Parameters.Add("@prmAverage", SqlDbType.Money).Direction =
ParameterDirection.Output;
// Create and initialize @RETURN_VALUE return value parameter.
command.Parameters.Add("@RETURN_VALUE", SqlDbType.Int).Direction =
ParameterDirection.ReturnValue;
// Call stored procedure, and iterate through result set.
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
Console.WriteLine("Product name: {0}", reader.GetString(0));
Console.WriteLine("Product price: {0}", reader.GetDecimal(1));
}
}

// Dispose (close) data reader object.

// Retrieve parameter named @prmAverage from the parameters collection.


decimal average;
average = (decimal) command.Parameters["@prmAverage"].Value;
Console.WriteLine("Average price above threshold: {0}", average);
// Retrieve parameter named @RETURN_VALUE from the parameters collection.
int count;
count = (int) command.Parameters["@RETURN_VALUE"].Value;
Console.WriteLine("Number of products above threshold: {0}", count);
}
}

// Dispose command object.


// Dispose (close) connection object.

57

58

Unit 2: Querying and Updating Databases by Using Commands

How to View Database Objects by Using Server Explorer


How to: View Database Objects by Using Server Explorer
You can use Server Explorer in Microsoft Visual Studio 2005 to view and
modify database objects, such as database tables and stored procedures.

To connect to the local SQL Server 2005 instance by using Server


Explorer

1. Start Visual Studio 2005.


2. If Server Explorer is not visible, on the View menu, click Server Explorer.
3. In Server Explorer, right-click Data Connections, and click Add
Connection.
4. In the Add Connection dialog box, enter or confirm the following details,
and click OK.
User interface element

Value

Server name

(local)

Authentication

Microsoft Windows Authentication

5. In Server Explorer, expand Data Connections, and verify that there is an


entry for the Adventure Works database on the local instance of Microsoft
SQL Server 2005.

To view and modify data in a database table


1. In Server Explorer, expand Data Connections.
2. Expand the database that you want to work with. For example, to work with
the Adventure Works database on the lon-dev-01 SQL Server 2005 instance,
expand lon-dev-01.AdventureWorks.dbo.
3. Expand Tables.
4. Right-click the table that you want to view, and then click Show Table
Data. Verify that Visual Studio 2005 displays the data for the table.
5. To modify data in the table, locate the cell that you want to modify. Enter a
new value for the cell, and move the cursor to a different cell. To specify
NULL as the cell value, press CTRL+0 while the cursor is in the cell.
6. To persist data modifications for a given row, move the cursor to a different
row.
7. Close the data window when you have finished.

Unit 2: Querying and Updating Databases by Using Commands

To view the column definitions in a database table


1. In Server Explorer, expand Data Connections.
2. Expand the database that you want to work with. For example, if you want
to work with the Adventure Works database on the local SQL Server 2005
instance, expand lon-dev-01.AdventureWorks.dbo.
3. Expand Tables.
4. Expand the table that you want to view. Server Explorer displays the list of
columns in the table.
5. To view the properties for table columns, right-click the table, and click
Open Table Definition.

To view and modify a stored procedure


1. In Server Explorer, expand Data Connections.
2. Expand the database that you want to work with. For example, if you want
to work with the Adventure Works database on the lon-dev-01 SQL Server
2005 instance, expand lon-dev-01.AdventureWorks.dbo.
3. Expand Stored Procedures.
4. Double-click the stored procedure that you want to view. Visual Studio
2005 displays the SQL code for the stored procedure in the code window.
5. To modify the stored procedure, type your SQL code in the code window.
To save the modified stored procedure, on the File menu, click Save.
6. Close the code window when you have finished.

59

60

Unit 2: Querying and Updating Databases by Using Commands

How to Execute Update Commands


This document is adapted from articles that you can find at the following
locations:
In the Microsoft Visual Studio 2005 documentation at

IDbCommand.ExecuteNonQuery Method
mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.NETDE
VFX.v20.en/cpref/html/M_System_Data_IDbCommand_ExecuteNonQuery
.htm

SqlCommand.ExecuteNonQuery Method
mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.NETDE
VFX.v20.en/cpref/html/M_System_Data_SqlClient_SqlCommand_Execute
NonQuery.htm

How to: Execute Update Commands


Microsoft ADO.NET command objects have an ExecuteNonQuery method,
which enables you to execute a data command such as an INSERT, UPDATE,
or DELETE statement. You can also ExecuteNonQuery to execute a catalog
update command, such as creating or dropping a database table.

To execute a data update command


1. Open a database connection.
2. Create and initialize a command object. For example, if you want to modify
data in a Microsoft SQL Server database, create a SqlCommand object.
3. Call the ExecuteNonQuery method on the command object. The method
returns the number of rows affected by the command.
4. Dispose the command object.
5. Close the database connection.
The following examples show how to increase the price of a particular product
in the Adventure Works database. The uspUpdatePrice stored procedure takes
two parameters, which specify the ID of the product to modify, and the amount
of the price increase. The application code calls the uspUpdatePrice stored
procedure, and displays a message on the console to indicate whether the
command succeeded.

Unit 2: Querying and Updating Databases by Using Commands


[Stored Procedure]
CREATE PROCEDURE dbo.uspUpdatePrice
(
@prmProductID int,
@prmAmount money
)
AS
BEGIN
UPDATE Production.Product
SET ListPrice = ListPrice + @prmAmount
WHERE ProductID=@prmProductID
END
[Visual Basic]
Using connection As New SqlConnection(aConnectionString)
connection.Open()
Using command As New SqlCommand("uspUpdatePrice", connection)
command.CommandType = CommandType.StoredProcedure
command.Parameters.Add("@prmProductID", SqlDbType.Int).Value = aProductID
command.Parameters.Add("@prmAmount", SqlDbType.Money).Value = anAmount
Dim rowsAffected As Integer = command.ExecuteNonQuery()
If rowsAffected = 1 Then
Console.WriteLine("The product has been updated successfully.")
Else
Console.WriteLine("The product has not been updated.")
End If
End Using
End Using

' Dispose command object.


' Dispose (close) connection object.

61

62

Unit 2: Querying and Updating Databases by Using Commands

[C#]
using (SqlConnection connection = new SqlConnection(aConnectionString))
{
connection.Open();
using (SqlCommand command = new SqlCommand("uspUpdatePrice", connection))
{
command.CommandType = CommandType.StoredProcedure;
command.Parameters.Add("@prmProductID", SqlDbType.Int).Value = aProductID;
command.Parameters.Add("@prmAmount", SqlDbType.Money).Value = anAmount;
int rowsAffected = command.ExecuteNonQuery();
if (rowsAffected == 1)
Console.WriteLine("The product has been updated successfully.");
else
Console.WriteLine("The product has not been updated.");
}
}

// Dispose command object.


// Dispose (close) connection object.

To execute a catalog update command


1. Open a database connection.
2. Create and initialize a command object. For example, if you want to perform
a catalog operation on a SQL Server database, create a SqlCommand
object.
3. Call the ExecuteNonQuery method on the command object. The method
always returns -1 when you perform a catalog update command. To
determine whether the operation succeeded or failed, cast the
ExecuteNonQuery return value to a boolean and test the boolean value in
your application code.
4. Dispose the command object.
5. Close the database connection.
The following examples show how to execute a command to drop a table
named MyTable in the database. The uspDropMyTable stored procedure
assigns a boolean value to an output parameter to indicate whether the table has
been dropped successfully. The application code calls the uspDropMyTable
stored procedure and displays a message on the console to indicate whether the
command succeeded.

Unit 2: Querying and Updating Databases by Using Commands

63

[Stored Procedure]
CREATE PROCEDURE dbo.uspDropMyTable
(
@prmSuccess bit OUTPUT
)
AS
BEGIN
SELECT @prmSuccess = 0
IF EXISTS (
SELECT * FROM dbo.sysobjects
WHERE id = OBJECT_ID(N'MyTable') AND OBJECTPROPERTY(id, N'IsUserTable') = 1)
BEGIN
DROP TABLE MyTable
SELECT @prmSuccess = 1
END
END
[Visual Basic]
Using connection As New SqlConnection(aConnectionString)
connection.Open()
Using command As New SqlCommand("uspDropMyTable", connection)
command.CommandType = CommandType.StoredProcedure
command.Parameters.Add("@prmSuccess", SqlDbType.Bit).Direction = _
ParameterDirection.Output
command.ExecuteNonQuery()
Dim success As Boolean = CBool(command.Parameters("@prmSuccess").Value)
If success Then
Console.WriteLine("MyTable has been dropped successfully.")
Else
Console.WriteLine("MyTable has not been dropped.")
End If
End Using
End Using

' Dispose command object.


' Dispose (close) connection object.

64

Unit 2: Querying and Updating Databases by Using Commands

[C#]
using (SqlConnection connection = new SqlConnection(aConnectionString))
{
connection.Open();
using (SqlCommand command = new SqlCommand("uspDropMyTable", connection))
{
command.CommandType = CommandType.StoredProcedure;
command.Parameters.Add("@prmSuccess", SqlDbType.Bit).Direction =
ParameterDirection.Output;
command.ExecuteNonQuery();
bool success = (bool)command.Parameters["@prmSuccess"].Value;
if (success)
Console.WriteLine("MyTable has been dropped successfully.");
else
Console.WriteLine("MyTable has not been dropped.");
}
}

// Dispose command object.


// Dispose (close) connection object.

Unit 2: Querying and Updating Databases by Using Commands

65

Collations
This document is adapted from articles that you can find at the following
locations:
In the Microsoft SQL Server 2005 Books Online documentation at

Working with Collations


ms-help://MS.SQLCC.v9/MS.SQLSVR.v9.en/udb9/html/61cdbb6b-3ca14d73-938b-22e4f06f75ea.htm

Collation Types
ms-help://MS.SQLCC.v9/MS.SQLSVR.v9.en/udb9/html/0770f908-abc34401-9934-8621118e8335.htm

Windows Collation Sorting Styles


ms-help://MS.SQLCC.v9/MS.SQLSVR.v9.en/instsql9/html/314b256e9960-4126-9fa8-957b6d322e8a.htm

Introduction to Collations
Collations specify the rules for how strings of character data are sorted and
compared, based on the norms of particular languages and locales. For example,
in an ORDER BY clause, a speaker of English would expect the character string
Chiapas to come before Colima in ascending order. But a speaker of Spanish
in Mexico might expect words beginning with Ch to appear at the end of a list
of words beginning with C. Collations dictate these kinds of sorting and
comparison rules. The Latin1_General collation will sort Chiapas before
Colima in an ORDER BY ASC clause, whereas the Traditional_Spanish
collation will sort Chiapas after Colima.
You can specify a collation for non-Unicode and Unicode character data:

When you specify a collation for non-Unicode character data, such as char,
varchar, and text data, a particular code page is associated with the
collation. For example, if you define a char column in a table with the
Latin1_General collation, Microsoft SQL Server interprets and displays the
data by using the code points of the 1252 (Latin1) code page.

When you specify collations for Unicode data, such as nchar, nvarchar,
and ntext, no associated code page is required because Unicode data
handles virtually all characters of all the languages in the world.

66

Unit 2: Querying and Updating Databases by Using Commands

Collation Types
There are two groups of collations in SQL Server 2005: Windows collations
and SQL collations.

Windows Collations. Windows collations are collations defined for SQL


Server to support Microsoft Windows locales. By specifying a Windows
collation for SQL Server, the instance of SQL Server uses the same code
pages and sorting and comparison rules as an application running on a
computer for which you have specified the associated Windows locale. For
example, the French Windows collation for SQL Server matches the
collation attributes of the French locale for Windows.
There are more Windows locales than there are SQL Server Windows
collations. The names of Windows locales are based on a language and
territory, for example, French (Canada). Several languages, however, share
common alphabets and rules for sorting and comparing characters. For
example, 33 Windows locales, including all of the Portuguese and English
Windows locales, use the Latin1 code page (1252) and follow a common
set of rules for sorting and comparing characters. The SQL Server Windows
collation based on the Latin1_General code page and sorting rules supports
all 33 of these Windows locales. Also, Windows locales specify attributes
not covered by SQL Server Windows collations, such as currency, date, and
time formats. Because countries/regions such as Great Britain and the
United States have different currency, date, and time formats, they require
different Windows locales. They do not require different SQL Server
collations because they have the same alphabet and rules for sorting and
comparing characters.

SQL Collations. SQL collations are a compatibility option to match the


attributes of common combinations of code page number and sort orders
that have been specified in earlier versions of SQL Server. Many of these
collations support suffixes for case, accent, kana, and width sensitivity, but
not in every case.

In SQL Server 2005, you should primarily use Windows collations. This is
particularly true if you have a mix of Unicode and non-Unicode columns in
your database. Windows collations actually apply Unicode-based sorting rules
to both Unicode and non-Unicode data alike. This means that SQL Server
internally converts non-Unicode data to Unicode to perform comparison
operations. Doing so provides consistency across data types in SQL Server, and
also provides developers the ability to sort strings in their applications by using
the same rules that SQL Server uses. Conversely, SQL collations apply nonUnicode sorting rules to non-Unicode data, and Unicode sorting rules to
Unicode data (using a corresponding Windows collation for the Unicode data).
This discrepancy can result in different results for comparisons of the same
characters. Therefore, if you have a mix of Unicode and non-Unicode columns
in your database, they should all be defined with Windows collations so that the
same sorting rules are used across Unicode and non-Unicode data.
You should use SQL collations only to maintain compatibility with existing
instances of earlier versions of SQL Server or to maintain compatibility in
applications developed by using SQL collations in earlier versions of SQL
Server.

Unit 2: Querying and Updating Databases by Using Commands

67

Windows Collation Sorting Styles


Windows collations define rules for storing character data based on an
associated Windows locale. The base Windows collation rules specify which
alphabet or language is used when dictionary sorting is applied, as well as the
code page used to store non-Unicode character data.
The following table describes some of the Windows collation sort order options
for SQL Server 2005.
Windows collation suffix (suffix)

Sort order description

Case-sensitive (_CS)

Distinguishes between uppercase and


lowercase letters. If selected, lowercase
letters sort ahead of their uppercase
versions.
If case-insensitivity is specified, SQL
Server considers the uppercase and
lowercase versions of letters to be
identical for sorting purposes.

Accent-sensitive (_AS)

Distinguishes between accented and


unaccented characters. For example, e is
not equal to .
If accent-insensitivity is specified, SQL
Server considers the accented and
unaccented versions of letters to be
identical for sorting purposes.

Kana-sensitive (_KS)

Distinguishes between the two types of


Japanese kana characters: Hiragana and
Katakana.
If kana-insensitivity is specified, SQL
Server considers Hiragana and Katakana
characters to be equal for sorting
purposes.

Width-sensitive (_WS)

Distinguishes between a single-byte


character and the same character when
represented as a double-byte character.
If width-insensitivity is specified, SQL
Server considers the single-byte and
double-byte representation of the same
character to be identical for sorting
purposes.

68

Unit 2: Querying and Updating Databases by Using Commands

Each Windows collation is combined as a series of suffixes to define case,


accent, width, or kana-sensitivity. For example, the suffix _CI_AI represents
case-insensitive, accent-insensitive, kana-insensitive, and width-insensitive.
You can use Windows collation suffixes in a COLLATE clause in a SELECT
statement. For example, the following SELECT statement performs a caseinsensitive, accent-insensitive search for all products that contain the character
e in their names. Product names that include the uppercase E character or
accented characters, such as , , , and , will be included in the result
set.
SELECT
Name
FROM
Production.Product
WHERE
Name LIKE ('%e%') COLLATE LATIN1_GENERAL_CI_AI

Unit 2: Querying and Updating Databases by Using Commands

69

How to Localize Dates, Times, and Numeric Values in


ASP.NET 2.0
This document is adapted from an article that you can find at the following
location:
On the MSDN Web site at

Design and Implementation Guidelines for Web Clients: Globalization and


Localization

Best Practices for Globalization and Localization

Overview of Globalization and Localization Issues


Globalization refers to the process of making sure that an application does not
contain any internal dependencies on a particular culture. For example, it is best
for a globalized application not to have any hard-coded number formats that can
differ across multiple countries, or to assume a particular sorting mechanism for
strings. A globalized application can correctly accept, process, and display a
worldwide assortment of scripts, data formats, and languages.
Localization is the process of adapting an application, and in particular the user
interface, to suit a specific culture. Localization typically involves tasks such as
translating strings into different natural languages, resizing user interface
elements to fit on the screen, and regenerating images for specific cultures.
Cultures around the world have different rules for formatting data such as
numbers, dates, and times. A unique culture ID identifies each culture. A
culture ID contains a neutral culture code that indicates the language of the
culture and an optional specific culture code indicating the country represented
by the culture:

Neutral culture codes are written in lowercase. For example, en represents


English and fr represents French.

Specific culture codes are appended in uppercase to enable specific


language and country combinations such as en-US (English in the United
States), en-GB (English in the United Kingdom), fr-FR (French in
France), and fr-CA (French in Canada) to be represented.

The Microsoft .NET Framework supports all cultures defined in RFC 1766, in
addition to an invariant culture for data that is culture independent. An invariant
culture uses an empty string as the culture ID, and equates to the English
language with no specific country.

70

Unit 2: Querying and Updating Databases by Using Commands

How to: Identify the Current Culture


On any particular computer running Microsoft Windows, the system regional
and language settings determine the systems current culture. By default, .NET
Framework applications inherit the current culture from these settings, and use
it when performing tasks such as formatting numbers, sorting strings, and
displaying currency.
The .NET Framework uses a combination of two cultures to handle different
aspects of localization:

Current culture. Determines how to format various data types, such as


numbers and dates.

Current user interface culture. The resource manager uses the current user
interface culture to determine which localized resource files to load. For
example, each localized resource file can contain strings targeted toward a
particular natural language, such as English or French, or a particular
specific culture, such as British English or French Canadian.

The System.Globalization.CultureInfo class in the .NET Framework has two


static properties, CurrentCulture and CurrentUICulture, to represent these
two aspects of localization:

CultureInfo.CurrentCulture. Indicates the culture of the currently


executing thread.

CultureInfo.CurrentUICulture. Indicates the user interface culture of the


currently executing thread.

The following code shows how to get the names of the culture and user
interface culture of the currently executing thread.
[Visual Basic]
Imports System.Globalization
Dim currentCultureName As String = CultureInfo.CurrentCulture.Name
Dim currentUICultureName As String = CultureInfo.CurrentUICulture.Name

[C#]
using System.Globalization;
string currentCultureName = CultureInfo.CurrentCulture.Name;
string currentUICultureName = CultureInfo.CurrentUICulture.Name;

Unit 2: Querying and Updating Databases by Using Commands

71

How to: Set the Current Culture in an ASP.NET Web Application


You can programmatically set the current culture in a Microsoft ASP.NET Web
application, based on the preferred national language of the user.
The following examples show how to retrieve the language preferences of the
user from the Request.UserLanguages property in an ASP.NET Web page.
The examples assign an appropriate culture to the currently executing thread,
based on the preferred language.
[Visual Basic]
Imports System.Globalization
Imports System.Threading
Thread.CurrentThread.CurrentCulture = _
CultureInfo.CreateSpecificCulture(Request.UserLanguages(0))

[C#]
using System.Globalization;
using System.Threading;
Thread.CurrentThread.CurrentCulture =
CultureInfo.CreateSpecificCulture(Request.UserLanguages[0]);

How to: Localize the Format of Data


You can use the String.Format method to localize the format of data such as
dates, times, numbers, and currencies, according to the culture in the currently
executing thread.
The following examples show how to display a localized System.DateTime, a
localized number, and a localized currency value. Each value will be formatted
according to the current culture.
[Visual Basic]
Dim formattedDate As String = String.Format({0:f}, aDateTime)
Dim formattedNumber As String = String.Format({0:n}, aNumber)
Dim formattedCurrency As String = String.Format({0:c}, aCurrency)

[C#]
string formattedDate = String.Format({0:f}, aDateTime);
string formattedNumber = String.Format({0:n}, aNumber);
string formattedCurrency = String.Format({0:c}, aCurrency);

72

Unit 2: Querying and Updating Databases by Using Commands

You can use the same formatting syntax in a data binding expression in an
ASP.NET Web page. The following example shows how to define TextBox
controls that contain data binding expressions for a date and time, a number,
and a currency value:
<asp:TextBox Text='<%# Bind("ADateTimeField", "{0:f}") %>'
<asp:TextBox Text='<%# Bind("ANumberField",
"{0:n}") %>'
<asp:TextBox Text='<%# Bind("ACurrencyField", "{0:c}") %>'

... />
... />
... />

Unit 3: Performing Transactional


Operations
Database Tables and Stored Procedures
Introduction
In the current phase of development of the Product Intranet Web site, you will
write code to access data in the following database engines:

Local instance of SQL Server 2005. You will write code to query and
modify product data in the Production.Product table in the Adventure
Works database.

Local instance of SQL Server 2005 Express. You will write code to log
global changes to product reorder points. You will save log information in
the ReorderPointIncrementLog table in the LogDatabase database.

This resource document describes the Production.Product and


ReorderPointIncrementLog tables and related stored procedures.

Production.Product Table in SQL Server 2005


The Production.Product table in the Adventure Works database contains
information about products sold by the Adventure Works organization. The
following table describes the schema of the Production.Product table.
Column name

SQL data type

Description

ProductID

int

Primary key column

Name

nvarchar(50)

Name of the product

ProductNumber

nvarchar(25)

Unique product number

MakeFlag

bit

0 = Product is purchased
1 = Product is made in-house

FinishedGoodsFlag

bit

0 = Product is not scalable


1 = Product is scalable

Color

nvarchar(15)

Product color

SafetyStockLevel

smallint

Minimum inventory quantity

ReorderPoint

smallint

Inventory level that triggers a


purchase order or work order

StandardCost

money

Standard cost of the product

ListPrice

money

Selling price

74

Unit 3: Performing Transactional Operations


(continued)
Column name

SQL data type

Description

Size

nvarchar(5)

Product size

SizeUnitMeasureCode

nchar(3)

Unit of measure for size


Foreign key to the
UnitMeasureCode column
in the UnitMeasure table

WeightUnitMeasureCode

nchar(3)

Unit of measure for weight


Foreign key to the
UnitMeasureCode column
in the UnitMeasure table

Weight

decimal(8, 2)

Product weight

DaysToManufacture

int

Number of days required


to manufacture the product

ProductLine

nchar(2)

R = Road
M = Mountain
T = Touring
S = Standard

Class

nchar(2)

H = High
M = Medium
L = Low

Style

nchar(2)

W = Womens
M = Mens
U = Universal

ProductSubcategoryID

int

Foreign key to the


ProductSubcategoryID
column in the
ProductSubcategory table

ProductModelID

int

Foreign key to the


ProductModelID column
in the ProductModel table

SellStartDate

datetime

Date the product was


available for sale

SellEndDate

datetime

Date the product was no


longer available for sale

DiscontinuedDate

datetime

Date the product was


discontinued

rowguid

uniqueidentifier

ROWGUIDCOL number
that uniquely identifies the
record (for merge
replication)

ModifiedDate

datetime

Date and time the record


was last updated

Unit 3: Performing Transactional Operations

75

You will use the following stored procedures to query and modify data in the
Production.Product table:

uspGetStockReorderPoints

uspInsertStock

uspUpdateStockReorderPoint

uspUpdateAllStockReorderPoints

uspGetStockReorderPoints
This stored procedure returns a result set that contains the product ID, name,
and reorder point for all products that have a stock reorder point in a specified
range. The stored procedure has two parameters, as described in the following
table.
Parameter name

SQL data type

Description

@prmLowerReorderPoint

smallint

Lower reorder point in the query

@prmUpperReorderPoint

smallint

Upper reorder point in the query

uspInsertStock
This stored procedure inserts a product into the Production.Product table. The
stored procedure has 10 parameters, as described in the following table.
Parameter name

SQL data type

Description

@prmName

nvarchar(50)

Name of the product

@prmProductNumber

nvarchar(25)

Unique product number

@prmMakeFlag

bit

0 = Product is purchased
1 = Product is made in-house

@prmFinishedGoodsFlag

bit

0 = Product is not scalable


1 = Product is scalable

@prmSafetyStockLevel

smallint

Minimum inventory quantity

@prmReorderPoint

smallint

Inventory level that triggers a


purchase order or work order

@prmStandardCost

money

Standard cost of the product

@prmListPrice

money

Selling price

@prmDaysToManufacture

int

Number of days required to


manufacture the product

@prmSellStartDate

datetime

Date the product was available


for sale

76

Unit 3: Performing Transactional Operations

uspUpdateStockReorderPoint
This stored procedure increments the reorder point for a specified product in the
Production.Product table. The stored procedure has two parameters, as
described in the following table:
Parameter name

SQL data type

Description

@prmProductID

int

Product ID of the product to be


updated

@prmReorderPointIncrement

smallint

Amount by which the reorder


point is to be incremented

uspUpdateAllStockReorderPoints
This stored procedure increments the reorder point for all products in the
Production.Product table. The stored procedure has a single parameter, as
described in the following table:
Parameter name

SQL data type

Description

@prmReorderPointIncrement

smallint

Amount by which the reorder


points are to be incremented

ReorderPointIncrementLog Table in SQL Server 2005


Express
The ReorderPointIncrementLog table in the LogDatabase database contains
information about global changes to stock reorder points. The following table
describes the schema of the ReorderPointIncrementLog table.
Column name

SQL data type

Description

ID

int

Primary key column

Increment

smallint

Reorder point increment

ModifiedDate

datetime

Date and time the record was


inserted

You will use the following stored procedure to insert data in the
ReorderPointIncrementLog table:

uspInsertReorderPointIncrementLog

uspInsertReorderPointIncrementLog
This stored procedure inserts a record into the ReorderPointIncrementLog
table. The stored procedure has a single parameter, as described in the following
table:
Parameter name

SQL data type

Description

@prmIncrement

smallint

Reorder point increment

Unit 3: Performing Transactional Operations

77

How to Manage Local Transactions by Using ADO.NET


This document is adapted from articles that you can find at the following
locations:
In the Microsoft Visual Studio 2005 documentation at

IDbTransaction Interface
mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.NETDE
VFX.v20.en/cpref/html/T_System_Data_IDbTransaction.htm

IDbConnection Interface
mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.NETDE
VFX.v20.en/cpref/html/T_System_Data_IDbConnection.htm

IsolationLevel Enumeration
mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.NETDE
VFX.v20.en/cpref/html/T_System_Data_IsolationLevel.htm

How to: Manage Local Transactions by Using ADO.NET


Microsoft ADO.NET includes an IDbTransaction interface that enables you to
run several commands within a transaction. If all the commands succeed, call
the Commit method on the transaction object to make the changes a permanent
part of the database. If any command fails, call the Rollback method on the
transaction object to cancel the effects of the commands.

To update a database within a local transaction


1. Open a database connection.
2. Call the BeginTransaction method on the connection object. Assign the
return value to a variable of a type that implements the IDbTransaction
interface, such as SqlTransaction. If you want to specify the isolation level
for the transaction, pass a System.Data.IsolationLevel enumeration value
into the BeginTransaction method.
3. For each command that you want to run in the transaction, set the
Transaction property to refer to the transaction object.
4. Run the required command objects.
5. If the commands complete successfully, call the Commit method on the
transaction object. If any problems occur, call the Rollback method to
cancel the updates.
6. Dispose the command object.
7. Dispose the transaction object.
8. Close the database connection.

78

Unit 3: Performing Transactional Operations

The following examples show how to perform several updates within a


transaction. The application code opens a connection to a database and creates a
transaction that uses the Serializable isolation level. The application code
creates and runs two commands within the transaction. If both updates succeed,
the application code commits the transaction. If either update fails, the
application code rolls back the transaction.
[Visual Basic]
Dim success As Boolean = False
Dim connection As SqlConnection = Nothing
Dim transaction as SqlTransaction = Nothing
Try
connection = New SqlConnection(aConnectionString)
connection.Open()
transaction = connection.BeginTransaction( _
System.Data.IsolationLevel.Serializable)
Using command1 As New SqlCommand(sql1, connection, transaction)
Dim rowsUpdated1 As Integer = command1.ExecuteNonQuery()
If rowsUpdated1 > 0 Then
Using command2 As New SqlCommand(sql2, connection, transaction)
Dim rowsUpdated2 As Integer = command2.ExecuteNonQuery()
If rowsUpdated2 > 0 Then
success = True
End If
End Using
End If
End Using
Finally
If success Then
transaction.Commit()
Else
transaction.Rollback()
End If
If connection IsNot Nothing Then
connection.Close()
End If
End Try

Unit 3: Performing Transactional Operations


[C#]
bool success = false;
SqlConnection connection = null;
SqlTransaction transaction = null;
try
{
connection = new SqlConnection(aConnectionString);
connection.Open();
transaction = connection.BeginTransaction(
System.Data.IsolationLevel.Serializable);
using (SqlCommand command1 = new SqlCommand(sql1, connection, transaction))
{
int rowsUpdated1 = command1.ExecuteNonQuery();
if (rowsUpdated1 > 0)
{
using (SqlCommand command2 =
new SqlCommand(sql2, connection, transaction))
{
int rowsUpdated2 = command2.ExecuteNonQuery();
if (rowsUpdated2 > 0)
{
success = true;
}
}
}
}
}
finally
{
if (success)
transaction.Commit();
else
transaction.Rollback();
if (connection != null)
connection.Close();
}

79

80

Unit 3: Performing Transactional Operations

How to Manage Transactions in the Data Tier


This document is adapted from articles that you can find at the following
locations:
In the Microsoft SQL Server 2005 Books Online documentation at

Controlling Transactions (Database Engine)


ms-help://MS.SQLCC.v9/MS.SQLSVR.v9.en/udb9/html/0cbd323b-60e14aab-824d-129d266c0a74.htm

Explicit Transactions
ms-help://MS.SQLCC.v9/MS.SQLSVR.v9.en/udb9/html/052c6ef6-88544d26-b6b5-0d4ccf6d1018.htm

How to: Manage Transactions in the Data Tier


You can manage transactions in the data tier by using SQL statements in stored
procedures and triggers to begin a transaction, commit a transaction, and roll
back a transaction.
The following table lists some of the SQL statements that you can use to
manage transactions in the data tier.
SQL statement

Description

BEGIN TRANSACTION

Marks the starting point of an explicit,


local transaction.

COMMIT TRANSACTION

Marks the end of a successful transaction,


and commits all changes that occurred
after the BEGIN TRANSACTION
statement.

ROLLBACK TRANSACTION

Marks the end of an unsuccessful


transaction, and cancels all changes that
occurred after the BEGIN
TRANSACTION statement.

If you begin a local transaction in a SQL Server database and modify data in a
remote table on a linked server or call a remote stored procedure, SQL Server
automatically promotes the local transaction to a distributed transaction. The
local SQL Server instance becomes the transaction controller, and it uses the
Microsoft Distributed Transaction Controller (DTC) to manage the distributed
transaction.

To manage an explicit local transaction in the data tier


1. Create a stored procedure.
2. In the stored procedure, begin a transaction.
3. Perform data update operations within the transaction.
4. Commit the transaction if all update operations succeeded, or roll back the
transaction if any update operation failed.

Unit 3: Performing Transactional Operations

The following example shows how to manage transactions in a stored


procedure. The stored procedure creates a transaction, and it performs two
update operations within the transaction. If both updates succeed, the stored
procedure commits the transaction. Otherwise, the stored procedure rolls back
the transaction.
[Stored procedure]
CREATE PROCEDURE dbo.uspUpdateTwoProducts
(
@prmProductID1 int,
@prmProductID2 int
)
AS
-- Declare a status flag, to indicate the overall success or failure.
DECLARE @varSuccess BIT
SELECT @varSuccess = 0
-- Start an explicit local transaction.
BEGIN TRANSACTION
-- Increase price of first product by 10%.
UPDATE Production.Product
SET ListPrice = ListPrice * 1.1
WHERE
ProductID=@prmProductID1
-- Proceed if first update succeeded
IF (@@ERROR = 0 AND @@ROWCOUNT = 1)
BEGIN
-- Decrease price of second product by 10%.
UPDATE Production.Product
SET ListPrice = ListPrice * 0.9
WHERE
ProductID=@prmProductID2
-- Set status flag to true if second update succeeded.
IF @@ERROR = 0 AND @@ROWCOUNT = 1
SELECT @varSuccess = 1
END
-- Commit or rollback the transaction.
IF @varSuccess = 1
COMMIT TRANSACTION
ELSE
ROLLBACK TRANSACTION
RETURN

81

82

Unit 3: Performing Transactional Operations

How to View Information About a Transaction by Using


ADO.NET
This document is adapted from articles that you can find at the following
locations:
In the Microsoft Visual Studio 2005 documentation at

Transaction Class
mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.NETDE
VFX.v20.en/cpref/html/T_System_Transactions_Transaction.htm

Transaction Members
mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.NETDE
VFX.v20.en/cpref/html/T_System_Transactions_Transaction_Members.htm

Transaction.TransactionInformation Property
mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.NETDE
VFX.v20.en/cpref/html/P_System_Transactions_Transaction_TransactionIn
formation.htm

How to: View Information About a Transaction by Using ADO.NET


You can use the System.Transactions.Transaction class to obtain information
about the current transaction. The Transaction class has a static (Shared in
Microsoft Visual Basic) property named Current, which returns a reference to
a Transaction object for the current transaction. The Transaction object has
several properties that enable you to view information about the transaction.

To view information about a transaction by using Microsoft ADO.NET


1. Add a reference to the System.Transactions assembly.
2. Import the System.Transactions namespace.
3. Get a reference to the current transaction, by using the static (Shared in
Visual Basic) Current property on the Transaction class.
4. Get the isolation level of the transaction by using the IsolationLevel
property of the transaction object. The IsolationLevel property returns an
instance of the System.Transactions.IsolationLevel enumeration type.
5. Get additional information about the transaction by using the
TransactionInformation property of the Transaction object. The
TransactionInformation property returns an instance of the
System.Transactions.TransactionInformation class.
6. Get the creation time of the transaction by getting the CreationTime
property of the TransactionInformation object.
7. Get the status of the transaction by getting the Status property of the
TransactionInformation object.

Unit 3: Performing Transactional Operations

83

8. Get the Globally Unique Identifier (GUID) of the transaction by getting the
LocalIdentifier property of the TransactionInformation object.
9. If the transaction is a distributed transaction, get the GUID of the distributed
transaction by getting the DistributedIdentifier property of the
TransactionInformation object. If the transaction is a local transaction,
this property returns a zeroed GUID.
The following examples show how to display information about the current
transaction on the console.
[Visual Basic]
Imports System.Transactions
...
Dim currentTransaction As Transaction = Transaction.Current
Dim info As TransactionInformation = currentTransaction.TransactionInformation
Console.WriteLine("Isolation level: {0}", _
currentTransaction.IsolationLevel.ToString())
Console.WriteLine("Creation time:
Console.WriteLine("Status:
Console.WriteLine("Local ID:
Console.WriteLine("Distributed ID:

{0}",
{0}",
{0}",
{0}",

info.CreationTime)
info.Status.ToString())
info.LocalIdentifier.ToString())
info.DistributedIdentifier.ToString())

[C#]
using System.Transactions;
...
Transaction currentTransaction = Transaction.Current;
TransactionInformation info = currentTransaction.TransactionInformation;
Console.WriteLine("Isolation level: {0}",
currentTransaction.IsolationLevel.ToString());
Console.WriteLine("Creation time:
Console.WriteLine("Status:
Console.WriteLine("Local ID:
Console.WriteLine("Distributed ID:

{0}",
{0}",
{0}",
{0}",

info.CreationTime);
info.Status.ToString());
info.LocalIdentifier.ToString());
info.DistributedIdentifier.ToString());

84

Unit 3: Performing Transactional Operations

How to Manage Distributed Transactions by Using


ADO.NET
This document is adapted from articles that you can find at the following
locations:
In the Microsoft Visual Studio 2005 documentation at

TransactionScope Class
mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.NETDE
VFX.v20.en/cpref/html/T_System_Transactions_TransactionScope.htm

TransactionOptions Structure
mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.NETDE
VFX.v20.en/cpref/html/T_System_Transactions_TransactionOptions.htm

TransactionScopeOption Enumeration
mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.NETDE
VFX.v20.en/cpref/html/T_System_Transactions_TransactionScopeOption.h
tm

How to: Manage Distributed Transactions by Using ADO.NET


You can use the System.Transactions.TransactionScope class to participate
in a local or distributed transaction.
The TransactionScope class has several overloaded constructors that enable
you to specify the isolation level for the transaction, the time span after which
the transaction scope times out and rolls back the transaction, and the nesting
behavior of the transaction scope. To specify the nesting behavior, use one of
the values in the TransactionScopeOption enumeration type. The following
table lists the values available in TransactionScopeOption.
TransactionScopeOption value

Description

Required

If an ambient transaction already exists,


the transaction scope uses that transaction.
Otherwise, the transaction scope creates a
new transaction.
This is the default value.

RequiresNew

The transaction scope always creates a


new transaction.

Suppress

The transaction scope suppresses the


ambient transaction context. All
operations within the transaction scope are
performed without an ambient transaction
context.

A transaction scope defines a block of code that participates in a transaction. If


the code block completes successfully, the transaction manager commits the
transaction. Otherwise, the transaction manager rolls back the transaction.

Unit 3: Performing Transactional Operations

85

To update multiple databases within a distributed transaction


1. Add a reference to the System.Transactions assembly.
2. Import the System.Transactions namespace.
3. If you want to specify the nesting behavior for the transaction, declare a
TransactionScopeOption variable, and assign it a suitable value.
4. If you want to specify the isolation level or timeout for the transaction,
create a TransactionOptions object, and set its IsolationLevel and
TimeOut properties.
5. Create a new TransactionScope object in a using statement (a Using
statement in Microsoft Visual Basic). Pass a TransactionScopeOption
variable and a TransactionOptions object into the constructor, if
appropriate.
6. In the using block (Using block in Visual Basic), open connections to each
database that you need to update, and perform update operations as required
by your application. If all updates succeed, call the Complete method on the
TransactionScope object
7. Close the using block (Using block in Visual Basic) to dispose the
TransactionScope object. If the transaction completed successfully, the
transaction manager commits the transaction. Otherwise, the transaction
manager rolls back the transaction.
The following examples show how to create and use a TransactionScope
object to manage a distributed transaction. The constructor for the
TransactionScope object specifies that a new transaction is required, selects
the ReadCommitted isolation level, and specifies a timeout of two minutes for
the transaction.
The code within the transaction scope opens connections to two separate
databases, and performs an update operation on each database. If both updates
succeed, the code calls the Complete method on the TransactionScope object.
At the end of the using block for the transaction scope, the Dispose method is
called implicitly on the TransactionScope object, and the transaction manager
commits or rolls back the transaction.

86

Unit 3: Performing Transactional Operations

[Visual Basic]
Imports System.Transactions
...
Dim options As New TransactionOptions()
options.IsolationLevel = System.Transactions.IsolationLevel.ReadCommitted
options.Timeout = New TimeSpan(0, 2, 0)
Using scope As New TransactionScope(TransactionScopeOption.Required, options)
Using connection1 As New SqlConnection(aConnectionString1)
Using command1 As New SqlCommand(sql1, connection1)
Dim rowsUpdated1 As Integer = command1.ExecuteNonQuery()
If (rowsUpdated1 > 0) Then
Using connection2 As New SqlConnection(aConnectionString2)
Using command2 As New SqlCommand(sql2, connection2)
Dim rowsUpdated2 As Integer = command2.ExecuteNonQuery()
If (rowsUpdated2 > 0) Then
transactionScope.Complete()
End If
End Using
End Using

' Dispose the second command object.

' Dispose (close) the second connection.

End If
End Using
End Using
End Using

' Dispose the first command object.


' Dispose (close) the first connection.

' Dispose TransactionScope object, to commit or rollback transaction.

Unit 3: Performing Transactional Operations

87

[C#]
using System.Transactions;
...
TransactionOptions options = new TransactionOptions();
options.IsolationLevel = System.Transactions.IsolationLevel.ReadCommitted;
options.Timeout = new TimeSpan(0, 2, 0);
using (TransactionScope scope =
new TransactionScope(TransactionScopeOption.Required, options))
{
using (SqlConnection connection1 = new SqlConnection(aConnectionString1))
{
using (SqlCommand command1 = new SqlCommand(sql1, connection1))
{
int rowsUpdated1 = command1.ExecuteNonQuery();
if (rowsUpdated1 > 0)
{
using (SqlConnection connection2 =
new SqlConnection(aConnectionString2))
{
using (SqlCommand command2 = new SqlCommand(sql2, connection2))
{
int rowsUpdated2 = command2.ExecuteNonQuery();
if (rowsUpdated2 > 0)
{
transactionScope.Complete();
}
}
}

// Dispose the second command object.


// Dispose (close) the second connection.

}
}
}
}

// Dispose the first command object.


// Dispose (close) the first connection.

// Dispose TransactionScope object, to commit or rollback transaction.

Unit 4: Performing Disconnected


Operations Programmatically
Database Tables and Stored Procedures
Introduction
In the first phase of development of the Store Tracker application, you will use
the Sales.SalesPerson and Sales.Store tables in the Adventure Works database.
This resource document describes the Sales.SalesPerson and Sales.Store tables
and related stored procedures.

Sales.SalesPerson Table
The Sales.SalesPerson table in the Adventure Works database contains
information about salespeople in the Adventure Works organization. The
following table describes the schema of the Sales.SalesPerson table and
provides additional information that will help you decide how to represent the
data in a dataset table.
Column name

SQL data type

Description

SalesPersonID

int

Primary key of the salesperson

TerritoryID

int

Primary key

Read only

Auto increment column

Does not allow nulls

Foreign key to the SalesTerritoryID


column in the Sales.SalesTerritory
table

SalesQuota

money

Projected yearly sales

Bonus

CommissionPct

money

smallmoney

Allows nulls
Allows nulls

Bonus due if quota is met

Default value: 0.0

Does not allow nulls

Commission percent received per sale

Default value: 0.0

Does not allow nulls

90

Unit 4: Performing Disconnected Operations Programmatically


(continued)
Column name

SQL data type

Description

SalesYTD

money

Sales total year to date


Default value: 0.0
Does not allow nulls

SalesLastYear

money

Sales total of previous year


Default value: 0.0
Does not allow nulls

SalesGrowth

money

Sales growth to date in this


year
Expression column:
SalesYTD
SalesLastYear
Allows nulls

rowguid

uniqueidentifier

ROWGUIDCOL number
that uniquely identifies the
record (for merge
replication)
Read only
Allows nulls
Value automatically
assigned by database on
insert

ModifiedDate

datetime

Date and time the record


was updated
Read only
Allows nulls
Value automatically
assigned by database on
insert and update

You will use the following stored procedures to query and modify data in the
Sales.SalesPerson table:

uspGetSingleSalesPerson

uspGetSalesPersons

uspInsertSalesPerson

uspUpdateSalesPerson

uspDeleteSalesPerson

Unit 4: Performing Disconnected Operations Programmatically

91

uspGetSingleSalesPerson
This stored procedure returns a result set that contains details for a specific
salesperson. The stored procedure has a single parameter, as described in the
following table.
Parameter name

SQL data type

Description

@prmSalesPersonID

int

Primary key of the salesperson

uspGetSalesPersons
This stored procedure returns a result set that contains details for all
salespeople. The stored procedure does not have any parameters.

uspInsertSalesPerson
This stored procedure inserts a salesperson into the Sales.SalesPerson table.
The stored procedure has seven parameters, as described in the following table.
Parameter name

SQL data type

Description

@prmSalesPersonID

int

Primary key of the salesperson

@prmTerritoryID

int

Foreign key to the SalesTerritoryID


column in the Sales.SalesTerritory
table

@prmSalesQuota

money

Projected yearly sales

@prmBonus

money

Bonus due if quota is met

@prmCommissionPct

smallmoney

Commission percent received per sale

@prmSalesYTD

money

Sales total year to date

@prmSalesLastYear

money

Sales total of previous year

uspUpdateSalesPerson
This stored procedure updates an existing salesperson in the Sales.SalesPerson
table. The stored procedure has seven parameters, as described in the following
table.
Parameter name

SQL data type

Description

@prmSalesPersonID

int

Primary key of the salesperson

@prmTerritoryID

int

Foreign key to the SalesTerritoryID


column in the Sales.SalesTerritory table

@prmSalesQuota

money

Projected yearly sales

@prmBonus

money

Bonus due if quota is met

@prmCommissionPct

smallmoney

Commission percent received per sale

@prmSalesYTD

money

Sales total year to date

@prmSalesLastYear

money

Sales total of previous year

92

Unit 4: Performing Disconnected Operations Programmatically

uspDeleteSalesPerson
This stored procedure deletes an existing salesperson in the Sales.SalesPerson
table. The stored procedure has a single parameter, as described in the following
table.
Parameter name

SQL data type

Description

@prmSalesPersonID

int

Primary key of the salesperson

Sales.Store Table
The Sales.Store table in the Adventure Works database contains information
about stores that are customers of the Adventure Works products. The following
table describes the schema of the Sales.Store table and provides additional
information that will help you decide how to represent the data in a dataset
table.
Column name

SQL data type

Description

CustomerID

int

Primary key of the store (customer)

Name

SalesPersonID

nvarchar(50)

int

Primary key

Read only

Auto increment column

Does not allow nulls

Name of the store

Maximum length: 50

Does not allow nulls

Foreign key to the SalesPersonID


column in the Sales.SalesPerson table

Demographics

xml

Demographics information for the store

rowguid

ModifiedDate

uniqueidentifier

datetime

Allows nulls
Allows nulls

ROWGUIDCOL number that uniquely


identifies the record (for merge
replication)

Read only

Allows nulls

Value automatically assigned


by database on insert

Date and time the record was updated

Read only

Allows nulls

Value automatically assigned


by database on insert and
update

Unit 4: Performing Disconnected Operations Programmatically

93

You will use the following stored procedures to query and modify data in the
Sales.Store table:

uspGetSingleStore

uspGetStores

uspInsertStore

uspUpdateStore

uspDeleteStore

uspGetSingleStore
This stored procedure returns a result set that contains details for a specific
store. The stored procedure has a single parameter, as described in the following
table.
Parameter name

SQL data type

Description

@prmCustomerID

int

Primary key of the store (customer)

uspGetStores
This stored procedure returns a result set that contains details for all stores. The
stored procedure does not have any parameters.

uspInsertStore
This stored procedure inserts a store into the Sales.Store table. The stored
procedure has four parameters, as described in the following table.
Parameter name

SQL data type

Description

@prmCustomerID

int

Primary key of the store (customer)

@prmName

nvarchar(50)

Name of the store

@prmSalesPersonID

int

Foreign key to the SalesPersonID


column in the Sales.SalesPerson table

@prmDemographics

xml

Demographics information for the store

uspUpdateStore
This stored procedure updates an existing store in the Sales.Store table. The
stored procedure has four parameters, as described in the following table.
Parameter name

SQL data type

Description

@prmCustomerID

int

Primary key of the store (customer)

@prmName

nvarchar(50)

Name of the store

@prmSalesPersonID

int

Foreign key to the SalesPersonID


column in the Sales.SalesPerson table

@prmDemographics

xml

Demographics information for the store

94

Unit 4: Performing Disconnected Operations Programmatically

uspDeleteStore
This stored procedure deletes an existing store in the Sales.Store table. The
stored procedure has a single parameter, as described in the following table.
Parameter name

SQL data type

Description

@prmCustomerID

int

Primary key of the store (customer)

Unit 4: Performing Disconnected Operations Programmatically

95

How to Create DataSets and DataTables


This document is adapted from articles that you can find at the following
locations:
In the Microsoft Visual Studio 2005 documentation:

Creating a DataSet
mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.VisualSt
udio.v80.en/WD_ADONET/html/57629d8f-393e-4677-8b8329ffde27f5fc.htm

Adding a DataTable to a DataSet


mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.VisualSt
udio.v80.en/WD_ADONET/html/556d29a3-8fc9-4e38-b3eec188f7e7b155.htm

Defining the Schema of a DataTable


mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.VisualSt
udio.v80.en/WD_ADONET/html/efbcdda4-f5a9-421d-8be24c194c74552f.htm

Adding Columns to a Table


mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.VisualSt
udio.v80.en/WD_ADONET/html/e85c4a0e-4f3f-458c-b58b0ddbc06bf974.htm

Creating Expression Columns


mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.VisualSt
udio.v80.en/WD_ADONET/html/0af3bd64-92a2-4b47-ae62f5df35f131a6.htm

Creating AutoIncrement Columns


mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.VisualSt
udio.v80.en/WD_ADONET/html/cf09732a-ab54-4d98-89e24d0a1f28fbce.htm

Defining a Primary Key for a Table


mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.VisualSt
udio.v80.en/WD_ADONET/html/2ea85959-e763-4669-8bd946a9dab894bd.htm

96

Unit 4: Performing Disconnected Operations Programmatically

Creating a DataSet
You create an instance of a DataSet by calling the DataSet constructor. Specify
an optional name argument. If you do not specify a name for the DataSet, the
name is set to NewDataSet.
You can also create a new DataSet based on an existing DataSet. The new
DataSet can be an exact copy of the existing DataSet; a clone of the DataSet
that copies the relational structure or schema but does not contain any of the
data from the existing DataSet; or a subset of the DataSet, containing only the
modified rows from the existing DataSet by using the GetChanges method.
The following code example demonstrates how to construct an instance of a
DataSet.
[Visual Basic]
Dim custDS As DataSet = New DataSet("CustomerOrders")

[C#]
DataSet custDS = new DataSet("CustomerOrders");

Adding a DataTable to a DataSet


Microsoft ADO.NET enables you to create DataTable objects and add them to
an existing DataSet. You can add DataColumn objects to the Columns
collection of the DataTable. You can set constraint information for a
DataTable by using the PrimaryKey property of the DataTable and the
Unique property of the DataColumn objects.
The following example constructs a DataSet, adds a new DataTable object to
the DataSet, and then adds three DataColumn objects to the table. Finally, the
code sets one column as the primary key column.
[Visual Basic]
Dim custDS As DataSet = New DataSet("CustomerOrders")
Dim ordersTable As DataTable = custDS.Tables.Add("Orders")
Dim pkCol As DataColumn = _
ordersTable.Columns.Add("OrderID", Type.GetType("System.Int32"))
ordersTable.Columns.Add("OrderQuantity", Type.GetType("System.Int32"))
ordersTable.Columns.Add("CompanyName", Type.GetType("System.String"))
ordersTable.PrimaryKey = New DataColumn() {pkCol}

Unit 4: Performing Disconnected Operations Programmatically

97

[C#]
DataSet custDS = new DataSet("CustomerOrders");
DataTable ordersTable = custDS.Tables.Add("Orders");
DataColumn pkCol = ordersTable.Columns.Add("OrderID", typeof(Int32));
ordersTable.Columns.Add("OrderQuantity", typeof(Int32));
ordersTable.Columns.Add("CompanyName", typeof(string));
ordersTable.PrimaryKey = new DataColumn[] {pkCol};

Adding Columns to a Table


The schema, or structure, of a table is represented by columns and constraints.
You define the schema of a DataTable by using DataColumn objects as well
as ForeignKeyConstraint and UniqueConstraint objects. The columns in a
table can map to columns in a data source, contain calculated values from
expressions, automatically increment their values, or contain primary key
values.
A DataTable contains a collection of DataColumn objects referenced by the
Columns property of the table. This collection of columns, along with any
constraints, defines the schema, or structure, of the table.
You create DataColumn objects within a table by using the DataColumn
constructor, or by calling the Add method of the Columns property of the table,
which is a DataColumnCollection. The Add method will accept optional
ColumnName, DataType, and Expression arguments and will create a new
DataColumn as a member of the collection. It will also accept an existing
DataColumn object and will add it to the collection; in addition, it will return a
reference to the added DataColumn, if requested. Because DataTable objects
are not specific to any data source, Microsoft .NET Framework types are used
when specifying the data type of a DataColumn.
The following example adds four columns to a DataTable.
[Visual Basic]
Dim workTable As DataTable = New DataTable("Customers")
Dim workCol As DataColumn = _
workTable.Columns.Add("CustID", Type.GetType("System.Int32"))
workCol.AllowDBNull = False
workCol.Unique = True
workTable.Columns.Add("CustLName", Type.GetType("System.String"))
workTable.Columns.Add("CustFName", Type.GetType("System.String"))
workTable.Columns.Add("Purchases", Type.GetType("System.Double"))

98

Unit 4: Performing Disconnected Operations Programmatically

[C#]
DataTable workTable = new DataTable("Customers");
DataColumn workCol = workTable.Columns.Add("CustID", typeof(Int32));
workCol.AllowDBNull = false;
workCol.Unique = true;
workTable.Columns.Add("CustLName", typeof(String));
workTable.Columns.Add("CustFName", typeof(String));
workTable.Columns.Add("Purchases", typeof(Double));

In the preceding example, notice that the properties for the CustID column are
set to not allow DBNull values and to constrain values to be unique. However,
if you define the CustID column as the primary key column of the table, the
AllowDBNull property will automatically be set to false and the Unique
property will automatically be set to true.

Creating Expression Columns


You can define an expression for a column, enabling it to contain a value
calculated from other column values in the same row or from the column values
of multiple rows in the table. To define the expression to be evaluated, use the
Expression property of the target column, and use the ColumnName property
to refer to other columns in the expression. The DataType for the expression
column must be appropriate for the value the expression will return.
The following table lists several possible uses for expression columns in a table.
Expression type

Example

Comparison

Total >= 500

Computation

UnitPrice * Quantity

Aggregation

Sum(Price)

You can set the Expression property on an existing DataColumn object, or


you can include the property as the third argument passed to the DataColumn
constructor, as shown in the following example.
[Visual Basic]
workTable.Columns.Add("Total", Type.GetType("System.Double"))
workTable.Columns.Add("SalesTax", Type.GetType("System.Double"), "Total * 0.086")

[C#]
workTable.Columns.Add("Total", typeof(Double));
workTable.Columns.Add("SalesTax", typeof(Double), "Total * 0.086");

Expressions can reference other expression columns; however, a circular


reference, in which two expressions reference each other, will generate an
exception.

Unit 4: Performing Disconnected Operations Programmatically

99

Creating AutoIncrement Columns


To ensure that the values in a column are unique, you can set the column values
to increment automatically when new rows are added to the table. To create an
auto-incrementing DataColumn, set the AutoIncrement property of the
column to true. The DataColumn will then start with the value defined in the
AutoIncrementSeed property, and with each row added the value of the
AutoIncrement column will increase by the value held in the
AutoIncrementStep property of the column.
For AutoIncrement columns, it is recommended that the ReadOnly property
of the DataColumn be set to true.
The following example demonstrates how to create a column that starts with a
value of 200 and adds incrementally in steps of three.
[Visual Basic]
Dim workCol As DataColumn = _
workTable.Columns.Add("CustomerID", Type.GetType("System.Int32"))
workCol.AutoIncrement = True
workCol.AutoIncrementSeed = 200
workCol.AutoIncrementStep = 3

[C#]
DataColumn workCol = workTable.Columns.Add("CustomerID", typeof(Int32));
workCol.AutoIncrement = true;
workCol.AutoIncrementSeed = 200;
workCol.AutoIncrementStep = 3;

Defining a Primary Key for a Table


A database table commonly has a column, or group of columns, that uniquely
identifies each row in the table. This identifying column or group of columns is
called the primary key.
When you identify a single DataColumn as the PrimaryKey for a DataTable,
the table automatically sets the AllowDBNull property of the column to false
and the Unique property to true. For multiple-column primary keys, the
AllowDBNull property is automatically set to false, but the Unique property is
not set to true. However, a UniqueContraint corresponding to the primary key
is added to the Contraints collection of the DataTable.
The PrimaryKey property of a DataTable receives as its value an array of one
or more DataColumn objects, as shown in the following examples. The first
example defines a single column as the primary key.

100

Unit 4: Performing Disconnected Operations Programmatically

[Visual Basic]
workTable.PrimaryKey = New DataColumn() {workTable.Columns("CustID")}
' Or
Dim myColArray(1) As DataColumn
myColArray(0) = workTable.Columns("CustID")
workTable.PrimaryKey = myColArray

[C#]
workTable.PrimaryKey = new DataColumn[] {workTable.Columns["CustID"]};
// Or
DataColumn[] myColArray = new DataColumn[1];
myColArray[0] = workTable.Columns["CustID"];
workTable.PrimaryKey = myColArray;

The following example defines two columns as a primary key.


[Visual Basic]
workTable.PrimaryKey = New DataColumn() {workTable.Columns("CustLName"), _
workTable.Columns("CustFName")}
' Or
Dim myKey(2) As DataColumn
myKey(0) = workTable.Columns("CustLName")
myKey(1) = workTable.Columns("CustFName")
workTable.PrimaryKey = myKey

[C#]
workTable.PrimaryKey = new DataColumn[] {workTable.Columns["CustLName"],
workTable.Columns["CustFName"]};
// Or
DataColumn[] myKey = new DataColumn[2];
myKey[0] = workTable.Columns["CustLName"];
myKey[1] = workTable.Columns["CustFName"];
workTable.PrimaryKey = myKey;

Unit 4: Performing Disconnected Operations Programmatically

101

How to Define DataTable Constraints and Relations


This document is adapted from articles that you can find at the following
locations:
In the Microsoft Visual Studio 2005 documentation:

Adding Constraints to a Table


mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.VisualSt
udio.v80.en/WD_ADONET/html/27c9f2fd-f64d-4b4e-bbf61d24f47067cb.htm

Adding a Relationship Between Tables


mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.VisualSt
udio.v80.en/WD_ADONET/html/a4a564fb-c1c4-4135-b6c2b030e51195e4.htm

Adding Constraints to a Table


You can use Constraints to enforce restrictions on the data in a DataTable to
maintain the integrity of the data. A constraint is an automatic rule, applied to a
column or related columns, that determines the course of action when the value
of a row is somehow altered. Constraints are enforced when the
EnforceConstraints property of the DataSet is true.
There are two kinds of constraints in Microsoft ADO.NET: the
ForeignKeyConstraint and the UniqueConstraint. By default, both
constraints are created automatically when you create a relationship between
two or more tables by adding a DataRelation to the DataSet. However, you
can disable this behavior by specifying createConstraints = false when
creating the relation.

ForeignKeyConstraint
A ForeignKeyConstraint enforces rules about how updates and deletions to
related tables are propagated. For example, if a value in a row of one table is
updated or deleted, and that same value is also used in one or more related
tables, a ForeignKeyConstraint will determine what happens in the related
tables.
The DeleteRule and UpdateRule properties of the ForeignKeyConstraint
define the action to be taken when the user attempts to delete or update a row in
a related table. The following table describes the different settings available for
the DeleteRule and UpdateRule properties of the ForeignKeyConstraint.
Rule

Description

Cascade

Deletes or updates related rows.

SetNull

Sets values in related rows to DBNull.

SetDefault

Sets values in related rows to the default value.

None

Specifies that no action be taken on related rows. This is the


default rule.

102

Unit 4: Performing Disconnected Operations Programmatically

A ForeignKeyConstraint can restrict as well as propagate changes to related


columns. Depending on the properties set for the ForeignKeyConstraint of a
column, and if the EnforceConstraints property of the DataSet is true,
performing certain operations on the parent row will result in an exception. For
example, if the DeleteRule property of the ForeignKeyConstraint is set to
None, a parent row cannot be deleted if it has any child rows.
You can create a foreign key constraint between single columns or between an
array of columns by using the ForeignKeyConstraint constructor. Pass the
resulting ForeignKeyConstraint object to the Add method of the tables
Constraints property, which is a ConstraintCollection. You can also pass
constructor arguments to several overloads of the Add method of a
ConstraintCollection to create a ForeignKeyConstraint.
When creating a ForeignKeyConstraint, you can pass the DeleteRule and
UpdateRule values to the constructor as arguments, or you can set them as
properties, as in the following example (where the DeleteRule value is set to
None).
[Visual Basic]
Dim custOrderFK As ForeignKeyConstraint = New ForeignKeyConstraint("CustOrderFK", _
custDS.Tables("CustTable").Columns("CustomerID"), _
custDS.Tables("OrdersTable").Columns("CustomerID"))
custOrderFK.DeleteRule = Rule.None
custDS.Tables("OrdersTable").Constraints.Add(custOrderFK)

[C#]
ForeignKeyConstraint custOrderFK = new ForeignKeyConstraint("CustOrderFK",
custDS.Tables["CustTable"].Columns["CustomerID"],
custDS.Tables["OrdersTable"].Columns["CustomerID"]);
custOrderFK.DeleteRule = Rule.None;
custDS.Tables["OrdersTable"].Constraints.Add(custOrderFK);

Changes to rows can be accepted by using the AcceptChanges method or


canceled by using the RejectChanges method of the DataSet, DataTable, or
DataRow. When a DataSet contains ForeignKeyConstraints, invoking the
AcceptChanges or RejectChanges methods causes the AcceptRejectRule to
be enforced. The AcceptRejectRule property of the ForeignKeyConstraint
determines which action will be taken on the child rows when AcceptChanges
or RejectChanges is called on the parent row.
The following table lists the values to which the AcceptRejectRule can be set.
Action

Description

Cascade

Accepts or rejects changes to child rows.

None

Specifies that no action be taken on child rows. This is the


default action.

Unit 4: Performing Disconnected Operations Programmatically

103

UniqueConstraint
The UniqueConstraint object, which can be applied either to a single column
or to an array of columns in a DataTable, ensures that all data in the specified
column or columns is unique across the rows of the table. You can create a
unique constraint for a column or array of columns by using the
UniqueConstraint constructor. Pass the resulting UniqueConstraint object to
the Add method of the tables Constraints property, which is a
ConstraintCollection. You can also pass constructor arguments to several
overloads of the Add method of a ConstraintCollection to create a
UniqueConstraint. When creating a UniqueConstraint for a column or
columns, you can optionally specify whether the column or columns are a
primary key.
You can also create a unique constraint for a column by setting the Unique
property of the column to true. Alternatively, setting the Unique property of a
single column to false removes any unique constraint that may exist. Defining a
column or columns as the primary key for a table will automatically create a
unique constraint for the specified column or columns. If you remove a column
from the PrimaryKey property of a DataTable, the UniqueConstraint is
removed.
The following example creates a UniqueConstraint for two columns of a
DataTable.
[Visual Basic]
Dim custTable As DataTable = custDS.Tables("Customers")
Dim custUC As UniqueConstraint = _
New UniqueConstraint(New DataColumn()
{custTable.Columns("CustomerID"), _
custTable.Columns("CompanyName")})
custDS.Tables("Customers").Constraints.Add(custUC)

[C#]
DataTable custTable = custDS.Tables["Customers"];
UniqueConstraint custUC = new UniqueConstraint(new DataColumn[]
{custTable.Columns["CustomerID"],
custTable.Columns["CompanyName"]});
custDS.Tables["Customers"].Constraints.Add(custUC);

Adding a Relationship Between Tables


In a DataSet that contains multiple DataTable objects, you can use
DataRelation objects to relate one table to another, to navigate through the
tables, and to return child or parent rows from a related table.
The arguments required to create a DataRelation are a name for the
DataRelation being created and an array of one or more DataColumn
references to the columns that serve as the parent and child columns in the
relationship. After you have created a DataRelation, you can use it to navigate
between tables and to retrieve values.
Adding a DataRelation to a DataSet adds, by default, a UniqueConstraint to
the parent table and a ForeignKeyConstraint to the child table.

104

Unit 4: Performing Disconnected Operations Programmatically

The following code example creates a DataRelation using two DataTable


objects in a DataSet. Each DataTable contains a column named CustID,
which serves as a link between the two DataTable objects. The example adds a
single DataRelation to the Relations collection of the DataSet. The first
argument in the example specifies the name of the DataRelation being created.
The second argument sets the parent DataColumn and the third argument sets
the child DataColumn.
[Visual Basic]
custDS.Relations.Add("CustOrders", _
custDS.Tables("Customers").Columns("CustID"), _
custDS.Tables("Orders").Columns("CustID"))

[C#]
custDS.Relations.Add("CustOrders",
custDS.Tables["Customers"].Columns["CustID"],
custDS.Tables["Orders"].Columns["CustID"]);

Unit 4: Performing Disconnected Operations Programmatically

105

How to Create and Configure a Data Adapter


This document is adapted from articles that you can find at the following
locations:
In the Microsoft Visual Studio 2005 documentation:

Populating a DataSet from a DataAdapter


mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.VisualSt
udio.v80.en/WD_ADONET/html/3fa0ac7d-e266-4954-bfac3fbe2f913153.htm

Using Parameters with a DataAdapter


mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.VisualSt
udio.v80.en/WD_ADONET/html/f21e6aba-b76d-46ad-a83e2ad8e0af1e12.htm

Introduction to DataAdapters
The Microsoft ADO.NET DataSet is a memory-resident representation of data
that provides a consistent relational programming model independent of the
data source. The DataSet represents a complete set of data including tables,
constraints, and relationships among the tables. Because the DataSet is
independent of the data source, a DataSet can include data local to the
application, as well as data from multiple data sources. Interaction with existing
data sources is controlled through the DataAdapter.
Each Microsoft .NET Framework data provider included with the .NET
Framework has a DataAdapter object: The .NET Framework Data Provider for
Microsoft SQL Server includes a SqlDataAdapter object, the .NET
Framework Data Provider for OLE DB includes an OleDbDataAdapter object,
the .NET Framework Data Provider for ODBC includes an OdbcDataAdapter
object, and the .NET Framework Data Provider for Oracle includes an
OracleDataAdapter object.
A DataAdapter is used to retrieve data from a data source and populate tables
within a DataSet. The DataAdapter also persists changes made to the DataSet
back to the data source. The DataAdapter uses the Connection object of the
.NET Framework data provider to connect to a data source and Command
objects to retrieve data from and persist changes to the data source.

106

Unit 4: Performing Disconnected Operations Programmatically

Configuring a DataAdapter
The DataAdapter has four properties that are used to retrieve data from and
persist data to the data source. The SelectCommand returns data from the data
source. The InsertCommand, UpdateCommand, and DeleteCommand are
used to manage changes at the data source.
The SelectCommand property must be set before calling the Fill method of the
DataAdapter. The InsertCommand, UpdateCommand, and/or
DeleteCommand properties must be set before the Update method of the
DataAdapter is called, depending on what changes were made to the data in
the DataSet. For example, if rows have been added, the InsertCommand must
be set before calling Update. When Update is processing an inserted, updated,
or deleted row, the DataAdapter uses the respective Command property to
process the action. Current information about the modified row is passed to the
Command object through the Parameters collection.
The following example shows sample SQL statements to be used as the
CommandText for the SelectCommand, InsertCommand,
UpdateCommand, and DeleteCommand properties of a SqlDataAdapter
object.
[Visual Basic]
Dim selectSQL As String = _
"SELECT CustomerID, CompanyName & _
FROM Customers & _
WHERE Country = @Country AND City = @City"
Dim insertSQL As String = _
"INSERT INTO Customers (CustomerID, CompanyName) " & _
"VALUES (@CustomerID, @CompanyName)"
Dim updateSQL As String = _
"UPDATE Customers & _
SET CustomerID = @CustomerID, CompanyName = @CompanyName " & _
"WHERE CustomerID = @OldCustomerID"
Dim deleteSQL As String = _
"DELETE FROM Customers WHERE CustomerID = @CustomerID"

Unit 4: Performing Disconnected Operations Programmatically

107

[C#]
string selectSQL =
"SELECT CustomerID, CompanyName +
FROM Customers +
WHERE Country = @Country AND City = @City";
string insertSQL =
"INSERT INTO Customers (CustomerID, CompanyName) " +
"VALUES (@CustomerID, @CompanyName)";
string updateSQL =
"UPDATE Customers +
SET CustomerID = @CustomerID, CompanyName = @CompanyName " +
"WHERE CustomerID = @OldCustomerID";
string deleteSQL =
"DELETE FROM Customers WHERE CustomerID = @CustomerID";

The following code example creates the parameters for the SELECT statement
from the preceding example and fills a DataSet.
[Visual Basic]
Dim nwindConn As SqlConnection = New SqlConnection( _
"Data Source=localhost;Integrated Security=SSPI;Initial Catalog=northwind")
Dim custDA As SqlDataAdapter = New SqlDataAdapter
Dim selectCMD AS SqlCommand = New SqlCommand(selectSQL, nwindConn)
custDA.SelectCommand = selectCMD
' Add parameters and set values.
selectCMD.Parameters.Add("@Country", SqlDbType.NVarChar, 15).Value = "UK"
selectCMD.Parameters.Add("@City", SqlDbType.NVarChar, 15).Value = "London"
Dim custDS As DataSet = New DataSet
custDA.Fill(custDS, "Customers")

[C#]
SqlConnection nwindConn = new SqlConnection(
"Data Source=localhost;Integrated Security=SSPI;Initial Catalog=northwind");
SqlDataAdapter custDA = new SqlDataAdapter();
SqlCommand selectCMD = new SqlCommand(selectSQL, nwindConn);
custDA.SelectCommand = selectCMD;
// Add parameters and set values.
selectCMD.Parameters.Add("@Country", SqlDbType.NVarChar, 15).Value = "UK";
selectCMD.Parameters.Add("@City", SqlDbType.NVarChar, 15).Value = "London";
DataSet custDS = new DataSet();
custDA.Fill(custDS, "Customers");

108

Unit 4: Performing Disconnected Operations Programmatically

Specifying the Source Column and Source Version


The SourceColumn and SourceVersion may be passed as arguments to the
Parameter constructor or set as properties of an existing Parameter. The
SourceColumn is the name of the DataColumn from the DataRow where the
value of the Parameter will be retrieved. The SourceVersion specifies which
DataRow version the DataAdapter uses to retrieve the value.
The following table shows the DataRowVersion enumeration values available
for use with SourceVersion.
Member name

Description

Current

The parameter uses the current value of the column. This is the
default.

Default

The parameter uses the DefaultValue of the column.

Original

The parameter uses the original value of the column.

Proposed

The parameter uses a proposed value.

The following code example defines an UPDATE statement in which the


CustomerID column is used as a SourceColumn for two parameters:
@CustomerID (SET CustomerID = @CustomerID) and @OldCustomerID
(WHERE CustomerID = @OldCustomerID). The @CustomerID parameter
is used to update the CustomerID column to the current value in the DataRow.
As a result, the CustomerID SourceColumn with a SourceVersion of
Current is used. The @OldCustomerID parameter is used to identify the
current row in the data source. Because the matching column value is found in
the Original version of the row, the same SourceColumn (CustomerID) with
a SourceVersion of Original is used.
[Visual Basic]
custDA.UpdateCommand.Parameters.Add( _
"@CustomerID", SqlDbType.NChar, 5, "CustomerID")
custDA.UpdateCommand.Parameters.Add( _
"@CompanyName", SqlDbType.NVarChar, 40, "CompanyName")
Dim myParm As SqlParameter = custDA.UpdateCommand.Parameters.Add( _
"@OldCustomerID", SqlDbType.NChar, 5, "CustomerID")
myParm.SourceVersion = DataRowVersion.Original

[C#]
custDA.UpdateCommand.Parameters.Add(
"@CustomerID", SqlDbType.NChar, 5, "CustomerID");
custDA.UpdateCommand.Parameters.Add(
"@CompanyName", SqlDbType.NVarChar, 40, "CompanyName");
SqlParameter myParm = custDA.UpdateCommand.Parameters.Add(
"@OldCustomerID", SqlDbType.NChar, 5, "CustomerID");
myParm.SourceVersion = DataRowVersion.Original;

Unit 4: Performing Disconnected Operations Programmatically

109

How to Load and Save Data in a DataSet


This document is adapted from articles that you can find at the following
locations:
In the Microsoft Visual Studio 2005 documentation:

Populating a DataSet from a DataAdapter


mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.VisualSt
udio.v80.en/WD_ADONET/html/3fa0ac7d-e266-4954-bfac3fbe2f913153.htm

Updating the Database with a DataAdapter and the DataSet


mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.VisualSt
udio.v80.en/WD_ADONET/html/d1bd9a8c-0e29-40e3-bda8d89176b72fb1.htm

Populating a DataSet from a DataAdapter


The Fill method of the DataAdapter is used to populate a DataSet with the
results of the SelectCommand of the DataAdapter. Fill takes as its arguments
a DataSet to be populated and a DataTable object, or the name of the
DataTable to be filled with the rows returned from the SelectCommand.
The Fill method uses the DataReader object internally to return the column
names and types used to create the tables in the DataSet, as well as the data to
populate the rows of the tables in the DataSet. Tables and columns are only
created if they do not already exist; otherwise, Fill uses the existing DataSet
schema.
The following code example creates an instance of a DataAdapter that uses a
Connection to the Microsoft SQL Server Northwind database and populates a
DataTable in a DataSet with the list of customers. The SQL statement and
Connection arguments passed to the DataAdapter constructor are used to
create the SelectCommand property of the DataAdapter.
[Visual Basic]
Dim nwindConn As SqlConnection = New SqlConnection( _
"Data Source=localhost; Integrated Security=SSPI; Initial Catalog=northwind")
Dim selectCMD As SqlCommand = New SqlCommand( _
"SELECT CustomerID, CompanyName FROM Customers", nwindConn)
Dim custDA As SqlDataAdapter = New SqlDataAdapter()
custDA.SelectCommand = selectCMD
nwindConn.Open()
Dim custDS As DataSet = New DataSet()
custDA.Fill(custDS, "Customers")
nwindConn.Close()

110

Unit 4: Performing Disconnected Operations Programmatically

[C#]
SqlConnection nwindConn = new SqlConnection(
"Data Source=localhost; Integrated Security=SSPI; Initial Catalog=northwind");
SqlCommand selectCMD = new SqlCommand(
"SELECT CustomerID, CompanyName FROM Customers", nwindConn);
SqlDataAdapter custDA = new SqlDataAdapter();
custDA.SelectCommand = selectCMD;
nwindConn.Open();
DataSet custDS = new DataSet();
custDA.Fill(custDS, "Customers");
nwindConn.Close();

The Fill method implicitly opens the Connection that the DataAdapter is
using if it finds that the connection is not already open. If Fill opened the
connection, it will also close the connection when Fill is finished. This can
simplify your code when dealing with a single operation such as a Fill or an
Update. However, if you are performing multiple operations that require an
open connection, you can improve the performance of your application by
explicitly calling the Open method of the Connection, performing the
operations against the data source, and then calling the Close method of the
Connection. You should strive to keep connections to the data source open for
a minimal amount of time to free up the resource to be used by other client
applications.

Populating a DataSet from Multiple DataAdapters


Any number of DataAdapters can be used in conjunction with a DataSet. Each
DataAdapter can be used to fill one or more DataTable objects and persist
updates back to the relevant data source. DataRelation and Constraint objects
can be added to the DataSet locally, enabling you to relate data from multiple
dissimilar data sources. For example, a DataSet can contain data from a SQL
Server database, an IBM DB2 database exposed via OLE DB, and a data source
that streams Extensible Markup Language (XML). One or more DataAdapter
objects can handle communication to each data source.
The following code example populates a list of customers from the Northwind
database on SQL Server 2000 and a list of orders from the Northwind database
stored in Microsoft Access 2000. Constraint checking is disabled during the Fill
operations.

Unit 4: Performing Disconnected Operations Programmatically

111

[Visual Basic]
Dim custConn As SqlConnection= New SqlConnection( _
"Data Source=localhost;Integrated Security=SSPI; Initial Catalog=northwind;")
Dim custDA As SqlDataAdapter = New SqlDataAdapter( _
"SELECT * FROM Customers", custConn)

Dim orderConn As OleDbConnection = New OleDbConnection( _


"Provider=Microsoft.Jet.OLEDB.4.0; & _
Data Source=c:\Program Files\Microsoft Office\Office\Samples\northwind.mdb;")
Dim orderDA As OleDbDataAdapter = New OleDbDataAdapter( _
"SELECT * FROM Orders", orderConn)

custConn.Open()
orderConn.Open()
Dim custDS As DataSet = New DataSet()
custDS.EnforceConstraints = False
custDA.Fill(custDS, "Customers")
orderDA.Fill(custDS, "Orders")
custDS.EnforceConstraints = True
custConn.Close()
orderConn.Close()

112

Unit 4: Performing Disconnected Operations Programmatically

[C#]
SqlConnection custConn = new SqlConnection(
"Data Source=localhost;Integrated Security=SSPI;Initial Catalog=northwind;");
SqlDataAdapter custDA = new SqlDataAdapter(
"SELECT * FROM Customers", custConn);
OleDbConnection orderConn = new OleDbConnection(
@"Provider=Microsoft.Jet.OLEDB.4.0; " +
@"Data Source=c:\Program Files\Microsoft " +
@"Office\Office\Samples\northwind.mdb;");
OleDbDataAdapter orderDA = new OleDbDataAdapter(
"SELECT * FROM Orders", orderConn);
custConn.Open();
orderConn.Open();
DataSet custDS = new DataSet();
custDS.EnforceConstraints = false;
custDA.Fill(custDS, "Customers");
orderDA.Fill(custDS, "Orders");
custDS.EnforceConstraints = true;
custConn.Close();
orderConn.Close();

Updating the Database with a DataAdapter and the DataSet


The Update method of the DataAdapter is called to resolve changes from a
DataSet back to the data source. The Update method, like the Fill method,
takes as arguments an instance of a DataSet and an optional DataTable object
or DataTable name. The DataSet instance is the DataSet that contains the
changes that have been made, and the DataTable identifies the table from
which to retrieve the changes.
When you call the Update method, the DataAdapter analyzes the changes that
have been made and executes the appropriate command (INSERT, UPDATE, or
DELETE). When the DataAdapter encounters a change to a DataRow, it uses
the InsertCommand, UpdateCommand, or DeleteCommand to process the
change. You must explicitly set the commands before calling Update. If
Update is called and the appropriate command does not exist for a particular
update (for example, no DeleteCommand for deleted rows), an exception will
be thrown.
Command parameters can be used to specify input and output values for an
SQL statement or a stored procedure for each modified row in a DataSet.
The Update method will resolve your changes back to the data source;however,
other clients may have modified data at the data source since the last time you
filled the DataSet. To refresh your DataSet with current data, use the
DataAdapter and Fill the DataSet again. New rows will be added to the table,
and updated information will be incorporated into existing rows. The Fill

Unit 4: Performing Disconnected Operations Programmatically

113

method determines whether a new row will be added or an existing row will be
updated by examining the primary key values of the rows in the DataSet and
the rows returned by the SelectCommand. If the Fill method encounters a
primary key value for a row in the DataSet that matches a primary key value
from a row in the results returned by the SelectCommand, the method updates
the existing row with the information from the row returned by the
SelectCommand and sets the RowState of the existing row to Unchanged. If a
row returned by the SelectCommand has a primary key value that does not
match any of the primary key values of the rows in the DataSet, the Fill method
adds a new row with a RowState of Unchanged.
Calling AcceptChanges on the DataSet, DataTable, or DataRow will cause
all Original values for a DataRow to be overwritten with the Current values
for the DataRow. If the field values that identify the row as unique have been
modified, the Original values will no longer match the values in the data source
after AcceptChanges is called.
The following examples demonstrate how to perform updates to modified rows
by explicitly setting the UpdateCommand of the DataAdapter. Notice that the
parameter specified in the WHERE clause of the UPDATE statement is set to
use the Original value of the SourceColumn. Using the Original value is
important because the Current value may have been modified and may not
match the value in the data source. The Original value is the value that was
used to populate the DataTable from the data source.
[Visual Basic]
Dim catDA As SqlDataAdapter = New SqlDataAdapter( _
"SELECT CategoryID, CategoryName FROM Categories", nwindConn)
catDA.UpdateCommand = New SqlCommand( _
"UPDATE Categories & _
SET CategoryName = @CategoryName " & _
"WHERE CategoryID = @CategoryID", nwindConn)
catDA.UpdateCommand.Parameters.Add( _
"@CategoryName", SqlDbType.NVarChar, 15, "CategoryName")
Dim workParm As SqlParameter = catDA.UpdateCommand.Parameters.Add( _
"@CategoryID", SqlDbType.Int)
workParm.SourceColumn = "CategoryID"
workParm.SourceVersion = DataRowVersion.Original
Dim catDS As DataSet = New DataSet()
catDA.Fill(catDS, "Categories")
Dim cRow As DataRow = catDS.Tables("Categories").Rows(0)
cRow("CategoryName") = "New Category"
catDA.Update(catDS, "Categories")

114

Unit 4: Performing Disconnected Operations Programmatically

[C#]
SqlDataAdapter catDA = new SqlDataAdapter(
"SELECT CategoryID, CategoryName FROM Categories", nwindConn);
catDA.UpdateCommand = new SqlCommand(
"UPDATE Categories +
SET CategoryName = @CategoryName " +
"WHERE CategoryID = @CategoryID", nwindConn);
catDA.UpdateCommand.Parameters.Add(
"@CategoryName", SqlDbType.NVarChar, 15, "CategoryName");
SqlParameter workParm = catDA.UpdateCommand.Parameters.Add(
"@CategoryID", SqlDbType.Int);
workParm.SourceColumn = "CategoryID";
workParm.SourceVersion = DataRowVersion.Original;
DataSet catDS = new DataSet();
catDA.Fill(catDS, "Categories");
DataRow cRow = catDS.Tables["Categories"].Rows[0];
cRow["CategoryName"] = "New Category";
catDA.Update(catDS, "Categories");

AutoIncrement Columns
If the tables from your data source have auto-incrementing columns, you can
fill the columns in your DataSet with the values generated by the data source.
You do this either by returning the auto-increment value as an output parameter
of a stored procedure and mapping that value to a column in a table or by using
the RowUpdated event of the DataAdapter.
However, the values in your DataSet can become out-of-sync with the values at
the data source and result in unexpected behavior. For example, consider a table
with an auto-incrementing primary key column of CustomerID. If you add two
new customers within the DataSet, they receive auto-incremented CustomerId
values of 1 and 2. When the second customer row is passed to the Update
method of the DataAdapter, the newly added row receives an autoincremented CustomerID value of 1 at the data source, which does not match
the value, which is 2, in the DataSet. When the DataAdapter fills the row in
the DataSet with the returned value, a constraint violation occurs because the
first customer row already has a CustomerID of 1.
To avoid this behavior, it is recommended that, when working with autoincrementing columns at a data source and auto-incrementing columns in a
DataSet, you create the column in the DataSet with an AutoIncrementStep of
-1 and an AutoIncrementSeed of 0, as well as ensuring that your data source
generates auto-incrementing identity values starting from 1 and incrementing
with a positive step value. As a result, the DataSet will generate negative
numbers for auto-incremented values that will not conflict with the positive
auto-increment values generated by the data source. Another option is to use
columns of type Guid instead of auto-incrementing columns.

Unit 4: Performing Disconnected Operations Programmatically

115

Ordering of Inserts, Updates, and Deletes


In many circumstances, the order in which changes made through the DataSet
are sent to the data source is important. You can use the GetChanges method of
a DataSet to produce a second DataSet object that contains only the changes
introduced into the original data source. You can pass a DataRowState
parameter to specify the type of changes the new object should contain. You
can then pass the new DataSet object to the Update method of the
DataAdapter to process the modified rows. By specifying a subset of rows to
be updated, you can control the order in which inserts, updates, and deletes are
processed.
For example, the following code ensures that the deleted rows of the table are
processed first, then the updated rows, and then the inserted rows.
[Visual Basic]
' First process deletes.
Dim deletedDataSet as DataSet = custDS.GetChanges(DataRowState.Deleted)
If Not deletedDataSet Is Nothing Then
custDA.Update(deletedDataSet)
End If
' Next process updates.
Dim modifiedDataSet as DataSet = custDS.GetChanges(DataRowState.Modified)
If Not modifiedDataSet Is Nothing Then
custDA.Update(modifiedDataSet)
End If
' Finally, process inserts.
Dim addedDataSet as DataSet = custDS.GetChanges(DataRowState.Added)
If Not addedDataSet Is Nothing Then
custDA.Update(addedDataSet)
End If

116

Unit 4: Performing Disconnected Operations Programmatically

[C#]
// First process deletes.
DataSet deletedDataSet = custDS.GetChanges(DataRowState.Deleted);
if (deletedDataSet != null)
{
custDA.Update(deletedDataSet);
}
// Next process updates.
DataSet modifiedDataSet = custDS.GetChanges(DataRowState.Modified);
if (modifiedDataSet != null)
{
custDA.Update(modifiedDataSet);
}
// Finally, process inserts.
DataSet addedDataSet = custDS.GetChanges(DataRowState.Added);
if (addedDataSet != null)
{
custDA.Update(addedDataSet);
}

Rejecting Changes to a DataSet


You can use the RejectChanges method of a DataSet to discard any changes
that have been made to the DataSet after the most recent call to
AcceptChanges. When you call RejectChanges, new rows are removed and
modified, and deleted rows return back to their original state.
The following examples show how to reject changes on a DataSet.
[Visual Basic]
custDS.RejectChanges()

[C#]
custDS.RejectChanges();

Unit 4: Performing Disconnected Operations Programmatically

117

How to View and Modify Data in a DataTable


This document is adapted from articles that you can find at the following
locations:
In the Microsoft Visual Studio 2005 documentation:

DataRow Class
mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.NETDE
VFX.v20.en/cpref/html/T_System_Data_DataRow.htm

DataRowCollection Class
mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.NETDE
VFX.v20.en/cpref/html/T_System_Data_DataRowCollection.htm

DataRowCollection.Find Method (Object)


mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.NETDE
VFX.v20.en/cpref/html/M_System_Data_DataRowCollection_Find_1_b4c5
a2da.htm

Adding Data to a Table


mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.VisualSt
udio.v80.en/WD_ADONET/html/d6aa8474-7bde-48f7-949d20dc38a1625b.htm

Deleting a Row from a Table


mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.VisualSt
udio.v80.en/WD_ADONET/html/c34f531d-4b9b-4071-b2d7342c402aa586.htm

How to: View Data in a DataTable


The DataRow and DataColumn objects are primary components of a
DataTable. Use the DataRow object and its properties and methods to retrieve
and evaluate as well as insert, delete, and update the values in the DataTable.
The DataRowCollection represents the actual DataRow objects in the
DataTable, and the DataColumnCollection contains the DataColumn objects
that describe the schema of the DataTable. Use the overloaded Item property
to return or set the value of a DataColumn within a row.
Use the HasVersion and IsNull properties on a DataRow to determine the
status of a particular row value and the RowState property to determine the
state of the row in relation to its parent DataTable.
The following example prints the value of column 2 for every row in a
DataRowCollection. You can also access columns by name or by
DataColumn object.

118

Unit 4: Performing Disconnected Operations Programmatically

[Visual Basic]
' Print the number of rows in the collection.
Console.WriteLine(table.Rows.Count)
Dim row As DataRow
' Print the value of column 2 in each row.
For Each row In table.Rows
Console.WriteLine(row(2))
Next

[C#]
// Print the number of rows in the collection.
Console.WriteLine(table.Rows.Count);
// Print the value of column 2 in each row.
foreach(DataRow row in table.Rows)
{
Console.WriteLine(row[2]);
}

How to: Find a Row in a DataTable


You can find a row in a table by using the Find method, which is defined in the
DataRowCollection class. The Find method gets the row specified by the
primary key value.
To use the Find method, the DataTable object to which the
DataRowCollection object belongs must have at least one column designated
as a primary key column.
The following example uses the Find method to find a particular primary key
value in a collection of DataRow objects. The method returns the specific
DataRow object allowing you to change its values as needed.
[Visual Basic]
Private Sub FindInPrimaryKeyColumn(ByVal table As DataTable, _
ByVal pkValue As Long)
' Find the number pkValue in the primary key column of the table.
Dim foundRow As DataRow = table.Rows.Find(pkValue)
' Print the value of column 2 of the found row.
If Not (foundRow Is Nothing) Then
Console.WriteLine(foundRow(2).ToString())
End If
End Sub

Unit 4: Performing Disconnected Operations Programmatically

119

[C#]
private void FindInPrimaryKeyColumn(DataTable table,
long pkValue)
{
// Find the number pkValue in the primary key column of the table.
DataRow foundRow = table.Rows.Find(pkValue);
// Print the value of column 2 of the found row.
if (foundRow != null)
Console.WriteLine(foundRow[2].ToString());
}

How to: Add a Row to a DataTable


After you create a DataTable and define its structure by using columns and
constraints, you can add new rows of data to the table. To add a new row,
declare a new variable as type DataRow. A new DataRow object is returned
when you call the NewRow method of a DataTable object. The DataTable
then creates the DataRow object based on the structure of the table, as defined
by the DataColumnCollection.
The following example demonstrates how to create a new row by calling the
NewRow method, manipulate the newly added row by using an index or the
column name, and then call the Add method to add the row to the
DataRowCollection.

[Visual Basic]
Dim workRow As DataRow = workTable.NewRow()
workRow("CustLName") = "Smith"
workRow(1) = "Smith"
workTable.Rows.Add(workRow)

[C#]
DataRow workRow = workTable.NewRow();
workRow["CustLName"] = "Smith";
workRow[1] = "Smith";
workTable.Rows.Add(workRow);

120

Unit 4: Performing Disconnected Operations Programmatically

You can also call the Add method of the DataRowCollection class to add a
new row by passing in an array of values, typed as Object, as shown in the
following example.
[Visual Basic]
workTable.Rows.Add(new Object() {1, "Smith"})

[C#]
workTable.Rows.Add(new Object[] {1, "Smith"});

Passing an array of values, typed as Object, to the Add method creates a new
row inside the table and sets its column values to the values in the object array.
Note that values in the array are matched sequentially to the columns, based on
the order in which the values appear in the table.
The following example adds ten rows to the table.
[Visual Basic]
Dim workRow As DataRow
Dim I As Integer
For I = 0 To 9
workRow = workTable.NewRow()
workRow(0) = I
workRow(1) = "CustName" & I.ToString()
workTable.Rows.Add(workRow)
Next

[C#]
DataRow workRow;
for (int i = 0; i <= 9; i++)
{
workRow = workTable.NewRow();
workRow[0] = i;
workRow[1] = "CustName" + i.ToString();
workTable.Rows.Add(workRow);
}

Unit 4: Performing Disconnected Operations Programmatically

121

How to: Delete a Row in a DataTable


There are two methods that you can use to delete a DataRow object from a
DataTable object: the Remove method of the DataRowCollection object and
the Delete method of the DataRow object. While the Remove method deletes a
DataRow from the DataRowCollection, the Delete method only marks the row
for deletion. The actual removal occurs when the application calls the
AcceptChanges method. By using Delete, you can programmatically check
which rows are marked for deletion before actually removing them. When a
row is marked for deletion, its RowState property is set to Deleted.
When using a DataSet or DataTable in conjunction with a DataAdapter and a
relational data source, you will usually want to use the Delete method of the
DataRow to remove the row. The Delete method marks the row as Deleted in
the DataSet or DataTable, but it does not remove it. Instead, when the
DataAdapter encounters a row marked as Deleted, it executes its
DeleteCommand to delete the row at the data source. The row can then be
permanently removed by using the AcceptChanges method. If you use
Remove to delete the row, the row will be removed entirely from the table but
the DataAdapter will not delete the row at the data source.
The Remove method of the DataRowCollection takes a DataRow as an
argument and removes it from the collection, as shown in the following
example.
[Visual Basic]
workTable.Rows.Remove(workRow)

[C#]
workTable.Rows.Remove(workRow);

In contrast, the following example demonstrates how to call the Delete method
on a DataRow to change its RowState to Deleted.
[Visual Basic]
workRow.Delete()

[C#]
workRow.Delete();

If a row is marked for deletion and you call the AcceptChanges method of the
DataTable object, the row is removed from the DataTable. In contrast, if you
call RejectChanges, the RowState of the row reverts to what it was before
being marked as Deleted.
If the RowState of a DataRow is Added, meaning it has just been added to the
table, and it is then marked as Deleted, it is immediately removed from the
table.

122

Unit 4: Performing Disconnected Operations Programmatically

How to Navigate Relations Between DataTables


This document is adapted from an article that you can find at the following
location:
In the Microsoft Visual Studio 2005 documentation:

Navigating a Relationship Between Tables


mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.VisualSt
udio.v80.en/WD_ADONET/html/e5e673f4-9b44-45ae-aaeac504d1cc5d3e.htm

How to: Navigate Relations Between DataTables


One of the primary functions of a DataRelation is to allow navigation from one
DataTable to another within a DataSet. This navigation capability allows you
to retrieve all the related DataRow objects in one DataTable when given a
single DataRow from a related DataTable. For example, after establishing a
DataRelation between a table of customers and a table of orders, you can
retrieve all the order rows for a particular customer row by using
DataRow.GetChildRows.
The following code example creates a DataRelation between the Customers
table and the Orders table of a DataSet and returns all the orders for each
customer.
[Visual Basic]
Dim custOrderRel As DataRelation = custDS.Relations.Add( _
"CustOrders", _
custDS.Tables("Customers").Columns("CustomerID"), _
custDS.Tables("Orders").Columns("CustomerID"))
Dim custRow As DataRow
Dim orderRow As DataRow
For Each custRow in custDS.Tables("Customers").Rows
Console.WriteLine(custRow("CustomerID"))
For Each orderRow in custRow.GetChildRows(custOrderRel)
Console.WriteLine(orderRow("OrderID"))
Next
Next

Unit 4: Performing Disconnected Operations Programmatically

123

[C#]
DataRelation custOrderRel = custDS.Relations.Add(
"CustOrders",
custDS.Tables["Customers"].Columns["CustomerID"],
custDS.Tables["Orders"].Columns["CustomerID"]);
foreach (DataRow custRow in custDS.Tables["Customers"].Rows)
{
Console.WriteLine(custRow["CustomerID"]);
foreach (DataRow orderRow in custRow.GetChildRows(custOrderRel))
{
Console.WriteLine(orderRow["OrderID"]);
}
}

The next example builds on the preceding example, relating four tables together
and navigating those relationships. As in the previous example, CustomerID
relates the Customers table to the Orders table. For each customer in the
Customers table, all the child rows in the Orders table are determined in order
to return the number of orders a particular customer has and their OrderID
values.
The expanded example also returns the values from the OrderDetails and
Products tables. The Orders table is related to the OrderDetails table by using
OrderID to determine, for each customer order, what products and quantities
were ordered. Because the OrderDetails table only contains the ProductID of
an ordered product, OrderDetails is related to Products by using ProductID to
return the ProductName. In this relation, the Products table is the parent and
the OrderDetails table is the child. As a result, when iterating through the
OrderDetails table, GetParentRow is called to retrieve the related
ProductName value.
Notice that when the DataRelation is created for the Customers and Orders
tables, no value is specified for the createConstraints flag (the default is true).
This assumes that all the rows in the Orders table have a CustomerID value
that exists in the parent Customers table. If a CustomerID exists in the Orders
table that does not exist in the Customers table, a ForeignKeyConstraint will
cause an exception to be thrown.
In situations where the child column might contain values that the parent
column does not contain, set the createConstraints flag to false when adding
the DataRelation. In the following example, the createConstraints flag is set
to false for the DataRelation between the Orders table and the OrderDetails
table. This setting enables the application to return all the records from the
OrderDetails table and a subset of records from the Orders table without
generating a run-time exception. The expanded example generates output in the
following format.

124

Unit 4: Performing Disconnected Operations Programmatically

Customer ID: NORTS


Order ID: 10517
Order Date:
Product:
Quantity:
Product:
Quantity:
Product:
Quantity:
Order ID: 11057
Order Date:
Product:
Quantity:

4/24/1997 12:00:00 AM
Filo Mix
6
Raclette Courdavault
4
Outback Lager
6
4/29/1998 12:00:00 AM
Outback Lager
3

The following code example is an expanded sample where the values from the
OrderDetails and Products tables are returned, with only a subset of the
records in the Orders table being returned.

Unit 4: Performing Disconnected Operations Programmatically

125

[Visual Basic]
Dim custOrderRel As DataRelation = custDS.Relations.Add( _
"CustOrders", _
custDS.Tables("Customers").Columns("CustomerID"), _
custDS.Tables("Orders").Columns("CustomerID"))
Dim orderDetailRel As DataRelation = custDS.Relations.Add( _
"OrderDetail", _
custDS.Tables("Orders").Columns("OrderID"), _
custDS.Tables("OrderDetails").Columns("OrderID"), False)
Dim orderProductRel As DataRelation = custDS.Relations.Add( _
"OrderProducts", _
custDS.Tables("Products").Columns("ProductID"), _
custDS.Tables("OrderDetails").Columns("ProductID"))
Dim custRow, orderRow, detailRow As DataRow
For Each custRow In custDS.Tables("Customers").Rows
Console.WriteLine("Customer ID:" & custRow("CustomerID").ToString())
For Each orderRow In custRow.GetChildRows(custOrderRel)
Console.WriteLine(" Order ID: " & orderRow("OrderID").ToString())
Console.WriteLine(vbTab & "Order Date: " & _
orderRow("OrderDate").ToString())
For Each detailRow In orderRow.GetChildRows(orderDetailRel)
Console.WriteLine(vbTab & "
Product: " & _
detailRow.GetParentRow(orderProductRel)("ProductName").ToString())
Console.WriteLine(vbTab & " Quantity: " & _
detailRow("Quantity").ToString())
Next
Next
Next

126

Unit 4: Performing Disconnected Operations Programmatically

[C#]
DataRelation custOrderRel = custDS.Relations.Add(
"CustOrders",
custDS.Tables["Customers"].Columns["CustomerID"],
custDS.Tables["Orders"].Columns["CustomerID"]);
DataRelation orderDetailRel = custDS.Relations.Add(
"OrderDetail",
custDS.Tables["Orders"].Columns["OrderID"],
custDS.Tables["OrderDetails"].Columns["OrderID"], false);
DataRelation orderProductRel = custDS.Relations.Add(
"OrderProducts",
custDS.Tables["Products"].Columns["ProductID"],
custDS.Tables["OrderDetails"].Columns["ProductID"]);
foreach (DataRow custRow in custDS.Tables["Customers"].Rows)
{
Console.WriteLine("Customer ID: " + custRow["CustomerID"]);
foreach (DataRow orderRow in custRow.GetChildRows(custOrderRel))
{
Console.WriteLine(" Order ID: " + orderRow["OrderID"]);
Console.WriteLine("\tOrder Date: " + orderRow["OrderDate"]);
foreach (DataRow detailRow in orderRow.GetChildRows(orderDetailRel))
{
Console.WriteLine("\t
Product: " +
detailRow.GetParentRow(orderProductRel)["ProductName"]);
Console.WriteLine("\t Quantity: " + detailRow["Quantity"]);
}
}
}

Unit 4: Performing Disconnected Operations Programmatically

127

How to Use Row States and Row Versions


This document is adapted from articles that you can find at the following
locations:
In the Microsoft Visual Studio 2005 documentation:

Row States and Row Versions


mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.VisualSt
udio.v80.en/WD_ADONET/html/2e6642c9-bfc6-425c-b3a7e4912ffa6c1f.htm

DataTable.GetChanges Method (DataRowState)


mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.NETDE
VFX.v20.en/cpref/html/M_System_Data_DataTable_GetChanges_1_54b41
efc.htm

How to: Use Row States


Microsoft ADO.NET manages rows in tables by using row states and versions.
A row state indicates the status of a row. Row versions maintain the values
stored in a row as it is modified, including current, original, and default values.
For example, after you have made a modification to a column in a row, the row
will have a row state of Modified, and two row versions will exist: Current,
which contains the current row values, and Original, which contains the row
values before the column was modified.
Each DataRow object has a DataRowState property that you can examine to
determine the current state of the row. The following table gives a brief
description of each DataRowState enumeration value.
DataRowState

Description

Unchanged

No changes have been made since the last call to


AcceptChanges or RejectChanges or since the row
was created by DataAdapter.Fill.

Added

The row has been added to the table, but


AcceptChanges or RejectChanges has not been called.

Modified

Some element of the row has been changed.

Deleted

The row has been deleted from a table and


AcceptChanges or RejectChanges has not been called.

Detached

Detached is set for a row that has been created but is


not part of any DataRowCollection, or for a row that is
no longer part of a DataRowCollection. The
DataRowState of a newly created row is set to
Detached. After the new DataRow is added to the
DataRowCollection by calling the Add method, the
value of the DataRowState property is set to Added.
Detached is also set for a row that has been removed
from a DataRowCollection by using the Remove
method, or by the Delete method followed by the
AcceptChanges method.

128

Unit 4: Performing Disconnected Operations Programmatically

When AcceptChanges is called on a DataSet, DataTable, or DataRow, all


rows with a row state of Deleted are removed. The remaining rows are given a
row state of Unchanged and the values in the Original row version are
overwritten with the Current row version values.
When RejectChanges is called, all rows with a row state of Added are
removed. The remaining rows are given a row state of Unchanged and the
values in the Current row version are overwritten with the Original row
version values.
You can use the GetChanges method on a DataTable object to get a copy of
rows that have a particular DataRowState, as shown in the following example.
[Visual Basic]
Dim addedRows As DataTable
= custTable.GetChanges(DataRowState.Added)
Dim modifiedRows As DataTable = custTable.GetChanges(DataRowState.Modified)
Dim deletedRows As DataTable = custTable.GetChanges(DataRowState.Deleted)

[C#]
DataTable addedRows
= custTable.GetChanges(DataRowState.Added);
DataTable modifiedRows = custTable.GetChanges(DataRowState.Modified);
DataTable deletedRows = custTable.GetChanges(DataRowState.Deleted);

How to: Use Row Versions


You can view the different row versions of a row by using a DataRowVersion
parameter with the column reference, as shown in the following example.
[Visual Basic]
Dim custRow As DataRow = custTable.Rows(0)
Dim custID As String = custRow("CustomerID", DataRowVersion.Original).ToString()

[C#]
DataRow custRow = custTable.Rows[0];
string custID = custRow["CustomerID", DataRowVersion.Original].ToString();

Unit 4: Performing Disconnected Operations Programmatically

129

The following table gives a brief description of each DataRowVersion


enumeration value.
DataRowVersion

Description

Current

The current values for the row. This row version does
not exist for rows with a RowState of Deleted.

Default

The default row version for a particular row. The


default row version for an Added, Modified, or
Unchanged row is Current. The default row version
for a Deleted row is Original. The default row version
for a Detached row is Proposed.

Original

The original values for the row. This row version does
not exist for rows with a RowState of Added.

Proposed

The proposed values for the row. This row version


exists during an edit operation on a row, or for a row
that is not part of a DataRowCollection.

You can test whether a DataRow has a particular row version by calling the
HasVersion method and passing a DataRowVersion as an argument. For
example, DataRow.HasVersion(DataRowVersion.Original) will return false
for newly added rows before AcceptChanges has been called.
The following code example displays the values in all the deleted rows of a
table. Deleted rows do not have a Current row version, so you must pass
DataRowVersion.Original when accessing the column values.
[Visual Basic]
Dim catTable As DataTable = catDS.Tables("Categories")
Dim delRows() As DataRow = _
catTable.Select(Nothing, Nothing, DataViewRowState.Deleted)
Console.WriteLine("Deleted rows:" & vbCrLf)
Dim catCol As DataColumn
Dim delRow As DataRow
For Each catCol In catTable.Columns
Console.Write(catCol.ColumnName & vbTab)
Next
Console.WriteLine()
For Each delRow In delRows
For Each catCol In catTable.Columns
Console.Write(delRow(catCol, DataRowVersion.Original) & vbTab)
Next
Console.WriteLine()
Next

130

Unit 4: Performing Disconnected Operations Programmatically

[C#]
DataTable catTable = catDS.Tables["Categories"];
DataRow[] delRows = catTable.Select(null, null, DataViewRowState.Deleted);
Console.WriteLine("Deleted rows:\n");
foreach (DataColumn catCol in catTable.Columns)
{
Console.Write(catCol.ColumnName + "\t");
}
Console.WriteLine();
foreach (DataRow delRow in delRows)
{
foreach (DataColumn catCol in catTable.Columns)
{
Console.Write(delRow[catCol, DataRowVersion.Original] + "\t");
}
Console.WriteLine();
}

Unit 4: Performing Disconnected Operations Programmatically

131

How to Handle DataTable Events and Errors


This document is adapted from articles that you can find at the following
locations:
In the Microsoft Visual Studio 2005 documentation:

Working with DataTable Events


mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.VisualSt
udio.v80.en/WD_ADONET/html/62f404a5-13ea-4b93-a29f55b74a16c9d3.htm

Adding and Reading Row Error Information


mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.VisualSt
udio.v80.en/WD_ADONET/html/8b1f9070-d032-48c7-b030bd8fbb2ca59a.htm

Accepting or Rejecting Changes to Rows


mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.VisualSt
udio.v80.en/WD_ADONET/html/e2d1a6fe-31f9-4b83-972806c406a3394e.htm

How to: Handle DataTable Events


The DataTable object provides a series of events that can be processed by an
application. The following table describes DataTable events.
Event

Description

ColumnChanged

Occurs when a value has been inserted successfully into a column.

ColumnChanging

Occurs when a value has been submitted for a column.

RowChanged

Occurs after a row in the table has been edited successfully.

RowChanging

Occurs when a row in the table is changing.

RowDeleted

Occurs after a row in the table has been marked as Deleted.

RowDeleting

Occurs prior to a row in the table being marked as Deleted.

The following example creates four events: OnColumnChanged,


OnColumnChanging, OnRowChanged, and OnRowChanging. Each of these
events occurs when a column or row changes.

132

Unit 4: Performing Disconnected Operations Programmatically

[Visual Basic]
AddHandler workTable.ColumnChanged, _
New DataColumnChangeEventHandler(AddressOf OnColumnChanged)
AddHandler workTable.ColumnChanging, _
New DataColumnChangeEventHandler(AddressOf OnColumnChanging)
AddHandler workTable.RowChanged, _
New DataRowChangeEventHandler(AddressOf OnRowChanged)
AddHandler workTable.RowChanging, _
New DataRowChangeEventHandler(AddressOf OnRowChanging)

Private Sub OnColumnChanged(sender As Object, _


args As DataColumnChangeEventargs)
Console.Write(" ColumnChanged: ")
Console.Write(args.Column.ColumnName & " changed to '" & _
args.ProposedValue.ToString() & "'" & vbCrLf)
End Sub

Private Sub OnColumnChanging(sender As Object, _


args As DataColumnChangeEventargs)
Console.Write("ColumnChanging: ")
Console.Write(args.Column.ColumnName & " equals '" & _
args.Row(args.Column).ToString() & "', changing to '" & _
args.ProposedValue.ToString() & "'" & vbCrLf)
End Sub

Private Sub OnRowChanging(sender As Object, _


args As DataRowChangeEventargs)
If args.Action <> DataRowAction.Nothing Then
Dim actionStr As String
actionStr = System.Enum.GetName(args.Action.GetType(), args.Action)
Console.WriteLine("
RowChanging: Action = " & actionStr & _
", CustID = " & args.Row("CustID").ToString())
End If
End Sub

Private Sub OnRowChanged(sender As Object, _


args As DataRowChangeEventargs)
If args.Action <> DataRowAction.Nothing Then
Dim actionStr As String
actionStr = System.Enum.GetName(args.Action.GetType(), args.Action)
Console.WriteLine("
RowChanged: Action = " & actionStr & _
", CustID = " & args.Row("CustID").ToString())
End If
End Sub

Unit 4: Performing Disconnected Operations Programmatically


[C#]
workTable.ColumnChanged
workTable.ColumnChanging
workTable.RowChanged
workTable.RowChanging

+=
+=
+=
+=

new
new
new
new

133

DataColumnChangeEventHandler(OnColumnChanged);
DataColumnChangeEventHandler(OnColumnChanging);
DataRowChangeEventHandler(OnRowChanged);
DataRowChangeEventHandler(OnRowChanging);

protected void OnColumnChanged(object sender, DataColumnChangeEventArgs e)


{
Console.Write(" ColumnChanged: ");
Console.Write(e.Column.ColumnName + " changed to '" +
e.ProposedValue + "'\n");
}

protected void OnColumnChanging(object sender, DataColumnChangeEventArgs e)


{
Console.Write("ColumnChanging: ");
Console.Write(e.Column.ColumnName + " equals '" +
e.Row[e.Column] + "', changing to '" +
e.ProposedValue + "'\n");
}

protected void OnRowChanging(object sender, DataRowChangeEventArgs e)


{
if (e.Action != DataRowAction.Nothing)
Console.WriteLine("
RowChanging: Action = " + e.Action +
", CustID = " + e.Row["CustID"]);
}

protected void OnRowChanged(object sender, DataRowChangeEventArgs e)


{
if (e.Action != DataRowAction.Nothing)
Console.WriteLine("
RowChanged: Action = " + e.Action +
", CustID = " + e.Row["CustID"]);
}

How to: Add and Read Row Error Information


To avoid having to respond each time a row error occurs while you are editing
values in a DataTable, you can add the error information to the row, to be used
later. The DataRow object supports this capability by providing a RowError
property on each row. Adding data to the RowError property of a DataRow
marks the HasErrors property of the DataRow to true. If the DataRow is part
of a DataTable, and DataRow.HasErrors is true, the DataTable.HasErrors
property is also true. The same applies to the DataSet to which the DataTable
belongs. When testing for errors, you can check the HasErrors property to
determine if error information has been added to any rows. If HasErrors is
true, you can use the GetErrors method of the DataTable to return and
examine only the rows with errors, as shown in the following example.

134

Unit 4: Performing Disconnected Operations Programmatically

[Visual Basic]
Dim workTable As DataTable = New DataTable("Customers")
workTable.Columns.Add("CustID", Type.GetType("System.Int32"))
workTable.Columns.Add("Total", Type.GetType("System.Double"))
AddHandler workTable.RowChanged, _
New DataRowChangeEventHandler(AddressOf OnRowChanged)
Dim I As Int32
For I = 0 To 10
workTable.Rows.Add(New Object() {I, I*100})
Next
If workTable.HasErrors Then
Console.WriteLine("Errors In Table " & workTable.TableName)
Dim myRow As DataRow
For Each myRow In workTable.GetErrors()
Console.WriteLine("CustID = " & myRow("CustID").ToString())
Console.WriteLine(" Error = " & myRow.RowError & vbCrLf)
Next
End If

Private Sub OnRowChanged(sender As Object, args As DataRowChangeEventArgs)


' Check for zero values.
If CDbl(args.Row("Total")) = 0 Then args.Row.RowError = "Total cannot be 0."
End Sub

Unit 4: Performing Disconnected Operations Programmatically

135

[C#]
DataTable workTable = new DataTable("Customers");
workTable.Columns.Add("CustID", typeof(Int32));
workTable.Columns.Add("Total", typeof(Double));
workTable.RowChanged += new DataRowChangeEventHandler(OnRowChanged);
for (int i = 0; i < 10; i++)
workTable.Rows.Add(new Object[] {i, i*100});
if (workTable.HasErrors)
{
Console.WriteLine("Errors In Table " + workTable.TableName);
foreach (DataRow myRow in workTable.GetErrors())
{
Console.WriteLine("CustID = " + myRow["CustID"]);
Console.WriteLine(" Error = " + myRow.RowError + "\n");
}
}

protected void OnRowChanged(Object sender, DataRowChangeEventArgs e)


{
// Check for zero values.
if (e.Row["Total"].Equals(0D))
e.Row.RowError = "Total cannot be 0.";
}

How to: Accept or Reject Changes to Rows


After verifying the accuracy of changes made to data in a DataTable, you can
accept the changes by using the AcceptChanges method of the DataRow,
DataTable, or DataSet, which will set the Current row values to be the
Original values and will set the RowState property to Unchanged. Accepting
or rejecting changes clears out any RowError information and sets the
HasErrors property to false. Accepting or rejecting changes can also affect
updating data in the data source.
If foreign key constraints exist on the DataTable, changes accepted or rejected
by using AcceptChanges and RejectChanges are propagated to child rows of
the DataRow according to the ForeignKeyConstraint.AcceptRejectRule.
The following example checks for rows with errors, resolves the errors where
applicable, and rejects the rows where the error cannot be resolved. Note that,
for resolved errors, the RowError value is reset to an empty string, resulting in
the HasErrors property being set to false. When all the rows with errors have
been resolved or rejected, AcceptChanges is called to accept all changes for the
entire DataTable.

136

Unit 4: Performing Disconnected Operations Programmatically

[Visual Basic]
If workTable.HasErrors Then
Dim errRow As DataRow
For Each errRow in workTable.GetErrors()
If errRow.RowError = "Total cannot exceed 1000." Then
errRow("Total") = 1000
errRow.RowError = ""
' Clear the error.
Else
errRow.RejectChanges()
End If
Next
End If
workTable.AcceptChanges()

[C#]
if (workTable.HasErrors)
{
foreach (DataRow errRow in workTable.GetErrors())
{
if (errRow.RowError == "Total cannot exceed 1000.")
{
errRow["Total"] = 1000;
errRow.RowError = "";
// Clear the error.
}
else
errRow.RejectChanges();
}
}
workTable.AcceptChanges();

Unit 4: Performing Disconnected Operations Programmatically

137

How to Merge DataSet Contents


This document is adapted from an article that you can find at the following
location:
In the Microsoft Visual Studio 2005 documentation:

Merging DataSet Contents


mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.VisualSt
udio.v80.en/WD_ADONET/html/e5e9309a-3ebb-4a9c-9d7821c4e2bafc5b.htm

How to: Merge DataSet Contents


You can use the Merge method of the DataSet to merge the contents of a
DataSet, DataTable, or DataRow array into an existing DataSet. Several
factors and options affect how new data is merged into an existing DataSet.

Primary Keys
If the table receiving new data and schema from a merge has a primary key,
new rows from the incoming data are matched with existing rows that have the
same Original primary key values as those in the incoming data. If the columns
from the incoming schema match those of the existing schema, the data in the
existing rows is modified. Columns that do not match the existing schema are
either ignored or added based on the MissingSchemaAction parameter (see
MissingSchemaAction later in this topic). New rows that have primary key
values that do not match any existing rows are appended to the existing table.
If incoming or existing rows have a row state of Added, their primary key
values are matched by using the Current primary key value of the Added row
because no Original row version exists.
If an incoming table and an existing table contain a column with the same name
but differing data types, an exception is thrown and the MergeFailed event of
the DataSet is raised. If an incoming table and an existing table both have
primary keys defined, but the primary keys are for different columns, an
exception is thrown and the MergeFailed event of the DataSet is raised.
If the table receiving new data from a merge does not have a primary key, new
rows from the incoming data cannot be matched to existing rows in the table
and are instead appended to the existing table.

preserveChanges
When you pass a DataSet, DataTable, or DataRow array to the Merge
method, you can include optional parameters that specify whether to preserve
changes in the existing DataSet and how to handle new schema elements found
in the incoming data. The first of these parameters after the incoming data is a
Boolean flag, preserveChanges, which specifies whether or not to preserve the
changes in the existing DataSet. If the preserveChanges flag is set to true,
incoming values will not overwrite existing values in the Current row version
of the existing row. If the preserveChanges flag is set to false, incoming values
will overwrite the existing values in the Current row version of the existing
row. If the preserveChanges flag is not specified, it is set to false by default.

138

Unit 4: Performing Disconnected Operations Programmatically

When preserveChanges is true, the data from the existing row is maintained in
the Current row version of the existing row, while the data from the Original
row version of the existing row is overwritten with the data from the Original
row version of the incoming row. The RowState of the existing row is set to
Modified. The following exceptions apply:

If the existing row has a RowState of Deleted, this RowState will remain
Deleted and will not be set to Modified. In this case, the data from the
incoming row will still be stored in the Original row version of the existing
row, overwriting the Original row version of the existing row (unless the
incoming row has a RowState of Added).

If the incoming row has a RowState of Added, the data from the Original
row version of the existing row will not be overwritten with data from the
incoming row because the incoming row does not have an Original row
version.

When preserveChanges is false, both the Current and Original row versions
in the existing row are overwritten with the data from the incoming row, and the
RowState of the existing row is set to the RowState of the incoming row. The
following exceptions apply:

If the incoming row has a RowState of Unchanged and the existing row
has a RowState of Modified, Deleted, or Added, the RowState of the
existing row is set to Modified.

If the incoming row has a RowState of Added, and the existing row has a
RowState of Unchanged, Modified, or Deleted, the RowState of the
existing row is set to Modified. Also, the data from the Original row
version of the existing row is not overwritten with data from the incoming
row because the incoming row does not have an Original row version.

MissingSchemaAction
You can use the optional MissingSchemaAction parameter of the Merge
method to specify how Merge will handle schema elements in the incoming
data that are not part of the existing DataSet.
The following table describes the options for MissingSchemaAction.
MissingSchemaAction

Description

Add

Add the new schema information to the


DataSet and populate the new columns
with the incoming values. This is the
default.

AddWithKey

Add the new schema and primary key


information to the DataSet and populate
the new columns with the incoming
values.

Error

Throw an exception if mismatched


schema information is encountered.

Ignore

Ignore the new schema information.

Unit 4: Performing Disconnected Operations Programmatically

139

Constraints
With the Merge method, constraints are not checked until all new data has been
added to the existing DataSet. After the data has been added, constraints are
enforced on the current values in the DataSet. You must ensure that your code
is written to handle any exceptions that might be thrown due to constraint
violations.
Consider as an example a case where an existing row in a DataSet is an
Unchanged row with a primary key value of 1. During a merge operation with
a Modified incoming row with an Original primary key value of 2 and a
Current primary key value of 1, the existing row and the incoming row are not
considered matching because the Original primary key values differ. However,
when the merge is completed and constraints are checked, an exception will be
thrown because the Current primary key values violate the unique constraint
for the primary key column.

Example
The following code example takes an existing DataSet with updates and passes
those updates to a DataAdapter to be processed at the data source. The results
are then merged into the original DataSet. The merged changes are committed
with AcceptChanges.
[Visual Basic]
Dim custTable As DataTable = custDS.Tables("Customers")
' Make modifications to the Customers table.
' Get changes to the DataSet.
Dim updDS As DataSet = custDS.GetChanges()
' Add an event handler to handle the errors during Update.
AddHandler custDA.RowUpdated, _
New SqlRowUpdatedEventHandler(AddressOf OnRowUpdated)
nwindConn.Open()
custDA.Update(updDS, "Customers")
nwindConn.Close()
' Merge the updates.
custDS.Merge(updDS, False, MissingSchemaAction.Error)
' Commit the changes.
custDS.AcceptChanges()

Private Shared Sub OnRowUpdated(sender As Object, _


args As SqlRowUpdatedEventArgs)
If args.Status = UpdateStatus.ErrorsOccurred
args.Row.RowError = args.Errors.Message
args.Status = UpdateStatus.SkipCurrentRow
End If
End Sub

140

Unit 4: Performing Disconnected Operations Programmatically

[C#]
DataTable custTable = custDS.Tables["Customers"];
// Make modifications to the Customers table.
// Get changes to the DataSet.
DataSet updDS = custDS.GetChanges();
// Add an event handler to handle the errors during Update.
custDA.RowUpdated += new SqlRowUpdatedEventHandler(OnRowUpdated);
nwindConn.Open();
custDA.Update(updDS, "Customers");
nwindConn.Close();
// Merge the updates.
custDS.Merge(updDS, false, MissingSchemaAction.Error);
// Commit the changes.
custDS.AcceptChanges();

protected void OnRowUpdated(object sender, SqlRowUpdatedEventArgs e)


{
if (e.Status == UpdateStatus.ErrorsOccurred)
{
e.Row.RowError = e.Errors.Message;
e.Status = UpdateStatus.SkipCurrentRow;
}
}

Unit 4: Performing Disconnected Operations Programmatically

141

How to Create and Use DataViews


This document is adapted from articles that you can find at the following
locations:
In the Microsoft Visual Studio 2005 documentation:

Creating and Using DataViews


mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.VisualSt
udio.v80.en/WD_ADONET/html/0fe5dfa2-c1cd-435f-90b6b4dd2e3ef34b.htm

Creating a DataView
mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.VisualSt
udio.v80.en/WD_ADONET/html/b1cc02d1-23b1-4439-a9980da1899f3442.htm

Sorting and Filtering Data Using a DataView


mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.VisualSt
udio.v80.en/WD_ADONET/html/fdd9c753-39df-48cd-98222781afe76200.htm

Introduction to DataViews
A DataView enables you to create different views of the data stored in a
DataTable, a capability that is often used in data-binding applications. Using a
DataView, you can expose the data in a table with different sort orders and
filter the data by row state or based on a filter expression that is applied to
column values.
A DataView provides a dynamic view of data whose content, ordering, and
membership reflect changes to the underlying DataTable as they occur. This is
different from the Select method of the DataTable, which returns a DataRow
array from a table based on a particular filter and/or sort order and whose
content reflects changes to the underlying table, but whose membership and
ordering remain static. The dynamic capabilities of the DataView make it ideal
for data-binding applications.
A DataView provides you with a dynamic view of a single set of data to which
you can apply different sorting and filtering criteria, similar to the view
provided by a database. However, a DataView differs significantly from a
database view in that the DataView cannot be treated as a table and cannot
provide a view of joined tables. You also cannot exclude columns that exist in
the source table, nor can you append columns, such as computational columns,
that do not exist in the source table.

142

Unit 4: Performing Disconnected Operations Programmatically

How to: Create a DataView


There are two ways to create a DataView. You can use the DataView
constructor, or you can create a reference to the DefaultView property of the
DataTable. The DataView constructor can be empty or will also take either a
DataTable as a single argument or a DataTable along with filter criteria, sort
criteria, and a row state filter.
Because the index for a DataView is built both when the DataView is created
and when any of the Sort, RowFilter, or RowStateFilter properties are
modified, you will achieve best performance by supplying any initial sort order
or filtering criteria as constructor arguments when you create the DataView.
Creating a DataView without specifying sort or filter criteria and then setting
the Sort, RowFilter, or RowStateFilter properties later results in the index
being built at least twice: once when the DataView is created and again when
any of the sort or filter properties are modified.
Note that if you create a DataView by using the constructor that does not take
any arguments, you will not be able to use the DataView until you have set the
Table property.
The following code example demonstrates how to create a DataView by using
the DataView constructor. A RowFilter, Sort column, and
DataViewRowState are supplied along with the DataTable.
[Visual Basic]
Dim custDV As DataView = New DataView(custDS.Tables("Customers"), _
"Country = 'USA'", _
"ContactName", _
DataViewRowState.CurrentRows)

[C#]
DataView custDV = new DataView(custDS.Tables["Customers"],
"Country = 'USA'",
"ContactName",
DataViewRowState.CurrentRows);

The following code example demonstrates how to obtain a reference to the


default DataView of a DataTable by using the DefaultView property of the
table.
[Visual Basic]
Dim custDV As DataView = custDS.Tables("Customers").DefaultView

[C#]
DataView custDV = custDS.Tables["Customers"].DefaultView;

Unit 4: Performing Disconnected Operations Programmatically

143

How to: Sort and Filter Data Using a DataView


The DataView provides several capabilities for sorting and filtering data in a
DataTable:

Using the Sort property, you can specify single or multiple column sort
orders and include ASC (ascending) and DESC (descending) sort direction
instructions.

Using the ApplyDefaultSort property, you can automatically create a sort


order, in ascending order, based on the primary key column or columns of
the table. ApplyDefaultSort applies only when the Sort property is a null
reference or an empty string and when the table has a primary key defined.

Using the RowFilter property, you can specify subsets of rows based on
their column values.
If you want to return the results of a particular query on the data, as opposed
to providing a dynamic view of a subset of the data, to achieve best
performance, use the Find or FindRows methods of the DataView rather
than setting the RowFilter property. Setting the RowFilter property causes
the index for the data to be rebuilt, adding overhead to your application and
decreasing performance. The RowFilter property is best used in a databound application where a bound control displays filtered results. The Find
and FindRows methods leverage the current index without requiring the
index to be rebuilt.

Using the RowStateFilter property, you can specify which row versions to
view. The DataView implicitly manages which row version to expose
depending upon the RowState of the underlying row. For example, if the
RowStateFilter is set to DataViewRowState.Deleted, the DataView will
expose the Original row version of all Deleted rows because there is no
Current row version. You can determine which row version of a row is
being exposed by using the RowVersion property of the DataRowView.

The following table shows the options for DataViewRowState.


DataViewRowState

Description

CurrentRows

The Current row version of all Unchanged, Added, and


Modified rows. This is the default.

Added

The Current row version of all Added rows.

Deleted

The Original row version of all Deleted rows.

ModifiedCurrent

The Current row version of all Modified rows.

ModifiedOriginal

The Original row version of all Modified rows

None

No rows.

OriginalRows

The Original row version of all Unchanged, Modified,


and Deleted rows.

Unchanged

The Current row version of all Unchanged rows.

144

Unit 4: Performing Disconnected Operations Programmatically

The following code example creates a view that shows all the products where
the units in stock is less than or equal to the reorder level, sorted first by
supplier ID and then by product name. The code prevents the user from adding,
editing, or deleting rows in the DataTable by using the DataView.
[Visual Basic]
Dim prodView As New DataView(prodDS.Tables("Products"))
prodView.RowFilter = "UnitsInStock <= ReorderLevel"
prodView.Sort = "SupplierID, ProductName"
prodView.AllowNew = False
prodView.AllowEdit = False
prodView.AllowDelete = False

[C#]
DataView prodView = new DataView(prodDS.Tables["Products"]);
prodView.RowFilter = "UnitsInStock <= ReorderLevel";
prodView.Sort = "SupplierID, ProductName";
prodView.AllowNew = false;
prodView.AllowEdit = false;
prodView.AllowDelete = false;

Unit 4: Performing Disconnected Operations Programmatically

145

How to Handle DataView Events


This document is adapted from an article that you can find at the following
location:
In the Microsoft Visual Studio 2005 documentation:

Working with DataView Events


mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.VisualSt
udio.v80.en/WD_ADONET/html/e5675663-fc91-4e0d-87a9481b25b64c0f.htm

How to: Handle DataView Events


You can use the ListChanged event of the DataView to determine whether a
view has been updated because of the addition, deletion, or modification of a
row in the underlying table. Such a change to a row would occur because the
schema of the underlying table has been modified by the addition or deletion of
a column or because a parent or child relationship has changed. The
ListChanged event will also notify you if the list of rows you are viewing has
changed significantly because of the application of a new sort order or a filter.
The ListChanged event implements the ListChangedEventHandler delegate
of the System.ComponentModel namespace and takes as input a
ListChangedEventArgs object. You can determine what type of change has
occurred by using the ListChangedType enumeration value in the
ListChangedType property of the ListChangedEventArgs object. For
changes that involve adding, deleting, or moving rows, the new index of the
added or moved row and the previous index of the deleted row can be accessed
by using the NewIndex property of the ListChangedEventArgs object. In the
case of a moved row, the previous index of the moved row can be accessed by
using the OldIndex property of the ListChangedEventArgs object.
The following code example shows how to add a ListChanged event handler.
[Visual Basic]
AddHandler custView.ListChanged, _
New System.ComponentModel.ListChangedEventHandler(AddressOf OnListChanged)

Private Shared Sub OnListChanged(sender As Object, _


arg As System.ComponentModel.ListChangedEventArgs)
Console.WriteLine("ListChanged:")
Console.WriteLine(vbTab & "
Type = " & _
System.Enum.GetName(arg.ListChangedType.GetType(), _
arg.ListChangedType))
Console.WriteLine(vbTab & "OldIndex = " & arg.OldIndex)
Console.WriteLine(vbTab & "NewIndex = " & arg.NewIndex)
End Sub

146

Unit 4: Performing Disconnected Operations Programmatically

[C#]
custView.ListChanged +=
new System.ComponentModel.ListChangedEventHandler(OnListChanged);
protected static void OnListChanged(object sender,
System.ComponentModel.ListChangedEventArgs e)
{
Console.WriteLine("ListChanged:");
Console.WriteLine("\t
Type = " + e.ListChangedType);
Console.WriteLine("\tOldIndex = " + e.OldIndex);
Console.WriteLine("\tNewIndex = " + e.NewIndex);
}

Unit 5: Performing Disconnected


Operations by Using Visual Studio
2005 Wizards
Database Tables and Stored Procedures
Introduction
In the first phase of development of the OnTheRoad application, you will use
the following tables in the Adventure Works database:

Production.ProductCategory

Production.ProductSubcategory

Production.Product

Production.ProductReview

Sales.SalesVisit

This resource document describes these tables and related stored procedures.

Production.ProductCategory Table
The Production.ProductCategory table in the Adventure Works database
contains information about product categories in the Adventure Works
organization. For example, there are product categories named Accessories,
Bikes, Clothing, and Components. The following table describes the schema of
the Production.ProductCategory table.
Column name

SQL data type

Description

ProductCategoryID

int

Primary key column

Name

nvarchar(50)

Category description

rowguid

uniqueidentifier

ROWGUIDCOL number that uniquely


identifies the record (for merge
replication)

ModifiedDate

datetime

Date and time the record was updated

148

Unit 5: Performing Disconnected Operations by Using Visual Studio 2005 Wizards

Production.ProductSubcategory Table
The Production.ProductSubcategory table in the Adventure Works database
contains information about product subcategories within particular product
categories. For example, within the Bikes product category, there are product
subcategories named Mountain Bikes, Road Bikes, and Touring Bikes. The
following table describes the schema of the Production.ProductSubcategory
table.
Column name

SQL data type

Description

ProductSubcategoryID

int

Primary key column

ProductCategoryID

int

Foreign key to the ProductCategoryID


column in the
Production.ProductCategory table

Name

nvarchar(50)

Subcategory description

rowguid

uniqueidentifier

ROWGUIDCOL number that uniquely


identifies the record (for merge
replication)

ModifiedDate

datetime

Date and time the record was updated

You will use the following stored procedure to query data in the
Production.ProductSubcategory table:

uspGetProductSubcategories

uspGetProductSubcategories
This stored procedure returns a result set that contains all the records from the
Production.ProductSubcategory table. The stored procedure does not have
any parameters.

Production.Product Table
The Production.Product table in the Adventure Works database contains
information about products sold by the Adventure Works organization. The
following table describes the schema of the Production.Product database
table.
Column name

SQL data type

Description

ProductID

int

Primary key column

Name

nvarchar(50)

Name of the product

ProductNumber

nvarchar(25)

Unique product number

MakeFlag

bit

0 = Product is purchased

FinishedGoodsFlag

bit

0 = Product is not scalable

1 = Product is made in-house


1 = Product is scalable
Color

nvarchar(15)

Product color

SafetyStockLevel

smallint

Minimum inventory quantity

ReorderPoint

smallint

Inventory level that triggers a purchase


order or work order

StandardCost

money

Standard cost of the product

Unit 5: Performing Disconnected Operations by Using Visual Studio 2005 Wizards

149

(continued)
Column name

SQL data type

Description

ListPrice

money

Selling price

Size

nvarchar(5)

Product size

SizeUnitMeasureCost

nchar(3)

Unit of measure for size

WeightUnitMeasureCost

nchar(3)

Unit of measure for weight

Weight

decimal(8, 2)

Product weight

DaysToManufacture

int

Number of days required


to manufacture the product

ProductLine

nchar(2)

R = Road
M = Mountain
T = Touring
S = Standard

Class

nchar(2)

H = High
M = Medium
L = Low

Style

nchar(2)

W = Womens
M = Mens
U = Universal

ProductSubcategoryID

int

Foreign key to the


ProductSubcategoryID
column in the
ProductSubcategory table

ProductModelID

int

Foreign key to the


ProductModelID column
in the ProductModel table

SellStartDate

datetime

Date the product was


available for sale

SellEndDate

datetime

Date the product was no


longer available for sale

DiscontinuedDate

datetime

Date the product was


discontinued

rowguid

uniqueidentifier

ROWGUIDCOL number
that uniquely identifies the
record (for merge
replication)

ModifiedDate

datetime

Date and time the record


was last updated

150

Unit 5: Performing Disconnected Operations by Using Visual Studio 2005 Wizards

Production.ProductReview Table
The Production.ProductReview table in the Adventure Works database
contains product review information. The following table describes the schema
of the Production.ProductReview database table.
Column name

SQL data type

Description

ProductReviewID

int

Primary key column

ProductID

int

Foreign key to the ProductID column in


the Production.Product table

ReviewerName

nvarchar(50)

Name of the reviewer

ReviewDate

datetime

Date review was submitted

EmailAddress

nvarchar(50)

Reviewers e-mail address

Rating

int

Product rating given by the reviewer


Minimum rating: 1
Maximum rating: 5

Comments

nvarchar(3850)

Reviewers comments

ModifiedDate

datetime

Date and time the record was last


updated

Sales.SalesVisit Table
The Sales.SalesVisit table in the Adventure Works database contains
information about forthcoming sales visits for salespeople at the Adventure
Works organization. The following table describes the schema of the
Sales.SalesVisit table.
Column name

SQL data type

Description

AppointmentID

int

Primary key column

SalesPersonID

int

ID of the sales person for the sales visit

DateAndTime

datetime

Date and time of the sales visit

Arrangements

nvarchar(100)

Description of the arrangements for the


sales visit, such as the location and
contact details

Notes

nvarchar(250)

Notes that the salesperson can make


before or after the sales visit to describe
the purpose or outcome of the sales
visit

Unit 5: Performing Disconnected Operations by Using Visual Studio 2005 Wizards

151

You will use the following stored procedure to query data in the
Production.ProductSubcategory table:

uspGetSalesVisitsBySalesPersonAndDate

uspGetSalesVisitsBySalesPersonAndDate
This stored procedure returns a result set that contains all the sales visits for a
particular sales person after a specified date and time. The stored procedure has
two parameters, as described in the following table.
Parameter name

SQL data type

Description

@prmSalesPersonID

int

The stored procedure will return sales


visits for this salesperson

@prmFromDate

datetime

The stored procedure will return sales


visits after this date

152

Unit 5: Performing Disconnected Operations by Using Visual Studio 2005 Wizards

How to Add TableAdapters to a DataSet


This document is extracted from articles that you can find at the following
locations:
In the Microsoft Visual Studio 2005 documentation at

TableAdapter Configuration Wizard


mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.VisualSt
udio.v80.en/dv_raddata/html/3a373dd9-7b34-4d3c-a48b-69414512bac8.htm

How to: Start the TableAdapter Configuration Wizard


mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.VisualSt
udio.v80.en/dv_raddata/html/301f2dcd-ed72-4229-80ef-3b59cb062d5d.htm

The TableAdapter Configuration Wizard


The TableAdapter Configuration Wizard creates and edits TableAdapters in
typed datasets. The wizard creates TableAdapters based on SQL statements
you enter into the wizard or on existing stored procedures in the database. The
wizard can also create new stored procedures in the database based on SQL
statements you enter into the wizard.
A TableAdapter connects to a database, executes a query or stored procedure
against a database, and fills a DataTable with the data returned by the query or
stored procedure. In addition to filling existing data tables with data,
TableAdapters can return new data tables filled with data.

Start the TableAdapter Configuration Wizard


Run the wizard to create or edit TableAdapters and their associated
DataTables.

To start the TableAdapter Configuration Wizard


1. Open your dataset in the Dataset Designer.
2. If you are creating a new TableAdapter, drag a TableAdapter object from
the DataSet tab of the Toolbox onto the Dataset Designer or right-click the
designer surface, click Add, and then click TableAdapter.
3. On the Choose Your Data Connection page, select a data connection from
the list of currently available connections or select New Connection to
create a new connection. Selecting New Connection opens the Add
Connection dialog box.
4. In the Save the connection string to the application configuration file
page, choose Yes, save the connection as, and enter a name for the
connection string in the text box.
5. Click Next.

Unit 5: Performing Disconnected Operations by Using Visual Studio 2005 Wizards

153

Choose a Command Type


This page allows you to choose which type of command to execute against the
database.

On the Choose a Command Type page, select from the following methods
of fetching data from the database:
Use SQL statements. This option allows you to type a SQL statement to
select the data from your database.
Create new stored procedures. Select this option to have the wizard
create new stored procedures (in the database) based on the specified
SELECT statement.
Use existing stored procedures. Select this option to map stored
procedures that already exist in your database to the SELECT, INSERT,
UPDATE, and DELETE commands of the TableAdapter.

Use SQL Statements


This section explains how to complete the TableAdapter Configuration Wizard
when the Use SQL statements option is selected.

To use SQL statements


1. On the Enter a SQL statement page, type the SQL statement that when
executed will fill your data table with data. Click Query Builder to help
you formulate complex queries.
2. On the Choose Methods to Generate page, choose which methods you
want to add to the TableAdapter.
3. The Wizard Results page shows the results of creating the TableAdapter.
If the wizard encounters any problems, this screen provides the error
information.

Use Existing Stored Procedures


This section explains how to complete the TableAdapter Configuration Wizard
when selecting the Use existing stored procedures option.

To use existing stored procedures


1. Select the stored procedure to execute for the SELECT, INSERT, UPDATE,
and DELETE commands of the TableAdapter. These stored procedures are
executed when the associated methods are called on the TableAdapter. For
example, the stored procedure assigned to the Insert command is executed
when the TableAdapter.Insert() method is called.
2. Map parameters from the selected stored procedure to the corresponding
columns in the data table. For example, if your stored procedure accepts a
parameter named @CompanyName that it passes to the CompanyName
column in the table, set the Source Column of the @CompanyName
parameter to CompanyName.
3. On the Choose Methods to Generate page, choose which methods you
want to add to the TableAdapter.
4. The Wizard Results page shows the results of creating the TableAdapter.
If the wizard encounters any problems, this screen provides the error
information.

154

Unit 5: Performing Disconnected Operations by Using Visual Studio 2005 Wizards

The stored procedure assigned to the SELECT command is executed by calling


the method of the TableAdapter that you name in the next step of the wizard.
The default is Fill, so the typical code to execute the SELECT procedure is
TableAdapter.Fill(targetDataTable). Substitute Fill with the name you assign
if you change it from the default of Fill, and replace targetDataTable with the
appropriate target instance of a suitably typed DataTable.

Create New Stored Procedures


This section explains how to complete the TableAdapter Configuration Wizard
when selecting the Create new stored procedures option.

To create new stored procedures


1. On the Enter a SQL Statement for the SELECT Stored Procedure page,
enter the SQL statement that fills the data table. This will be the
TableAdapters SELECT statement and the basis for creating the stored
procedures for selecting, updating, inserting, and deleting data. Click Query
Builder to help you formulate complex queries.
2. On the Create the Stored Procedures page enter a name for the new
SELECT, UPDATE, INSERT, and DELETE stored procedures.
3. On the Choose Methods to Generate page, indicate which methods you
want to add to the TableAdapter.
4. The Wizard Results page shows the results of creating the TableAdapter.
If the wizard encounters any problems, this screen provides the error
information.

Advanced Options
Clicking Advanced Options accesses the following advanced options of the
TableAdapter.

Generate Insert, Update, and Delete statements. When you select this
option, the wizard will attempt to generate INSERT, UPDATE, and
DELETE statements based on the SELECT statement defined on the Enter
a SQL Statement page.

Use optimistic concurrency. Selecting this option modifies the UPDATE


and DELETE statements to detect if individual records have been modified
since they were originally read into the data table. An exception is thrown
when concurrency violations are detected.

Refresh the DataTable. Selecting this option refreshes the data in the table
after executing INSERT and UPDATE statements.

Unit 5: Performing Disconnected Operations by Using Visual Studio 2005 Wizards

155

Next Steps
After the wizard has finished, the TableAdapter and its companion DataTable
are added to the dataset and become available for viewing and editing in the
Dataset Designer. You might perform a number of steps after that.

Add more TableAdapters and DataTables. The wizard creates one


TableAdapter/DataTable pair. If you intend to use a dataset that contains
multiple data tables, you will probably want to add more TableAdapters to
your dataset.

Add additional queries to the TableAdapter.

View this dataset in the Data Sources window and drag items onto
Microsoft Windows Forms to create data-bound controls.

156

Unit 5: Performing Disconnected Operations by Using Visual Studio 2005 Wizards

How to Add Database Objects to a DataSet


This document is extracted from an article that you can find at the following
location:
In the Microsoft Visual Studio 2005 documentation at

Walkthrough: Creating a Dataset with the Dataset Designer


mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.VisualSt
udio.v80.en/dv_raddata/html/12360f54-db6c-45d2-a91f-fee43214b555.htm

Walkthrough: Add Database Objects to a DataSet


In this walkthrough, you will create a dataset using the Dataset Designer. It will
take you through the process of creating a new project and adding a new
DataSet item to it. You will learn how to create tables based on tables in a
database without using a wizard.
During this walkthrough, you will learn how to:

Create a new Microsoft Windows Application project.

Add an empty DataSet item to the project.

Create and configure a data source in your application by building a dataset


with the Dataset Designer.

Create a connection to the Northwind database in Server Explorer.

Create DataTables with TableAdapters in the dataset based on tables in


the database.

In order to complete this walkthrough, you will need access to the Northwind
sample database (Microsoft SQL Server or Access version).

To create a new Windows Application


1. On the File menu, create a new project.
2. Create a Windows Application in the language of your choice.
3. Name the project DatasetDesignerWalkthrough, and then click OK.
Visual Studio will add the project to Solution Explorer and display a new
form in the designer.

To add a new Dataset item to the project


1. On the Project menu, click Add New Item.
The Add New Item dialog box appears.
2. In the Templates box of the Add New Item dialog box, click DataSet.
3. Name the Dataset NorthwindDataset, and then click Add.
Visual Studio will add a file called NorthwindDataset.xsd to the project
and open it in the Dataset Designer.

Unit 5: Performing Disconnected Operations by Using Visual Studio 2005 Wizards

157

To create a connection to the Northwind database


1. On the View menu, click Server Explorer.
2. In Server Explorer, click the Connect to Database button.
3. Create a connection to the Northwind sample database. You can connect to
the SQL Server or Access version of Northwind for this walkthrough.

To create the Customers table


1. Expand the data connection you created in Server Explorer, and then expand
the Tables node.
2. Drag the Customers table from Server Explorer onto the Dataset Designer.
A Customers DataTable and CustomersTableAdapter are added to the
dataset.

To create the Orders table


Drag the Orders table from Server Explorer onto the Dataset Designer.
An Orders data table, OrdersTableAdapter, and data relation between the
Customers and Orders tables are added to the dataset.

To create the OrderDetails table


Drag the OrderDetails table from Server Explorer onto the Dataset
Designer.
An OrderDetails data table, OrderDetailsTableAdapter, and a data relation
between the Orders and OrderDetails tables are added to the dataset.

To add functionality to your application

The dataset you just created is now available in the Data Sources window.
On the Data menu, click Show Data Sources to view the data sources. You
can select items in the Data Sources window and drag them onto a form.

Add code to save changes in the dataset back to the database.

Add more queries to the TableAdapters.

Add validation logic to the ColumnChanging or RowChanging events of


the data tables in the dataset.

158

Unit 5: Performing Disconnected Operations by Using Visual Studio 2005 Wizards

How to Create DataRelations with the Dataset Designer


This document is extracted from an article that you can find at the following
location:
In the Microsoft Visual Studio 2005 documentation at

How to: Create DataRelations with the Dataset Designer


mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.VisualSt
udio.v80.en/dv_raddata/html/a3ab4803-0b50-4b74-9920-ab20bfbf1aa2.htm

How to: Create DataRelations with the Dataset Designer


Relationships between DataTables use DataRelation objects to connect
records that are associated with each other, such as a customer and their orders.
When you create data tables with the data design tools in Visual Studio,
relationships are created automatically if the information can be gathered from
the source of your data. If you manually add DataTables or TableAdapters
from the DataSet tab of the Toolbox, you may have to create the relationship
manually as explained in this topic.

To create a DataRelation between two DataTables


1. Open your dataset in the Dataset Designer.
2. Drag a Relation object from the DataSet toolbox onto the child data table
in the relationship. The Relation dialog box opens, populating the Child
Table box with the table you dragged the Relation onto.
3. Select the parent table from the Parent Table box. The parent table contains
records on the one side of a one-to-many relationship.
4. Verify the correct child table is displayed in the Child Table box. The child
table contains records on the many side of a one-to-many relationship.
5. Type a name for the relationship in the Name box, or leave the default name
based on the selected tables. This is the name of the actual DataRelation
object in code.
6. Select the columns that join the tables in the Key Columns and Foreign
Key Columns lists.
7. Select whether to create a relation, a constraint, or both.
8. Select or clear the Nested Relation box. Selecting this option sets the
Nested property to true, and it causes the child rows of the relation to be
nested within the parent column when written as XML data or synchronized
with an XmlDataDocument.
9. Set the rules to be enforced when making changes to records in these tables.
10. Click OK to create the relationship; a relation line appears on the designer
between the two tables. You can toggle showing the relation name on the
design surface by selecting or clearing Show Relation Labels on the Data
menu.

Unit 5: Performing Disconnected Operations by Using Visual Studio 2005 Wizards

159

How to Fill a Typed DataSet by Using a TableAdapter


This document is extracted from an article that you can find at the following
location:
In the Microsoft Visual Studio 2005 documentation at

How to: Fill a Dataset with Data


mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.VisualSt
udio.v80.en/dv_raddata/html/7ab436d4-54ba-4621-902f-3f193279e18c.htm

How to: Fill a DataSet with Data Using a TableAdapter


You can execute SQL statements or stored procedures against a data source
using TableAdapters or command objects. To load data into datasets created
using design tools in Visual Studio, you may wish to use the auto-generated
TableAdapters. To load data into datasets created programmatically, you will
usually use data adapters. If your application does not use datasets, use
command objects to execute SQL statements or stored procedures directly
against a database.
Datasets that are created with Visual Studio design time tools (such as the
Dataset Designer or the Data Source Configuration Wizard) may be filled with
data by executing SQL statements and stored procedures using TableAdapters.
TableAdapters are not actual classes in the Microsoft .NET Framework, so
when creating datasets without design tools, you must use data adapters to fill
and update data.
You can execute TableAdapter queries to fill datasets (more specifically, to
load data into the DataTables that make up a dataset). You can create
TableAdapter queries using the TableAdapter Query Configuration Wizard
in the Dataset Designer. A TableAdapter query appears as a named method on
a TableAdapter and is executed by calling this method.
Filling a dataset with data actually refers to loading data into the individual
DataTable objects that make up the dataset. You fill the data tables by
executing TableAdapter queries or executing data adapter (for example,
SqlDataAdapter) commands. Whether you can use TableAdapters depends
on how you created the dataset. If you used the design tools in Visual Studio,
such as the Data Source Configuration Wizard, your dataset contains
TableAdapters. If you created your dataset programmatically, you will
typically need to create data adapters to load data into the data tables. The data
adapter approach can also be used to populate part or all of a typed dataset if
TableAdapters are not suitable for whatever reason.
When dragging items from the Data Sources window onto a form, the code to
fill the data table with data is automatically added to the Form_Load event
handler. Open the form in the code editor to see the exact syntax to fill your
specific tables. If you do not want to fill the table when the form loads, you can
move this code to some other method or remove it entirely.

160

Unit 5: Performing Disconnected Operations by Using Visual Studio 2005 Wizards

To fill a DataTable by using a TableAdapter, call a query method on the


TableAdapter to load data into the target data table. Pass the DataTable you
want to fill to the TableAdapter query method. If your query takes parameters,
you pass those to the method as well. If the dataset contains multiple tables, you
should have separate TableAdapters for each table, and you must therefore fill
each table separately.
By default, every time you execute a TableAdapter query, the data in the table
is cleared prior to the results of the query being loaded into the table. You can
keep the existing data in the table and append the results by setting the
TableAdapters ClearBeforeFill property to false.

To fill a dataset with data using a TableAdapter


1. Open your form or component in the code editor.
2. Add code anywhere in your application where you need to load a data table
with data. If your query does not take parameters, pass in the DataTable
you want to fill. The code should look similar to the following:
[Visual Basic]
customersTableAdapter.Fill(northwindDataSet.Customers)

[C#]
customersTableAdapter.Fill(northwindDataSet.Customers);

3. If your query method takes parameters, pass in the DataTable you want to
fill and the parameters expected by the query. Depending on the actual
parameters in your query, the code might look similar to the following
examples:
[Visual Basic]
customersTableAdapter.FillByCity(northwindDataSet.Customers, "Seattle")
customersTableAdapter.FillByCityAndState(northwindDataSet.Customers, _
"Seattle", _
"WA")

[C#]
customersTableAdapter.FillByCity(northwindDataSet.Customers, "Seattle");
customersTableAdapter.FillByCityAndState(northwindDataSet.Customers,
"Seattle",
"WA");

Unit 5: Performing Disconnected Operations by Using Visual Studio 2005 Wizards

161

How to Save a Typed DataSet by Using a TableAdapter


This document is extracted from an article that you can find at the following
location:
In the Microsoft Visual Studio 2005 documentation at

How to: Save Dataset Changes to a Database


mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.VisualSt
udio.v80.en/dv_raddata/html/c9970150-b71b-4c9d-a355-5efb6b510dca.htm

How to: Save a Typed DataSet by Using a TableAdapter


After the data in your dataset has been modified and validated, you probably
want to send the updated data back to a database. In order to send the modified
data to a database, you call the Update method of a TableAdapter or data
adapter. The adapters Update method will update a single data table and
execute the correct command (INSERT, UPDATE, or DELETE) based on the
RowState of each data row in the table.
Because attempting to update a data source with the contents of a dataset can
result in errors, you should place the code that calls the adapters Update
method inside of a Try...Catch block.
The exact procedure to update a data source can vary depending on your
business needs, but your application should include the following steps:
1. Call the adapters Update method within a Try...Catch block.
2. If an exception is caught, locate the data row that caused the error.
3. Reconcile the problem in the data row (programmatically if possible, or by
presenting the invalid row to the user for modification), and then reattempt
the update.

162

Unit 5: Performing Disconnected Operations by Using Visual Studio 2005 Wizards

Saving Data from a Single DataTable to a Database with a


TableAdapter
Call the Update method of a TableAdapter or data adapter, passing the data
table that contains the values to be written to the database.

To update a database with a dataset using a TableAdapter


The following example shows how to attempt an update from within a
Try...Catch block with the contents of the Customers DataTable in
NorthwindDataSet.
[Visual Basic]
Try
customersTableAdapter.Update(Me.northwindDataSet.Customers)
Catch ex As Exception
MessageBox.Show("Update failed")
End Try

[C#]
try
{
customersTableAdapter.Update(this.northwindDataSet.Customers);
}
catch (System.Exception ex)
{
MessageBox.Show("Update failed");
}

Updating Two Related Tables in a Dataset with a


TableAdapter
When updating related tables in a dataset, it is important to update in the proper
sequence to reduce the chance of violating referential integrity constraints. The
order of command execution will also follow the indices of the
DataRowCollection in the dataset. To prevent data integrity errors from being
raised, the best practice is to update the database in the following sequence:
1. Child table: delete records.
2. Parent table: insert, update, and delete records.
3. Child table: insert and update records.
You should include all the update logic within a transaction. A transaction is a
process that assures that all related changes to a database are successful before
committing any changes.

Unit 5: Performing Disconnected Operations by Using Visual Studio 2005 Wizards

163

To update two related tables using a TableAdapter


The following example shows how to update a data source with a dataset
that contains related tables. In order to follow the preceding sequence,
three temporary DataTables will be created to hold the differing
records. Then the Update method will be called for each subset of rows
from within a Try...Catch block. If update errors occur, the suggested
course of action is to branch off and resolve them. If all updates are
successful, the transaction commits the changes. Finally, dispose of the
temporary datatables and table adapters to release the resources.
[Visual Basic]
Private Sub UpdateDB()
Dim deletedChildRecords As DataTable = _
northwindDataSet.Orders.GetChanges(Data.DataRowState.Deleted)
Dim newChildRecords As DataTable = _
northwindDataSet.Orders.GetChanges(Data.DataRowState.Added)
Dim modifiedChildRecords As DataTable = _
northwindDataSet.Orders.GetChanges(Data.DataRowState.Modified)
Try
If deletedChildRecords IsNot Nothing Then
OrdersTableAdapter.Update(deletedChildRecords)
deletedChildRecords.Dispose()
End If
CustomersTableAdapter.Update(northwindDataSet.Customers)
If newChildRecords IsNot Nothing Then
OrdersTableAdapter.Update(newChildRecords)
newChildRecords.Dispose()
End If
If modifiedChildRecords IsNot Nothing Then
OrdersTableAdapter.Update(modifiedChildRecords)
modifiedChildRecords.Dispose()
End If
northwindDataSet.AcceptChanges()
Catch ex As Exception
' Update error, resolve and try again.
End Try
End Sub

164

Unit 5: Performing Disconnected Operations by Using Visual Studio 2005 Wizards

[C#]
void UpdateDB()
{
DataTable deletedChildRecords =
northwindDataSet.Orders.GetChanges(DataRowState.Deleted);
DataTable newChildRecords =
northwindDataSet.Orders.GetChanges(DataRowState.Added);
DataTable modifiedChildRecords =
northwindDataSet.Orders.GetChanges(DataRowState.Modified);
try
{
if (deletedChildRecords != null)
{
OrdersTableAdapter.Update(deletedChildRecords);
deletedChildRecords.Dispose();
}
CustomersTableAdapter.Update(northwindDataSet.Customers);
if (newChildRecords != null)
{
OrdersTableAdapter.Update(newChildRecords);
newChildRecords.Dispose();
}
if (modifiedChildRecords != null)
{
OrdersTableAdapter.Update(modiifiedChildRecords);
modifiedChildRecords.Dispose();
}
northwindDataSet.AcceptChanges();
}
catch (Exception ex)
{
// Update error, resolve and try again.
}
}

Unit 5: Performing Disconnected Operations by Using Visual Studio 2005 Wizards

165

How to Display a Typed DataSet in a Windows Form


This document is extracted from articles that you can find at the following
locations:
In the Microsoft Visual Studio 2005 documentation at

How to: Bind Data to Existing Controls


mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.VisualSt
udio.v80.en/dv_raddata/html/bc5f59f1-e8dc-45c1-b4ab-4c9543aad7f1.htm

How to: Display Data in a Microsoft Windows Forms Data Grid


mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.VisualSt
udio.v80.en/dv_raddata/html/edaf1885-a9ce-418a-a688-01be2c64b661.htm

How to: Display Data in Individual Windows Forms Controls


mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.VisualSt
udio.v80.en/dv_raddata/html/0163a34a-38cb-40b9-8f38-3058a90caf21.htm

How to: Display Related Data on Windows Forms


mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.VisualSt
udio.v80.en/dv_raddata/html/60b1f1ec-6257-42ab-83f0-06d54ed364fd.htm

How to: Add a Parameterized Query to a Windows Form


mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.VisualSt
udio.v80.en/dv_raddata/html/13db4ad3-56b9-4a0b-b3a5-6a4ff84d4416.htm

How to: Bind Data to Existing Controls


The Microsoft .NET Framework provides extensive support for binding user
interface controls to data sources, such as a DataTable.
You can drag items from the Data Sources window onto existing controls at
any time during the development process to bind the control to the selected
item. When you drag an item onto a control that is already on a form, the
control is then bound to that item. If a control is already bound to data, it will be
bound to the item most recently dropped onto it.
Controls must be capable of displaying the underlying data type of the item
dragged from the Data Sources window to be valid drop targets. For example,
dragging an item that has a data type of DateTime from the Data Sources
window onto a CheckBox is invalid, because the CheckBox is not capable of
displaying a date.

166

Unit 5: Performing Disconnected Operations by Using Visual Studio 2005 Wizards

To bind an existing control to data in a data source


1. Open the form in the Windows Forms Designer.
2. Expand a table in the Data Sources window to display its individual
columns.
To open the Data Sources window, click Show Data Sources on the Data
menu.
3. Drag the desired item from the Data Sources window onto an existing
control.
The control is now bound to the selected item.

How to: Display Data in a Windows Forms Data Grid


You can display data in a DataGridView control by dragging items from the
Data Sources window onto your form. Prior to dragging from the Data
Sources window, right-click the data source and set its type to DataGridView.
An alternative to displaying data in a grid is to display the data in individual
controls.
Data can be displayed in a new DataGridView that is created when items from
the Data Sources window are dragged onto empty areas of the form, or data
can be displayed in an existing DataGridView, where items are dragged onto
an existing grid. When you drag items onto an existing grid, any previously
established data bindings are replaced with the new items.

To display data in a new Windows Forms DataGridView control


1. Open the Data Sources window.
If the Data Sources window is empty, add a data source to it.
2. Open the form in the Windows Forms Designer.
3. Select a table in the Data Sources window, click the drop-down arrow, and
then select DataGridView.
4. Drag the table from the Data Sources window onto a form.
A DataGridView control and a tool strip (BindingNavigator) for
navigating records appear on the form. A DataSet, TableAdapter,
BindingSource, and BindingNavigator appear in the component tray.

To display data in an existing Windows Forms DataGridView control


1. Open the Data Sources window.
If the Data Sources window is empty, add a data source to it.
2. Open your form in the Windows Forms Designer.
3. Select a table in the Data Sources window, click the drop-down arrow, and
then select DataGridView.
4. Drag the table from the Data Sources window onto a DataGridView on the
form.
5. The DataGridView control is now bound to the table dropped onto it. A
DataSet, TableAdapter, and BindingSource appear in the component tray.

Unit 5: Performing Disconnected Operations by Using Visual Studio 2005 Wizards

167

How to: Display Data in Individual Windows Forms Controls


You display data on Windows Forms in individual controls by dragging objects
from the Data Sources window onto a form. Prior to dragging from the Data
Sources window, set the control type of the table to Details.
You can display an entire table of data in individual controls by dragging the
table (or a node representing a collection if you are using an object data source)
from the Data Sources window onto a form in a Windows application.
You can also display selected columns of data in individual controls by
dragging the individual columns (or properties if you are using an object data
source) from the Data Sources window onto a form in a Windows application.

To display an entire table of data


1. Open the Data Sources window.
If the Data Sources window is empty, add a data source to it.
2. Open the form in the Windows Forms Designer.
3. Select a table in the Data Sources window, click the drop-down arrow, and
then select Details.
4. Drag the table from the Data Sources window onto a form.
An individual data-bound control for each column or property is created on
the form, accompanied by an appropriately titled Label control.

To display select columns of data


1. Open the Data Sources window.
If the Data Sources window is empty, add a data source to it.
2. Expand the table to display the individual columns.
To set the control that is created for each column, select the column in the
Data Sources window, click the drop-down arrow, and select a control from
the list of available controls.
3. Open the form in the Windows Forms Designer.
4. Drag the desired columns from the Data Sources window onto a form.
For each column or property you drag, an individual data-bound control is
created on the form, accompanied by an appropriately titled Label control.

168

Unit 5: Performing Disconnected Operations by Using Visual Studio 2005 Wizards

How to: Display Related Data on Windows Forms


You can display related data by dragging items that share the same main table
node from the Data Sources window onto your form. For example, if you have
a data source that has a customers table and a related orders table, you would
see both tables as top-level nodes (in the tree view) in the Data Sources
window. Expand the customers node so that you can see the columns, and you
will notice the last column in the list is an expandable node representing the
orders table. This node represents the related orders for a customer. This means
that if you want to create a form that allows you to select a customer and then
display a list of orders for that customer, you would drag the items you want to
display from this single hierarchy.

To create controls that display related records


1. Open your form in the Windows Forms Designer.
2. Open the Data Sources window.
3. Expand the node representing the parent table in the relationship. (The
parent table is the table on the one side of a one-to-many relationship.)
4. Drag items you want to display from the parent table in the relationship
from the Data Sources window onto your form.
5. Related child tables appear as expandable nodes at the bottom of the parent
tables column list. Drag items you want to display from this related node
onto your form.
Dragging items from each of the top-level nodes creates separate unrelated
BindingSource Components that do not facilitate navigating the related
records.

Unit 5: Performing Disconnected Operations by Using Visual Studio 2005 Wizards

169

How to Add Code to a DataSet


This document is extracted from an article that you can find at the following
location:
In the Microsoft Visual Studio 2005 documentation at

How to: Add Code to a Dataset


mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.VisualSt
udio.v80.en/dv_raddata/html/dfbc21eb-7ea2-4942-addd-49677f5493be.htm

How to: Add Code to a DataSet


You can add code to extend the functionality of typed datasets by writing code
into the datasets partial class file.
The code that defines typed datasets is generated when changes are made in the
Dataset Designer or when changes are made during the running of the Data
Source Configuration Wizard. To prevent your code from being deleted during
regeneration of the dataset, add code to the datasets partial class file (rather
than to the designer-generated portion of the partial class).
For example, to add validation code to a dataset, you could double-click
columns and tables, which results in the generation of ColumnChanging and
RowChanging event handlers.

To add code to a typed datasets partial class file


1. Open the dataset in the Dataset Designer.
2. Double-click the design surface in an empty area to open the datasets
partial class file in the code editor.
3. Add your code to the datasets partial class file.
The code in the partial class file and in the generated dataset class file is
compiled into one class, so variable names, property names, method names,
and so on must be unique across all files that define the dataset, or compiler
errors will occur.

170

Unit 5: Performing Disconnected Operations by Using Visual Studio 2005 Wizards

How to Use the Data Source Configuration Wizard


This document is extracted from an article that you can find at the following
location:
In the Microsoft Visual Studio 2005 documentation at

Data Source Configuration Wizard


mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.VisualSt
udio.v80.en/dv_raddata/html/c4df7de5-5da0-4064-940c-761dd6d9e28f.htm

How to: Use the Data Source Configuration Wizard


The Data Source Configuration Wizard creates and edits data sources in your
application. The wizard creates and edits data sources made from databases,
Web services, or objects.
After running the wizard, the data source is available in the Data Sources
window for dragging onto Microsoft Windows Forms.

Running the Wizard


There are two ways to run the wizard:

Selecting Add New Data Source from the Data menu or from the Data
Sources window.

Selecting Add Project Data Source from the DataSource property of


certain Windows Forms controls.

Depending on the selected data source type (database, Web service, or object),
the wizard will take you to any one of a number of pages in the wizard.

Choose a Data Source type

Choose your data connection

Save the connection string to the application configuration file

Choose your database objects

Add Web Reference

Select the Object you wish to bind to

Unit 5: Performing Disconnected Operations by Using Visual Studio 2005 Wizards

171

Choose a Data Source Type


Select the type of data source to create from the available options listed on the
Choose a Data Source type page. Currently supported data source types
include databases, Web services, and objects. The end result of running the
wizard is determined by the data source type selected.
The following table shows the results of selecting each type:
Data source type

Result in project

Database

A typed dataset (.xsd file).

Web Service

The resulting data source is determined by the objects


returned from the Web service.

Object

If the object is not located in the project a reference to the


selected object is added.

Choose Your Data Connection


Select an existing connection from the list of connections, or click New
Connection to open the Add Connection dialog box and create a connection to
your database.
Upon completion of the Add Connection dialog box, the Connection string
area may be expanded to display information on the selected provider as well as
on the connection string. This information is read-only.

Save the Connection String to the Application


Configuration File
Select Yes, save the connection as to store the connection string in the
application configuration file. Type a name for the connection or use the
provided default name.
Saving connection strings in the application configuration file simplifies the
process of maintaining your application if the database connection changes. In
the event of a change in the database connection, you can edit the connection
string in the application configuration file as opposed to editing the source code
and having to recompile your application.
Information is stored in the application configuration file as plain text. To
reduce the possibility of unauthorized access to sensitive information, you may
want to encrypt your data.

Choose Your Database Objects


Select the objects from your database to bring into the application from the
available objects displayed in the tree view and provide a name for the dataset
in the Dataset name area.

172

Unit 5: Performing Disconnected Operations by Using Visual Studio 2005 Wizards

Add Web Reference


Completing the Add Web Reference dialog box adds a reference to the Web
service in your project and populates the Data Sources window with objects
returned from the Web service. The data source created from the Web service
contains the items returned from the Web service. For example, if the Web
service returns a dataset, the data source created is the dataset, whereas if the
Web service returns a type, the data source is the type returned.

Select the Object You Wish to Bind To


Select the object to bind to from the tree view. The tree view only displays
objects currently in your application. To bind to objects external to your
application, click Add Reference and select the desired assembly in the Add
Reference dialog box.
After adding the reference, navigate the tree view to the desired type and click
Next.

Unit 5: Performing Disconnected Operations by Using Visual Studio 2005 Wizards

173

How to Edit, Add, and View Queries in a TableAdapter


This document is extracted from articles that you can find at the following
locations:
In the Microsoft Visual Studio 2005 documentation at

TableAdapter Query Configuration Wizard


mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.VisualSt
udio.v80.en/dv_raddata/html/8c06b306-24d0-4521-948d07e3b7badd95.htm

How to: Start the TableAdapter Query Configuration Wizard


mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.VisualSt
udio.v80.en/dv_raddata/html/fc7b468e-3417-48a4-a8aa-cace8f99c24a.htm

How to: Create TableAdapter Queries


mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.VisualSt
udio.v80.en/dv_raddata/html/df0cf4a5-e9cc-4de6-8b94-ce74fb7b2452.htm

How to: Edit TableAdapter Queries


mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.VisualSt
udio.v80.en/dv_raddata/html/aac7b7b4-bd91-4225-95d4-a07643568c43.htm

How to: View the Queries in a TableAdapter


mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.VisualSt
udio.v80.en/dv_raddata/html/2619484d-708f-492f-963a-04aa16bc1a48.htm

How to: Edit and Add Queries in a TableAdapter


TableAdapters can contain multiple query methods to fill their associated data
tables. You can define as many queries for a TableAdapter as your application
requires, as long as each query returns data that conforms to the same schema as
its associated data table.
The first query in a TableAdapter is the TableAdapters main query. Editing
this main query opens the TableAdapter Configuration Wizard and edits the
schema of the TableAdapters data table.
All queries listed below the main query are additional queries and are
configured using the TableAdapter Query Configuration Wizard. You can
also edit a TableAdapter query when the existing query no longer suits the
needs of your application.
In addition to queries that return data of the same schema as a TableAdapters
data table, you can add queries that return scalar values. For example, creating a
query that returns a count of customers (SELECT COUNT(*) From Customers)
is a valid query for a TableAdapter even though the data returned does not
conform to the tables schema. Only queries that return the same schema or
scalar values are acceptable.

174

Unit 5: Performing Disconnected Operations by Using Visual Studio 2005 Wizards

After you complete the wizard, a method is added to the TableAdapter that,
when called, executes the query (for example,
customersTableAdapter.FillByCity(northwindDataSet.Customers,
"Seattle").

To start the TableAdapter Query Configuration Wizard


1. Open your dataset in the Dataset Designer.
2. To create a new TableAdapter query:
a. Drag a Query object from the DataSet tab of the Toolbox onto a
DataTable, or select Add Query from the TableAdapters shortcut
menu. (Dragging a Query onto an empty area of the Dataset Designer
only allows the creation of a scalar function.)
b. On the Choose a Command Type page, select from the following
methods of fetching data from the database:
Use SQL statements. Select this option if you want to type an SQL
statement to select the data from your database.
Create new stored procedure. Select this option to have the wizard
create a new stored procedure (in the database) based on the
specified SELECT statement.
Use existing stored procedure. Select this option to execute an
existing stored procedure when running the query.
3. To edit an existing TableAdapter query:
Right-click the query on the Dataset Designer, and select Configure
from the shortcut menu.
Note that right-clicking the main query of a TableAdapter reconfigures
the TableAdapter and DataTable schema, whereas right-clicking an
additional query on a TableAdapter configures only the selected query.
The TableAdapter Configuration Wizard reconfigures the
TableAdapter definition; the TableAdapter Query Configuration
Wizard reconfigures only the selected query.

To create a new query by using SQL statements


1. On the Choose a Query Type page, select the type of query to create from
the list of available queries.
The wizard creates several types of queries depending on the requirements
of your application. You can choose SELECT queries that return rows of
data (a data table) or SELECT queries that return scalar values (a single
value such as Count or Sum).
Creating an INSERT, UPDATE, or DELETE statement does not replace the
TableAdapters commands that are used when calling the TableAdapters
Update method. For example, selecting UPDATE as a query type will
create a new query with a name you specify later in the wizard. You execute
this query by calling this named method of the TableAdapter. Calling the
TableAdapters Update method will execute statements created when the
original TableAdapter was configured.
2. On the Specify a SQL <query type> Statement page, type the SQL
statement to execute when calling the query.

Unit 5: Performing Disconnected Operations by Using Visual Studio 2005 Wizards

175

3. If the Choose Methods to Generate page is displayed, select which


methods the wizard generates for the query.
Fill a DataTable. Creates a method for filling the data table. You pass
the name of the data table as a parameter when calling this method to fill
the data table with the returned data. Optionally, you can change the
default name in the Method Name box. Providing a meaningful name
can be helpful when working with this query in code.
Return a DataTable. Creates a method for returning a filled data table.
In certain applications, it can be more desirable to return a filled data
table as opposed to filling the existing data table with data. Optionally,
you can change the default name in the Method Name box.
4. If the Choose Function Name page is displayed, type a name for the
function. Creating a TableAdapter query adds a method to the
TableAdapter with the name provided here. Call this method to execute the
query. Providing a meaningful name is helpful when working with this
query in code. When creating new stored procedures, you are asked for two
names. The first name is the name of the stored procedure created in the
database; the second name is the name of the method on the TableAdapter
that executes the stored procedure when called.

To create a new query by using new stored procedures


1. On the Choose a Query Type page, select the type of query to create from
the list of available queries.
2. In the Generate the stored procedure page, type the SQL statement to
execute when calling the stored procedure.
3. On the Create the stored procedure page, do the following:
a. Type a name for the new stored procedure.
b. Select whether to create the stored procedure in the underlying database.

To create a new query by using existing stored procedures


1. Select an existing stored procedure from the drop-down list on the Choose
an existing stored procedure page of the wizard.
The Parameters and Results for the selected stored procedure are displayed
for reference.
2. On the Choose the shape of data returned by the stored procedure page,
specify whether the stored procedure returns tabular data (a result set), a
single value, or no value.
3. If the Choose Methods to Generate page is displayed, select which
methods the wizard generates for the query.
Fill a DataTable
Return a DataTable
4. If the Choose Function Name page is displayed, type a name for the
function.

176

Unit 5: Performing Disconnected Operations by Using Visual Studio 2005 Wizards

How to: View the Queries in a TableAdapter


TableAdapters can contain three types of query:

The main Fill query, which is used to define the schema of the
TableAdapters data table.

Additional queries that can be used to fill the table as well. (The additional
queries do not define the schema of the table, but they do need to return data
that conforms to that schema, or return a scalar value.)

Additionally, depending on the structure of the main Fill query, a


TableAdapter can contain INSERT, UPDATE, and DELETE queries
(typically referred to as commands) that are used to send updated data back
to the database.

To view a TableAdapters main Fill query


1. Open the dataset in the Dataset Designer.
2. Right-click the TableAdapters title bar and select Configure.
The TableAdapter Configuration Wizard opens, displaying the SQL
statement used to fill the table or the stored procedure that is called to fill
the table.

To view a TableAdapter query


1. Open the dataset in the Dataset Designer.
2. Right-click the TableAdapter query you want to view, and select
Configure.
The TableAdapter Query Configuration Wizard opens, displaying the
SQL statement for the query.

To view a TableAdapter's updating commands


1. Open the dataset in the Dataset Designer.
2. Select the TableAdapter by clicking the TableAdapters title bar.
3. Locate and expand the command you want to view in the Properties
window.
The SQL statement or stored procedure name is viewable in the
CommandText property.

Unit 6: Performing XML Operations on


Disconnected Data
Database Tables and Stored Procedures
Introduction
In the current phase of development of the OnTheRoad application, you will
use the following tables in the Adventure Works database:

Production.ProductCategory

Production.ProductSubcategory

Production.Product

Production.ProductReview

Sales.SalesVisit

This resource document describes these tables and related stored procedures.

Production.ProductCategory Table
The Production.ProductCategory table in the Adventure Works database
contains information about product categories in the Adventure Works
organization. For example, there are product categories named Accessories,
Bikes, Clothing, and Components. The following table describes the schema of
the Production.ProductCategory table.
Column name

SQL data type

Description

ProductCategoryID

int

Primary key column

Name

nvarchar(50)

Category description

rowguid

uniqueidentifier

ROWGUIDCOL number that uniquely


identifies the record (for merge
replication)

ModifiedDate

datetime

Date and time the record was updated

178

Unit 6: Performing XML Operations on Disconnected Data

Production.ProductSubcategory Table
The Production.ProductSubcategory table in the Adventure Works database
contains information about product subcategories within particular product
categories. For example, within the Bikes product category, there are product
subcategories named Mountain Bikes, Road Bikes, and Touring Bikes. The
following table describes the schema of the Production.ProductSubcategory
table.
Column name

SQL data type

Description

ProductSubcategoryID

int

Primary key column

ProductCategoryID

int

Foreign key to the ProductCategoryID


column in the
Production.ProductCategory table

Name

nvarchar(50)

Subcategory description

rowguid

uniqueidentifier

ROWGUIDCOL number that uniquely


identifies the record (for merge
replication)

ModifiedDate

datetime

Date and time the record was updated

You will use the following stored procedure to query data in the
Production.ProductSubcategory table:

uspGetProductSubcategories

uspGetProductSubcategories
This stored procedure returns a result set that contains all the records from the
Production.ProductSubcategory table. The stored procedure does not have
any parameters.

Production.Product Table
The Production.Product table in the Adventure Works database contains
information about products sold by the Adventure Works organization. The
following table describes the schema of the Production.Product database
table.
Column name

SQL data type

Description

ProductID

int

Primary key column

Name

nvarchar(50)

Name of the product

ProductNumber

nvarchar(25)

Unique product number

MakeFlag

bit

0 = Product is purchased

FinishedGoodsFlag

bit

0 = Product is not scalable

1 = Product is made in-house


1 = Product is scalable
Color

nvarchar(15)

Product color

SafetyStockLevel

smallint

Minimum inventory quantity

ReorderPoint

smallint

Inventory level that triggers a purchase


order or work order

StandardCost

money

Standard cost of the product

Unit 6: Performing XML Operations on Disconnected Data

179

(continued)
Column name

SQL data type

Description

ListPrice

money

Selling price

Size

nvarchar(5)

Product size

SizeUnitMeasureCost

nchar(3)

Unit of measure for size

WeightUnitMeasureCost

nchar(3)

Unit of measure for weight

Weight

decimal(8, 2)

Product weight

DaysToManufacture

int

Number of days required


to manufacture the product

ProductLine

nchar(2)

R = Road
M = Mountain
T = Touring
S = Standard

Class

nchar(2)

H = High
M = Medium
L = Low

Style

nchar(2)

W = Womens
M = Mens
U = Universal

ProductSubcategoryID

int

Foreign key to the


ProductSubcategoryID
column in the
ProductSubcategory table

ProductModelID

int

Foreign key to the


ProductModelID column
in the ProductModel table

SellStartDate

datetime

Date the product was


available for sale

SellEndDate

datetime

Date the product was no


longer available for sale

DiscontinuedDate

datetime

Date the product was


discontinued

rowguid

uniqueidentifier

ROWGUIDCOL number
that uniquely identifies the
record (for merge
replication)

ModifiedDate

datetime

Date and time the record


was last updated

180

Unit 6: Performing XML Operations on Disconnected Data

Production.ProductReview Table
The Production.ProductReview table in the Adventure Works database
contains product review information. The following table describes the schema
of the Production.ProductReview database table.
Column name

SQL data type

Description

ProductReviewID

int

Primary key column

ProductID

int

Foreign key to the ProductID column in


the Production.Product table

ReviewerName

nvarchar(50)

Name of the reviewer

ReviewDate

datetime

Date review was submitted

EmailAddress

nvarchar(50)

Reviewers e-mail address

Rating

int

Product rating given by the reviewer


Minimum rating: 1
Maximum rating: 5

Comments

nvarchar(3850)

Reviewers comments

ModifiedDate

datetime

Date and time the record was last


updated

Sales.SalesVisit Table
The Sales.SalesVisit table in the Adventure Works database contains
information about forthcoming sales visits for salespeople at the Adventure
Works organization. The following table describes the schema of the
Sales.SalesVisit table.
Column name

SQL data type

Description

AppointmentID

int

Primary key column

SalesPersonID

int

ID of the salesperson for the sales visit

DateAndTime

datetime

Date and time of the sales visit

Arrangements

nvarchar(100)

Description of the arrangements for the


sales visit, such as the location and
contact details

Notes

nvarchar(250)

Notes that the salesperson can make


before or after the sales visit, to
describe the purpose or outcome of the
sales visit

Unit 6: Performing XML Operations on Disconnected Data

181

You will use the following stored procedure to query data in the
Production.ProductSubcategory table:

uspGetSalesVisitsBySalesPersonAndDate

uspGetSalesVisitsBySalesPersonAndDate
This stored procedure returns a result set that contains all the sales visits for a
particular salesperson after a specified date and time. The stored procedure has
two parameters, as described in the following table.
Parameter name

SQL data type

Description

@prmSalesPersonID

int

The stored procedure will return sales


visits for this salesperson

@prmFromDate

datetime

The stored procedure will return sales


visits after this date

182

Unit 6: Performing XML Operations on Disconnected Data

How to Save a Dataset as XML Data


This document is extracted from an article that you can find at the following
location:
In the Microsoft Visual Studio 2005 documentation at

Writing a DataSet as XML Data


mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.VisualSt
udio.v80.en/WD_ADONET/html/fd15f8a5-3b4c-46d0-a5614559ab2a4705.htm

How to: Save a Dataset as XML Data


The XML representation of the dataset can be written to a file, a stream, an
XmlWriter, or a string. These choices provide great flexibility for how you
transport the XML representation of the dataset. To obtain the XML
representation of the dataset as a string, use the GetXml method, as shown in
the following example.
[Visual Basic]
Dim xmlDS As String = custDS.GetXml()

[C#]
string xmlDS = custDS.GetXml();

GetXml returns the XML representation of the dataset without schema


information. To write the schema information from the dataset (as XML
Schema) to a string, use GetXmlSchema.
To write a dataset to a file, a stream, or an XmlWriter, use the WriteXml
method. The first parameter you pass to WriteXml is the destination of the
XML output. For example, pass a string containing a file path, a
System.IO.TextWriter object, and so on. You can pass an optional second
parameter of an XmlWriteMode to specify how the XML output is to be
written.
The following table shows the options for XmlWriteMode.
XmlWriteMode

Description

IgnoreSchema

Writes the current contents of the dataset as XML data without an


XML Schema. This is the default.

WriteSchema

Writes the current contents of the dataset as XML data with the
dataset structure as inline XML Schema.

DiffGram

Writes the entire dataset as a DiffGram, including original and


current values.

Unit 6: Performing XML Operations on Disconnected Data

183

When writing an XML representation of a dataset that contains DataRelation


objects, you will most likely want the resulting XML to have the child rows of
each relation nested within their related parent elements. To accomplish this, set
the Nested property of the DataRelation to true.
The following example shows how to write the XML representation of a dataset
to a file, by specifying a file name.
[Visual Basic]
custDS.WriteXml("Customers.xml", XmlWriteMode.WriteSchema)

[C#]
custDS.WriteXml("Customers.xml", XmlWriteMode.WriteSchema);

The following example shows how to write the XML representation of a dataset
to a file, by providing a System.IO.StreamWriter object.
[Visual Basic]
Dim writer As New System.IO.StreamWriter("Customers.xml")
custDS.WriteXml(writer, XmlWriteMode.WriteSchema)
writer.Close()

[C#]
System.IO.StreamWriter xmlSW = new System.IO.StreamWriter("Customers.xml");
custDS.WriteXml(xmlSW, XmlWriteMode.WriteSchema);
xmlSW.Close();

Mapping Columns to XML Elements, Attributes, and Text


You can specify how a column of a table is represented in XML using the
ColumnMapping property of the DataColumn object. The following table
shows the different MappingType values for the ColumnMapping property of
a table column and the resulting XML.
MappingType

Description

Element

The column is written as an XML element. The ColumnName is


the name of the element, and the contents of the column are
written as the text of the element. For example:
<ColumnName>Column Contents</ColumnName>

Attribute

The column is written as an XML attribute. The ColumnName is


the name of the attribute, and the contents of the column are
written as the value of the attribute. For example: <RowElement
ColumnName="Column Contents" />

SimpleContent

The contents of the column are written as text in the XML


element for the current row. For example:
<RowElement>Column Contents</RowElement>
You cannot use the SimpleContent mapping type for a column of
a table that has Element columns or nested relations.

Hidden

The column will not be written in the XML output.

184

Unit 6: Performing XML Operations on Disconnected Data

How to Nest DataRelations


This document is extracted from an article that you can find at the following
location:
In the Microsoft Visual Studio 2005 documentation at

Nested DataRelations
mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.VisualSt
udio.v80.en/WD_ADONET/html/9530f9c9-dd98-4b93-8cdb40d7f1e8d0ab.htm

How to: Nest DataRelations


In a relational representation of data, individual tables contain rows that are
related to one another using a column or set of columns. In the Microsoft
ADO.NET dataset, the relationship between tables is implemented using a
DataRelation.
When you create a DataRelation, the parent-child relationships of the columns
are managed only through the relation. The tables and columns are separate
entities. In the hierarchical representation of data that XML provides, the
parent-child relationships may be represented by parent elements that contain
nested child elements. To facilitate the nesting of child objects when a dataset is
written as XML data using WriteXml (or synchronized with an
XmlDataDocument object), the DataRelation exposes a Nested property.
Setting the Nested property of a DataRelation to true causes the child rows of
the relation to be nested within the parent column when written as XML data or
synchronized with an XmlDataDocument. The Nested property of the
DataRelation is false, by default.
For example, consider the following dataset:

Unit 6: Performing XML Operations on Disconnected Data

185

[Visual Basic]
Dim nwindConn As New SqlConnection( _
"Data Source=localhost; Integrated Security=SSPI; Initial Catalog=Northwind;")
Dim custDA As New SqlDataAdapter( _
"SELECT CustomerID, CompanyName FROM Customers", nwindConn)
Dim orderDA As New SqlDataAdapter( _
"SELECT OrderID, CustomerID, OrderDate FROM Orders", nwindConn)
nwindConn.Open()
Dim custDS As New DataSet("CustomerOrders")
custDA.Fill(custDS, "Customers")
orderDA.Fill(custDS, "Orders")
nwindConn.Close()
Dim custOrderRel As DataRelation = _
custDS.Relations.Add("CustOrders", _
custDS.Tables("Customers").Columns("CustomerID"), _
custDS.Tables("Orders").Columns("CustomerID"))

[C#]
SqlConnection nwindConn = new SqlConnection(
"Data Source=localhost; Integrated Security=SSPI; Initial Catalog=Northwind;");
SqlDataAdapter custDA = new SqlDataAdapter(
"SELECT CustomerID, CompanyName FROM Customers", nwindConn);
SqlDataAdapter orderDA = new SqlDataAdapter(
"SELECT OrderID, CustomerID, OrderDate FROM Orders", nwindConn);
nwindConn.Open();
DataSet custDS = new DataSet("CustomerOrders");
custDA.Fill(custDS, "Customers");
orderDA.Fill(custDS, "Orders");
nwindConn.Close();
DataRelation custOrderRel =
custDS.Relations.Add("CustOrders",
custDS.Tables["Customers"].Columns["CustomerID"],
custDS.Tables["Orders"].Columns["CustomerID"]);

Because the Nested property of the DataRelation object is not set to true for
this dataset, the child objects will not be nested within the parent elements when
this dataset is represented as XML data.

186

Unit 6: Performing XML Operations on Disconnected Data

The following code example shows the output that will result from calling
WriteXml on the dataset.
<CustomerOrders>
<Customers>
<CustomerID>ALFKI</CustomerID>
<CompanyName>Alfreds Futterkiste</CompanyName>
</Customers>
<Customers>
<CustomerID>ANATR</CustomerID>
<CompanyName>Ana Trujillo Emparedados y helados</CompanyName>
</Customers>
<Orders>
<OrderID>10643</OrderID>
<CustomerID>ALFKI</CustomerID>
<OrderDate>1997-08-25T00:00:00</OrderDate>
</Orders>
<Orders>
<OrderID>10692</OrderID>
<CustomerID>ALFKI</CustomerID>
<OrderDate>1997-10-03T00:00:00</OrderDate>
</Orders>
<Orders>
<OrderID>10308</OrderID>
<CustomerID>ANATR</CustomerID>
<OrderDate>1996-09-18T00:00:00</OrderDate>
</Orders>
</CustomerOrders>

Note that the Customers elements and the Orders elements are shown as
sibling elements. If you wanted to have the Orders elements show up as
children of their respective parent elements, the Nested property of the
DataRelation would need to be set to true and you would add the following:
[Visual Basic]
custOrderRel.Nested = True

[C#]
custOrderRel.Nested = true;

Unit 6: Performing XML Operations on Disconnected Data

The following code shows what the resulting output would look like, with the
Orders elements nested within their respective parent elements.
<CustomerOrders>
<Customers>
<CustomerID>ALFKI</CustomerID>
<Orders>
<OrderID>10643</OrderID>
<CustomerID>ALFKI</CustomerID>
<OrderDate>1997-08-25T00:00:00</OrderDate>
</Orders>
<Orders>
<OrderID>10692</OrderID>
<CustomerID>ALFKI</CustomerID>
<OrderDate>1997-10-03T00:00:00</OrderDate>
</Orders>
<CompanyName>Alfreds Futterkiste</CompanyName>
</Customers>
<Customers>
<CustomerID>ANATR</CustomerID>
<Orders>
<OrderID>10308</OrderID>
<CustomerID>ANATR</CustomerID>
<OrderDate>1996-09-18T00:00:00</OrderDate>
</Orders>
<CompanyName>Ana Trujillo Emparedados y helados</CompanyName>
</Customers>
</CustomerOrders>

187

188

Unit 6: Performing XML Operations on Disconnected Data

How to Load a Dataset from XML


This document is extracted from an article that you can find at the following
location:
In the Microsoft Visual Studio 2005 documentation at

Loading a DataSet from XML


mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.VisualSt
udio.v80.en/WD_ADONET/html/49c083b7-a5ed-41cf-aabc5aaba96f00e6.htm

How to: Load a Dataset from XML


To fill a dataset with data from XML, use the ReadXml method defined in the
DataSet class. The ReadXml method will read from a file, a stream, or an
XmlReader and takes as arguments the source of the XML plus an optional
XmlReadMode argument.
The ReadXml method reads the contents of the XML stream or document and
loads the dataset with data. It will also create the relational schema of the
dataset depending on the XmlReadMode specified and whether a relational
schema already exists.
The following table describes the options for the XmlReadMode argument.
XmlReadMode

Description

Auto

Examines the XML and chooses the most appropriate option in


the following order:

If the XML is a DiffGram, DiffGram is used.


If the dataset contains a schema or the XML contains an
inline schema, ReadSchema is used.
If the dataset does not contain a schema and the XML does
not contain an inline schema, InferSchema is used.

If you know the format of the XML being read, for best
performance it is recommended that you set an explicit
XmlReadMode rather than allowing the Auto default.
ReadSchema

Reads any inline schema and loads the data and schema.
If the dataset already contains a schema, new tables are added
from the inline schema to the existing schema in the dataset. If
any tables in the inline schema already exist in the dataset, an
exception is thrown. You will not be able to modify the schema of
an existing table using XmlReadMode.ReadSchema.
If the dataset does not contain a schema, and there is no inline
schema, no data is read.
Inline schema can be defined using XML Schema definition
language (XSD) schema.

Unit 6: Performing XML Operations on Disconnected Data

189

(continued)
XmlReadMode

Description

IgnoreSchema

Ignores any inline schema and loads the


data into the existing dataset schema. Any
data that does not match the existing
schema is discarded. If no schema exists
in the dataset, no data is loaded.
If the data is a DiffGram, IgnoreSchema
has the same functionality as DiffGram.

InferSchema

Ignores any inline schema and infers the


schema per the structure of the XML data,
and then loads the data.
If the dataset already contains a schema,
the current schema is extended by adding
new tables where there is no existing
table, or by adding columns to existing
tables. An exception is thrown if an
inferred table already exists with a
different namespace, or if any inferred
columns conflict with existing columns.

DiffGram

Reads a DiffGram and adds the data to the


current schema. DiffGram merges new
rows with existing rows where the unique
identifier values match. See the note on
Merging Data from XML at the end of
this topic.

Fragment

Continues reading multiple XML


fragments until the end of the stream is
reached. Fragments that match the dataset
schema are appended to the appropriate
tables. Fragments that do not match the
dataset schema are discarded.

The following code example shows how to load a dataset from an XML file, by
passing the XML file name to the ReadXml method.
[Visual Basic]
Dim myDS As New DataSet()
myDS.ReadXml("input.xml", XmlReadMode.ReadSchema)

[C#]
DataSet myDS = new DataSet();
myDS.ReadXml("input.xml", XmlReadMode.ReadSchema);

190

Unit 6: Performing XML Operations on Disconnected Data

The following code example shows how to load a dataset from a string that
contains XML, by passing a System.IO.StringReader object to the ReadXml
method.
[Visual Basic]
Dim myDS As New DataSet()
Dim myTable As New DataTable("table1")
myTable.Columns.Add("col1", Type.GetType("System.String"))
myDS.Tables.Add(myTable)
Dim xmlData As String = _
"<XmlDS> & _

<table1><col1>Value1</col1></table1> & _

<table1><col1>Value2</col1></table1> & _
</XmlDS>"
Dim reader As New System.IO.StringReader(xmlData)
myDS.ReadXml(reader, XmlReadMode.IgnoreSchema)

[C#]
DataSet myDS = new DataSet();
DataTable myTable = new DataTable("table1");
myTable.Columns.Add("col1", typeof(string));
myDS.Tables.Add(myTable);
string xmlData =
"<XmlDS> +

<table1><col1>Value1</col1></table1> +

<table1><col1>Value2</col1></table1> +
</XmlDS>";
System.IO.StringReader reader = new System.IO.StringReader(xmlData);
myDS.ReadXml(reader, XmlReadMode.IgnoreSchema);

If you call ReadXml to load a very large file, you may encounter slow
performance. To ensure best performance for ReadXml, on a large file, call the
DataTable.BeginLoadData method for each table in the dataset, and then call
ReadXml. Finally, call DataTable.EndLoadData for each table in the dataset,
as shown in the following example.
[Visual Basic]
For Each t As DataTable In ds.Tables
t.BeginLoadData()
Next
ds.ReadXml("file.xml")
For Each t As DataTable in ds.Tables
t.EndLoadData()
Next

Unit 6: Performing XML Operations on Disconnected Data

191

[C#]
foreach (DataTable t in ds.Tables)
{
t.BeginLoadData();
}
ds.ReadXml("file.xml");
foreach (DataTable t in ds.Tables)
{
t.EndLoadData();
}

If the XSD schema for your dataset includes a targetNamespace, data may not
be read, and you may encounter exceptions when calling ReadXml to load the
dataset with XML that contains elements with no qualifying namespace. To
read unqualified elements in this case, set elementFormDefault equal to
unqualified in your XSD schema. For example:
<xsd:schema
id="MyDataSet"
elementFormDefault="unqualified"
targetNamespace="http://www.tempuri.org/MyDataSet.xsd"
xmlns="http://www.tempuri.org/MyDataSet.xsd"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
</xsd:schema>

Merging Data from XML


If the dataset already contains data, the new data from the XML is added to the
data already present in the dataset. ReadXml does not merge from the XML
into the dataset any row information with matching primary keys. To overwrite
existing row information with new information from XML, use ReadXml to
create a new dataset, and then call the dataset Merge method to merge the new
dataset into the existing dataset. Note that loading a DiffGram using ReadXML
with an XmlReadMode of DiffGram will merge rows that have the same
unique identifier.

192

Unit 6: Performing XML Operations on Disconnected Data

How to Save Dataset Schema Information as an XML


Schema
This document is extracted from an article that you can find at the following
location:
In the Microsoft Visual Studio 2005 documentation at

Writing DataSet Schema Information as XML Schema (XSD)


mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.VisualSt
udio.v80.en/WD_ADONET/html/4e530831-695e-49ff-8f0be5b0c526b8eb.htm

How to: Save Dataset Schema Information as an XML Schema


You can write the schema of a dataset (its tables, columns, relations, and
constraints) as XML Schema definition language (XSD) schema so that you can
transport it, with or without related data, in an XML document. XML Schema,
which can be written to a file, a stream, an XmlWriter, or a string, is useful for
generating a typed dataset.
To write the schema of a dataset as XML Schema to a file, a stream, or an
XmlWriter, use the WriteXmlSchema method of the dataset.
WriteXmlSchema takes one parameter that specifies the destination of the
resulting XML Schema.
The following code example demonstrates how to write the XML Schema of a
dataset to a file by passing a string containing a file name.
[Visual Basic]
custDS.WriteXmlSchema("Customers.xsd")

[C#]
custDS.WriteXmlSchema("Customers.xsd");

The following code example demonstrates how to write the XML Schema of a
dataset to a file by passing a System.IO.StreamWriter object.
[Visual Basic]
Dim xmlStream As New System.IO.StreamWriter("Customers.xsd")
custDS.WriteXmlSchema(xmlStream)
xmlStream.Close()

[C#]
System.IO.StreamWriter xmlStream = new System.IO.StreamWriter("Customers.xsd");
custDS.WriteXmlSchema(xmlStream);
xmlStream.Close();

Unit 6: Performing XML Operations on Disconnected Data

To obtain the schema of a dataset and write it as an XML Schema string, use
the GetXmlSchema method as shown in the following example.
[Visual Basic]
Dim xsdDS As String = custDS.GetXmlSchema()

[C#]
string xsdDS = custDS.GetXmlSchema();

193

194

Unit 6: Performing XML Operations on Disconnected Data

How to Load Dataset Schema Information from an XML


Schema
This document is extracted from an article that you can find at the following
location:
In the Microsoft Visual Studio 2005 documentation at

Loading DataSet Schema Information from XML


mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.VisualSt
udio.v80.en/WD_ADONET/html/43dfb23b-5cef-46f2-8d8778f0fba1eb8c.htm

How to: Load Dataset Schema Information from an XML Schema


The schema of a dataset (its tables, columns, relations, and constraints) can be
defined programmatically, created by the Fill or FillSchema methods of a data
adapter, or loaded from an XML document. To load dataset schema information
from an XML document, you can use either the ReadXmlSchema or the
InferXmlSchema method of the dataset, as follows:

ReadXmlSchema allows you to load or infer dataset schema information


from the document containing XML Schema definition language (XSD)
schema, or an XML document with inline XML Schema.

InferXmlSchema allows you to infer the schema from the XML document,
while ignoring certain XML namespaces that you specify.

ReadXmlSchema
To load the schema of a dataset from an XML document, without loading any
data, you can use the ReadXmlSchema method of the dataset.
ReadXmlSchema creates dataset schema defined using XML Schema
definition language (XSD) schema.
The ReadXmlSchema method takes a single argument of a file name, a stream,
or an XmlReader containing the XML document to be loaded. The XML
document can contain only schema or can contain schema inline with XML
elements containing data.
If the XML document passed to ReadXmlSchema contains no inline schema
information, ReadXmlSchema will infer the schema from the elements in the
XML document. If the dataset already contains a schema, the current schema
will be extended by adding new columns to existing tables and adding new
tables if they do not already exist. If a column being added already exists in the
dataset but has an incompatible type with the column found in the XML, an
exception will be thrown.
Whereas ReadXmlSchema loads or infers only the schema of a dataset, the
ReadXml method of the DataSet will load or infer both the schema and the
data contained in the XML document.

Unit 6: Performing XML Operations on Disconnected Data

195

The following code example shows how to load a dataset schema from an XML
schema file.
[Visual Basic]
Dim myDS As New DataSet()
myDS.ReadXmlSchema("schema.xsd")

[C#]
DataSet myDS = new DataSet();
myDS.ReadXmlSchema("schema.xsd");

The following code example shows how to load a dataset schema from a
stream, by using a System.IO.StreamReader object.
[Visual Basic]
Dim xmlStream As New System.IO.StreamReader("schema.xsd")
Dim myDS As New DataSet()
myDS.ReadXmlSchema(xmlStream)
xmlStream.Close()

[C#]
System.IO.StreamReader xmlStream = new System.IO.StreamReader("schema.xsd");
DataSet myDS = new DataSet();
myDS.ReadXmlSchema(xmlStream);
xmlStream.Close();

InferXmlSchema
You can also instruct the dataset to infer its schema from an XML document by
using the InferXmlSchema method of the dataset. InferXmlSchema functions
the same as do both ReadXml with an XmlReadMode of InferSchema (loads
data as well as infers schema) and ReadXmlSchema if the document being
read contains no inline schema. However, InferXmlSchema provides the
additional capability of allowing you to specify particular XML namespaces to
be ignored when the schema is inferred. InferXmlSchema takes two required
arguments: the location of the XML document, specified by a file name, a
stream, or an XmlReader; and a string array of XML namespaces to be ignored
by the operation.

196

Unit 6: Performing XML Operations on Disconnected Data

For example, consider the following XML:


<NewDataSet xmlns:od="urn:schemas-microsoft-com:officedata">
<Categories>
<CategoryID od:adotype="3">1</CategoryID>
<CategoryName od:maxLength="15" od:adotype="130">Beverages</CategoryName>
<Description od:adotype="203">Soft drinks and teas</Description>
</Categories>
<Products>
<ProductID od:adotype="20">1</ProductID>
<ReorderLevel od:adotype="3">10</ReorderLevel>
<Discontinued od:adotype="11">0</Discontinued>
</Products>
</NewDataSet>

Because of the attributes specified for the elements in the preceding XML
document, the ReadXmlSchema method as well as the ReadXml method with
an XmlReadMode of InferSchema would both create tables for every element
in the document: Categories, CategoryID, CategoryName, Description,
Products, ProductID, ReorderLevel, and Discontinued. However, a more
appropriate structure would be to create only the Categories and Products
tables and then to create CategoryID, CategoryName, and Description
columns in the Categories table and ProductID, ReorderLevel, and
Discontinued columns in the Products table. To ensure that the inferred
schema ignores the attributes specified in the XML elements, use the
InferXmlSchema method and specify the XML namespace for officedata to be
ignored, as shown in the following example:
[Visual Basic]
Dim myDS As New DataSet()
myDS.InferXmlSchema("input_od.xml", _
New String() {"urn:schemas-microsoft-com:officedata"})

[C#]
DataSet myDS = new DataSet();
myDS.InferXmlSchema("input_od.xml",
new string[] {"urn:schemas-microsoft-com:officedata"});

Unit 7: Reading and Writing XML Data


How to Create an XmlWriter
This document is adapted from the articles that you can find at the following
locations:
In the Microsoft Visual Studio 2005 documentation at

XmlWriter class
mshelp://MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.NETDEVFX.v20.en/cpr
ef/html/T_System_Xml_XmlWriter.htm

New Features in the XmlWriter class


mshelp://MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.VisualStudio.v80.en/W
D_XML/html/ad199ad5-4079-48d6-b477-74e0adf6960c.htm

Using the XmlWriter


mshelp://MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.VisualStudio.v80.en/W
D_XML/html/1f8904f1-1900-44eb-9956-8fcfcbb0189c.htm

Creating XML Writers


mshelp://MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.VisualStudio.v80.en/W
D_XML/html/365a45ab-1a3a-4c89-9644-1e0cf2b4b5ce.htm

How To: Specify the Output Format on the XmlWriter


mshelp://MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.VisualStudio.v80.en/W
D_XML/html/0834182a-3ae2-4e68-9e93-07e4a9bcc456.htm

198

Unit 7: Reading and Writing XML Data

How to: Create an XmlWriter


System.Xml.XmlWriter provides a forward-only, read-only, non-cached way
of generating XML streams, which helps you to build XML documents that
conform to the W3C Extensible Markup Language (XML) 1.0 (Second Edition)
recommendation and the Namespaces in XML recommendation.
The following list shows that the XmlWriter has methods and properties
defined to:
Specify whether to support namespaces.
Write well-formed XML.
Encode binary bytes as base64 and as binhex and write out the resulting
text.
Manage the output, including methods to determine the progress of the
output, with the WriteState property.
Write multiple documents to one output stream.
Flush or close the output.
Report the current namespace prefix xml:lang or xml:space scope.
Write valid names, qualified names, and name tokens.
XmlWriter instances are created using the static (Shared in Microsoft Visual
Basic) XmlWriter.Create method. You can use an XmlWriterSettings
object to specify the set of features you want to enable on the new XmlWriter
object.
Note Although the Microsoft .NET Framework includes concrete
implementations of the XmlWriter class, such as the XmlTextWriter class, in
the .NET Framework version 2.0 release, the recommended practice is to create
XmlWriter instances by using the XmlWriter.Create method.
Features are enabled or disabled using the properties on the XmlWriterSettings
class. You specify which writer features to support by passing an
XmlWriterSettings object to the XmlWriter.Create method. By using the
XmlWriter.Create method and the XmlWriterSettings class, you get the
following benefits:
You are able to specify which features you want supported on the
created XmlWriter object.
The XmlWriterSettings object can be re-used to create multiple writer
objects. The XmlWriterSettings object is copied and marked read-only
for each created writer. Changes to the settings on an
XmlWriterSettings instance do not affect existing writers with the same
settings. Thus, you can use the same settings to create multiple writers
with the same functionality. Alternatively, you can modify the settings
on an XmlWriterSettings instance and create a new writer with a
different set of features.
You can add features to an existing writer. The XmlWriter.Create
method can accept another XmlWriter object. The underlying
XmlWriter object does not have to be a writer created by the
XmlWriter.Create method.

Unit 7: Reading and Writing XML Data

199

Take full advantage of all the new features added to the XmlWriter
class in this release. There are certain features, such as better
conformance checking and compliance to the XML 1.0
recommendation, that are available only on XmlWriter objects created
by the XmlWriter.Create method.
If an XmlWriterSettings object is not passed to the XmlWriter.Create
method, the default writer settings are used. The following table lists the default
settings on the XmlWriterSettings class.
XmlWriterSettings property

Default value

CheckCharacters

true

CloseOutput

false

ConformanceLevel

ConformanceLevel.Document

Encoding

Encoding.UTF8

Indent

false

IndentChars

Two spaces

NewLineChars

Carriage return, linefeed

NewLineHandling

NewHandling.Replace

NewLineOnAttributes

false

OmitXmlDeclaration

false

The following example creates an XmlWriter object that outputs to an XML


file.
[Visual Basic]
Dim writer as XmlWriter = XmlWriter.Create("books.xml", settings)
[C#]
XmlWriter writer = XmlWriter.Create("books.xml", settings);

The following example uses an XmlWriterSettings object to specify four


spaces for indentation and to specify that the attribute should be written on
separate lines:
[Visual Basic]
Dim settings As New XmlWriterSettings()
settings.Indent = True
settings.IndentChars = "
"
settings.NewLineOnAttributes = True
Dim writer As XmlWriter = XmlWriter.Create("books.xml", settings)
[C#]
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;
settings.IndentChars = "
";
settings.NewLineOnAttributes = true;
XmlWriter writer = XmlWriter.Create("books.xml", settings);

200

Unit 7: Reading and Writing XML Data

How to Write an XML Document


This document is adapted from an article that you can find at the following
location:
In the Microsoft Visual Studio 2005 documentation at

XmlWriter
mshelp://MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.NETDEVFX.v20.en/cpr
ef/html/T_System_Xml_XmlWriter.htm

The XmlWriter Class


The XmlWriter represents a writer that provides a fast, non-cached, forwardonly means of generating streams or files containing Extensible Markup
Language (XML) data that conforms to the World Wide Web Consortium
(W3C) XML 1.0 and the Namespaces in XML recommendations.

Writing Elements
There are two ways to write elements to an XML document.
The first approach is to call the WriteElementString method. In its simplest
form, this method is called along with two parameters, the name of the element
and the value of the element.
The following example shows how to use this overloaded version of
WriteElementString.
[Visual Basic]
' Write the price.
writer.WriteElementString("price", "19.95")
[C#]
// Write the price.
writer.WriteElementString("price", "19.95");

The second approach is to call the WriteStartElement, write the value


separately by using the WriteString method, and then call the
WriteEndElement method.
[Visual Basic]
' Write the title.
writer.WriteStartElement("title")
writer.WriteString("My Book Title")
writer.WriteEndElement()
[C#]
// Write the title.
writer.WriteStartElement("title");
writer.WriteString("My Book Title");
writer.WriteEndElement();

Unit 7: Reading and Writing XML Data

201

To write strongly typed data, call the WriteValue() method. This method
converts simple Microsoft .NET Framework data types into an appropriate
string representation. Alternatively, you can use the XmlConvert class to
convert data types to a string. For example, the following Microsoft Visual C#
code converts the data from Double to String and writes the element
<price>19.95</price>.
[Visual Basic]
Dim price As Double = 19.95
writer.WriteElementString("price", XmlConvert.ToString(price))
[C#]
double price = 19.95;
writer.WriteElementString("price", XmlConvert.ToString(price));

When writing an empty element, an additional space is added between the tag
name and the closing tag, for example, <item />. This additional space provides
compatibility with earlier browsers.
When a String is used as a method parameter, a null reference (Nothing in
Microsoft Visual Basic) and String.Empty are equivalent. String.Empty
follows the W3C rules.
XmlWriter does not check for the following:
Duplicate attributes.
Characters in the DOCTYPE public identifier or system identifier.

Writing Attributes
You can use the WriteAttributeString method to write attributes. In its
simplest form, this method is called along with two parameters, the name of the
attribute and the value of the attribute.
The following example shows how to use this overloaded version of
WriteAttributeString.
[Visual Basic]
writer.WriteAttributeString("name", "purchaseOrder")
[C#]
writer.WriteAttributeString("name", "purchaseOrder");

202

Unit 7: Reading and Writing XML Data

Another way to write attributes is to call WriteStartAttribute, write the value


separately by calling the WriteString method, and then call the
WriteEndAttribute method.
[Visual Basic]
Dim prefix As String = writer.LookupPrefix("urn:samples")
writer.WriteStartAttribute(prefix, "ISBN", "urn:samples")
writer.WriteString("1-861003-78")
writer.WriteEndAttribute()
[C#]
string prefix = writer.LookupPrefix("urn:samples");
writer.WriteStartAttribute(prefix, "ISBN", "urn:samples");
writer.WriteString("1-861003-78");
writer.WriteEndAttribute();

Defining Namespaces
XmlWriter maintains a namespace stack corresponding to all the namespaces
defined in the current element stack. Using XmlWriter, you can declare
namespaces manually.
[Visual Basic]
w.WriteStartElement("root")
w.WriteAttributeString("xmlns", "x", Nothing, "urn:1")
w.WriteStartElement("item", "urn:1")
w.WriteEndElement()
w.WriteStartElement("item", "urn:1")
w.WriteEndElement()
w.WriteEndElement()
[C#]
w.WriteStartElement("root");
w.WriteAttributeString("xmlns", "x", null, "urn:1");
w.WriteStartElement("item", "urn:1");
w.WriteEndElement();
w.WriteStartElement("item", "urn:1");
w.WriteEndElement();
w.WriteEndElement();

The preceding examples produce the following output: XmlWriter promotes


the namespace declaration to the root element to avoid having it duplicated on
the two child elements. The child elements pick up the prefix from the
namespace declaration.
<root xmlns:x="urn:1">
<x:item/>
<x:item/>
</x:root>

Unit 7: Reading and Writing XML Data

203

By using the write methods that take a prefix as an argument, you can also
specify which prefix to use. In the following example, two different prefixes are
mapped to the same namespace URI, thereby producing the XML text
<x:root xmlns:x="urn:1">
<y:item xmlns:y="urn:1"/></x:root>.
[Visual Basic]
Dim w As XmlWriter = XmlWriter.Create(Console.Out)
w.WriteStartElement("x", "root", "urn:1")
w.WriteStartElement("y", "item", "urn:1")
w.WriteEndElement()
w.WriteEndElement()
w.Close()
[C#]
XmlWriter w = XmlWriter.Create(Console.Out);
w.WriteStartElement("x", "root", "urn:1");
w.WriteStartElement("y", "item", "urn:1");
w.WriteEndElement();
w.WriteEndElement();
w.Close();

If there are multiple namespace declarations mapping different prefixes to the


same namespace URI, XmlWriter walks the stack of namespace declarations
backward and picks the closest one.
[Visual Basic]
Dim sw As New StringWriter()
Dim w As XmlWriter = XmlWriter.Create(sw)
w.WriteStartElement("x", "root", "urn:1")
w.WriteStartElement("y", "item", "urn:1")
w.WriteAttributeString("attr", "urn:1", "123")
w.WriteEndElement()
w.WriteEndElement()
w.Close()
[C#]
StringWriter sw = new StringWriter();
XmlWriter w = XmlWriter.Create(sw);
w.WriteStartElement("x", "root", "urn:1");
w.WriteStartElement("y", "item", "urn:1");
w.WriteAttributeString("attr", "urn:1", "123");
w.WriteEndElement();
w.WriteEndElement();
w.Close();

204

Unit 7: Reading and Writing XML Data

In the preceding examples, because the WriteAttributeString call does not


specify a prefix, the writer uses the last prefix pushed onto the namespace stack,
and it produces the following XML:
<x:root xmlns:x="urn:1">
<y:item y:attr="123" xmlns:y="urn:1" />
</x:root>

If namespace conflicts occur, XmlWriter resolves them by generating alternate


prefixes. For example, if an attribute and element have the same prefix but
different namespaces, XmlWriter generates an alternate prefix for the attribute.
The generated prefixes are named n{i} where i is a number beginning at 1. The
number is reset to 1 for each element.
Attributes which are associated with a namespace URI must have a prefix
(default namespaces do not apply to attributes). If an attribute references a
namespace URI, but it does not specify a prefix, the writer generates a prefix for
the attribute.

Unit 7: Reading and Writing XML Data

205

How to Write a Message to a Microsoft Message Queue


This document is adapted from an article that you can find at the following
location:
In the Microsoft Visual Studio 2005 documentation at

MessageQueue class
mshelp://MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.NETDEVFX.v20.en/cpr
ef/html/T_System_Messaging_MessageQueue.htm

Using Message Queuing


Message Queuing technology allows applications running at different times to
communicate across heterogeneous networks and systems that might be
temporarily offline. Applications send, receive, or peek (read without removing)
messages from queues. Message Queuing is an optional component of
Microsoft Windows 2000 and Microsoft Windows NT.
The MessageQueue class is a wrapper around Message Queuing. There are
multiple versions of Message Queuing, and using the MessageQueue class can
result in slightly different behavior, depending on the operating system you are
using. For information about specific features of each version of Message
Queuing, see the topic Whats New in Message Queuing in the Platform
software development kit (SDK) in Microsoft MSDN.
The MessageQueue class provides a reference to a Message Queuing queue.
You can specify a path in the MessageQueue constructor to connect to an
existing resource, or you can create a new queue on the server. Before you can
call Send, Peek, or Receive, you must associate the new instance of the
MessageQueue class with an existing queue. At that point, you can manipulate
the queue properties such as Category and Label.
When you create a new instance of the MessageQueue class, you are not
creating a new Message Queuing queue. Instead, you can use the Create,
Delete, and Purge methods to manage queues on the server.
Note Unlike Purge, Create and Delete are static (Shared in Microsoft Visual
Basic) members, so you can call them without creating a new instance of the
MessageQueue class.
You can set the MessageQueue object's Path property with one of three names:
the friendly name, the FormatName, or the Label. The friendly name, which is
defined by the queue's MachineName and QueueName properties, is
MachineName\ QueueName for a public queue and MachineName\ Private$\
QueueName for a private queue. The FormatName property allows offline
access to message queues. Finally, you can use the queues Label property to
set the queues Path.
To send a message to the queue, connect to a queue on the local computer and
send the queue to it.

206

Unit 7: Reading and Writing XML Data

Sending a Message to a Queue


The following example shows how to send a message to a queue.
[Visual Basic]
Imports System
Imports System.Messaging
Namespace MyProject
Public Class Order
Public orderId As Integer
Public orderTime As DateTime
End Class 'Order
Public Class MyTestProgram
Public Shared Sub Main()
' Create a new order and set values.
Dim sentOrder As New Order()
sentOrder.orderId = 3
sentOrder.orderTime = DateTime.Now
' Connect to a queue on the local computer.
Dim myQueue As New MessageQueue(".\myQueue")
' Send the Order to the queue.
myQueue.Send(sentOrder)
End Sub
End Class
End Namespace

Unit 7: Reading and Writing XML Data


[C#]
using System;
using System.Messaging;
namespace MyProject
{
public class Order
{
public int orderId;
public DateTime orderTime;
}
public class MyTestProgram
{
public static void Main()
{
// Create a new order and set values.
Order sentOrder = new Order();
sentOrder.orderId = 3;
sentOrder.orderTime = DateTime.Now;
// Connect to a queue on the local computer.
MessageQueue myQueue = new MessageQueue(".\\myQueue");
// Send the Order to the queue.
myQueue.Send(sentOrder);
}
}
}

207

208

Unit 7: Reading and Writing XML Data

Sending Messages as Part of a Transaction


To send a message as part of a transaction, you can use the
MessageQueueTransaction class to create a transaction and pass it to an
overload of the MessageQueue.Send method that takes a transaction
parameter. Messages sent as part of a transaction must be sent to transactional
queues.
Messages sent to transactional queues are removed if the transaction is rolled
back.
If you instantiate a MessageQueueTransaction and pass it to an applicable
overload of the Send method to send a message to a non-transactional queue,
the method throws an exception indicating Wrong Transaction Usage.
The following example shows how to send a string to a transactional queue.
Note The following example shows how to use one of the overloaded versions
of Send. For other examples that might be available, see the individual overload
topics.
[Visual Basic]
Imports System
Imports System.Messaging
Namespace MyProject
Public Class MyTestProgram
Public Shared Sub Main()
' Connect to a queue on the local computer.
Dim myQueue As New MessageQueue(".\myTransactionalQueue")
' Send a message to the queue.
If myQueue.Transactional = True Then
Dim trans As New MessageQueueTransaction()
trans.Begin()
myQueue.Send("My Message Data.", trans)
trans.Commit()
Else
myQueue.Send("My Message Data.")
End If
End Sub
End Class
End Namespace

Unit 7: Reading and Writing XML Data


[C#]
using System;
using System.Messaging;
namespace MyProject
{
public class MyTestProgram
{
public static void Main()
{
// Connect to a queue on the local computer.
MessageQueue myQueue = new MessageQueue(".\\myTransactionalQueue");
// Send a message to the queue.
if (myQueue.Transactional == true)
{
MessageQueueTransaction trans = new MessageQueueTransaction();
trans.Begin();
myQueue.Send("My Message Data.", trans);
trans.Commit();
}
else
{
myQueue.Send("My Message Data.");
}
}
}
}

209

210

Unit 7: Reading and Writing XML Data

How to Read a Message from a Microsoft Message Queue


This document is adapted from the article that you can find at the following
location:
In the Microsoft Visual Studio 2005 documentation at

MessageQueue class
mshelp://MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.NETDEVFX.v20.en/cpr
ef/html/T_System_Messaging_MessageQueue.htm

Message Queuing
Message Queuing technology allows applications running at different times to
communicate across heterogeneous networks and systems that might be
temporarily offline. Applications send, receive, or peek (read without removing)
messages from queues. Message Queuing is an optional component of
Microsoft Windows 2000 and Microsoft Windows NT, and it must be
installed separately.
The MessageQueue class is a wrapper around Message Queuing. There are
multiple versions of Message Queuing, and using the MessageQueue class can
result in slightly different behavior, depending on the operating system you are
using.
MessageQueue supports two types of message retrieval: synchronous and
asynchronous. The synchronous methods, Peek and Receive, cause the process
thread to wait a specified time interval for a new message to arrive in the queue.
The asynchronous methods, BeginPeek and BeginReceive, allow the main
application tasks to continue in a separate thread until a message arrives in the
queue. These methods work by using callback objects and state objects to
communicate information between threads.
To read a message from a message queue synchronously, you must connect to
the Message Queue on the local computer, and then you must call the receive
method.

Unit 7: Reading and Writing XML Data

Receiving a Message from a Queue


The following example sends a message to a queue and receives a message
from a queue, using an application-specific class called Order.
[Visual Basic]
Imports System
Imports System.Messaging
Namespace MyProject
Public Class Order
Public orderId As Integer
Public orderTime As DateTime
End Class
Public Class MyTestProgram
Public Shared Sub Main()
' Connect to a queue on the local computer.
Dim myQueue As New MessageQueue(".\myQueue")
' Set the formatter to indicate the body contains an Order.
myQueue.Formatter = New XmlMessageFormatter(New Type() _
{GetType(MyProject.Order)})
Try
' Receive and format the message.
Dim myMessage As Message = myQueue.Receive()
Dim myOrder As Order = CType(myMessage.Body, Order)
' Display message information.
Console.WriteLine("Order ID: " & myOrder.orderId.ToString())
Console.WriteLine("Sent: "
& myOrder.orderTime.ToString())
Catch m As MessageQueueException
' Handle Message Queuing exceptions.
Catch e As InvalidOperationException
' Handle invalid serialization format.
Console.WriteLine(e.Message)
' Catch other exceptions as necessary.
End Try
End Sub
End Class
End Namespace

211

212

Unit 7: Reading and Writing XML Data

[Visual C#]
using System;
using System.Messaging;
namespace MyProject
{
public class Order
{
public int orderId;
public DateTime orderTime;
}
public class MyTestProgram
{
public static void Main()
{
// Connect to the a queue on the local computer.
MessageQueue myQueue = new MessageQueue(".\\myQueue");
// Set the formatter to indicate body contains an Order.
myQueue.Formatter = new XmlMessageFormatter(new Type[]
{typeof(MyProject.Order)});
try
{
// Receive and format the message.
Message myMessage = myQueue.Receive();
Order myOrder = (Order)myMessage.Body;
// Display message information.
Console.WriteLine("Order ID: " + myOrder.orderId.ToString());
Console.WriteLine("Sent: "
+ myOrder.orderTime.ToString());
}
catch (MessageQueueException)
{
// Handle Message Queuing exceptions.
}
// Handle invalid serialization format.
catch (InvalidOperationException e)
{
Console.WriteLine(e.Message);
}
// Catch other exceptions as necessary.
}
}
}

Unit 7: Reading and Writing XML Data

213

Receiving Messages as Part of a Transaction


To receive a message as part of a transaction, you can use the
MessageQueueTransaction class to create a transaction and pass it to an
overload of the MessageQueue.Receive method that takes a transaction
parameter. Messages received from transactional queues must be received using
a specified transaction.
Messages received from transactional queues are returned to the queue if the
transaction is rolled back.
If you instantiate a MessageQueueTransaction and pass it to an applicable
overload of the Receive method to receive a message from a non-transactional
queue, the method throws an exception indicating Wrong Transaction Usage.
The following example receives a message from a transactional queue.

214

Unit 7: Reading and Writing XML Data

[Visual Basic]
Imports System
Imports System.Messaging
Namespace MyProject
Public Class MyTestProgram
Public Shared Sub Main()
' Connect to a transactional queue on the local computer.
Dim myQueue As New MessageQueue(".\myTransactionalQueue")
' Set the formatter.
myQueue.Formatter = New XmlMessageFormatter(New Type() _
{GetType(String)})
' Create a transaction.
Dim myTransaction As New MessageQueueTransaction()
Try
' Begin the transaction.
myTransaction.Begin()
' Receive the message.
Dim myMessage As Message = myQueue.Receive(myTransaction)
Dim myOrder As String = CStr(myMessage.Body)
' Display message information.
Console.WriteLine(myOrder)
' Commit the transaction.
myTransaction.Commit()
Catch e As MessageQueueException
' Handle nontransactional queues.
If e.MessageQueueErrorCode = _
MessageQueueErrorCode.TransactionUsage Then
Console.WriteLine("Queue is not transactional.")
End If
' Roll back the transaction.
myTransaction.Abort()
' Catch other exceptions as necessary, such as
' InvalidOperationException, thrown when the formatter
' cannot deserialize the message.
End Try
End Sub
End Class
End Namespace

Unit 7: Reading and Writing XML Data

215

[Visual C#]
using System;
using System.Messaging;
namespace MyProject
{
public class MyTestProgram
{
public static void Main()
{
// Connect to a transactional queue on the local computer.
MessageQueue myQueue = new MessageQueue(".\\myTransactionalQueue");
// Set the formatter.
myQueue.Formatter = new XmlMessageFormatter(new Type[]
{typeof(string)});
// Create a transaction.
MessageQueueTransaction myTransaction = new MessageQueueTransaction();
try
{
// Begin the transaction.
myTransaction.Begin();
// Receive the message.
Message myMessage = myQueue.Receive(myTransaction);
string myOrder = (string)myMessage.Body;
// Display message information.
Console.WriteLine(myOrder);
// Commit the transaction.
myTransaction.Commit();
}
catch (MessageQueueException e)
{
// Handle nontransactional queues.
if (e.MessageQueueErrorCode ==
MessageQueueErrorCode.TransactionUsage)
{
Console.WriteLine("Queue is not transactional.");
}
// Roll back the transaction.
myTransaction.Abort();
}
// Catch other exceptions as necessary, such as
// InvalidOperationException, thrown when the formatter
// cannot deserialize the message.
}
}
}

216

Unit 7: Reading and Writing XML Data

How to Create an XmlReader


This document is adapted from the articles that you can find at the following
locations:
In the Microsoft Visual Studio 2005 documentation at

Using the XmlReader Class


mshelp://MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.VisualStudio.v80.en/W
D_XML/html/0eab0afd-0e43-4f68-a152-200d2f74a3fa.htm

Creating Xml Readers


mshelp://MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.VisualStudio.v80.en/W
D_XML/html/441e5aac-dfa9-41ed-9336-cd541b11c2d1.htm

Validation Using the XmlSchemaSet


mshelp://MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.VisualStudio.v80.en/W
D_XML/html/42141aed-a8c0-4014-92b5-eb37eb7d302c.htm

Validating Xml Data with Xml Reader


mshelp://MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.VisualStudio.v80.en/W
D_XML/html/2958dbbd-60ca-4447-9b16-72f41d223486.htm

The XmlReader Class


The System.Xml.XmlReader class reads Extensible Markup Language (XML)
data from a stream or file. It provides non-cached, forward-only, read-only
access to XML data. This section describes how to create an XmlReader
instance with a specified set of features, such as data validation, dataconformance checking, and reading typed data.

Creating an XmlReader Object


XmlReader instances are created by using the XmlReader.Create method.
The XmlReaderSettings class is used to specify the set of features you want to
enable on the XmlReader object.
Note Although the Microsoft .NET Framework includes concrete
implementations of the XmlReader class, such as the XmlTextReader,
XmlNodeReader, and the XmlValidatingReader classes, in the .NET
Framework version 2.0 release, the recommended practice is to create
XmlReader instances by using the XmlReader.Create method.
Features are enabled or disabled by using the properties on the
XmlReaderSettings class. The XmlReaderSettings object is then passed to the
XmlReader.Create method.

Unit 7: Reading and Writing XML Data

217

By using the XmlReader.Create method and the XmlReaderSettings class,


you get the following benefits:

You are able to specify which features you want supported on the created
XmlReader object.

You can reuse the XmlReaderSettings to create multiple reader objects.


You can use the same settings to create multiple readers with the same
functionality. Alternatively, you can modify the XmlReaderSettings object
and create a new reader with a different set of features.

You can add features to an existing reader. The XmlReader.Create method


can accept another XmlReader object. The underlying XmlReader object
can be a user-defined reader or an XmlTextReader object.

You can take full advantage of all the new features added to the XmlReader
class in the .NET Framework 2.0 release. There are certain features, such as
better conformance checking and compliance to the XML 1.0
recommendation, that are available only on XmlReader objects created by
the XmlReader.Create method.

The following table lists the default property settings on the


XmlReaderSettings class.
XmlReaderSettings property

Default value

CheckCharacters

true

ConformanceLevel

ConformanceLevel.Document

IgnoreComments

false

IgnoreProcessingInstructions

false

IgnoreWhitespace

false

LineNumberOffset

0.

LinePositionOffset

NameTable

null

ProhibitDtd

true

Schemas

An empty System.Xml.Schema.XmlSchemaSet
object

ValidationFlags

Allows xml:* attributes and processes identity


constraints but ignores schema location, inline
schemas, and validation warnings

ValidationType

ValidationType.None

XmlResolver

A new System.Xml.XmlUrlResolver object

218

Unit 7: Reading and Writing XML Data

XmlReader Scenarios
The following table describes some common scenarios and which settings on
the XmlReaderSettings class to apply.
Scenario

XmlReaderSettings

Requires the data to be a wellformed XML document

ConformanceLevel =
ConformanceLevel.Document

Requires the data to be a wellformed XML-parsed entity

ConformanceLevel =
ConformanceLevel.Fragment

Needs data to be validated


against a document type
definition (DTD)

ProhibitDtd = false

Needs data to be validated


against an XML Schema

ValidationType =
ValidationType.Schema

ValidationType = ValidationType.DTD

Schemas = XmlSchemaSet to use for


validation
Needs data to be validated
against an inline XML Schema

ValidationType =
ValidationType.Schema
ValidationFlags &=
~XmlSchemaValidationFlags.IgnoreInl
ineSchema

Needs type support

ValidationType =
ValidationType.Schema
Schemas = XmlSchemaSet to use

The following example shows how to create an XmlReader object. The


XmlReader object reads an XML document named books.xml and ignores
white space and comments in the XML document.
[Visual Basic]
Dim settings As New XmlReaderSettings()
settings.ConformanceLevel = ConformanceLevel.Fragment
settings.IgnoreWhitespace = True
settings.IgnoreComments = True
Dim reader As XmlReader = XmlReader.Create("books.xml", settings)
[C#]
XmlReaderSettings settings = new XmlReaderSettings();
settings.ConformanceLevel = ConformanceLevel.Fragment;
settings.IgnoreWhitespace = true;
settings.IgnoreComments = true;
XmlReader reader = XmlReader.Create("books.xml", settings);

Unit 7: Reading and Writing XML Data

219

Validating an XML Document


To define the structure of an XML document, as well as its element
relationships, data types, and content constraints, you use a DTD or schema.
Although an XML document is considered to be well formed if it meets all the
syntactical requirements defined by the World Wide Web Consortium (W3C)
XML 1.0 Recommendation, it is not considered to be valid unless it is well
formed and also conforms to the constraints defined by its DTD or schema.
Therefore, although all valid XML documents are well formed, not all wellformed XML documents are valid.
The System.Xml.XmlReader class can enforce validation using a DTD or
XSD schema. The XmlReaderSettings.ValidationType property determines
whether the XmlReader instance enforces validation.
You can use a System.Xml.Schema.XmlSchemaSet object to hold a cache of
XML Schema Definition language (XSD) schemas. XmlSchemaSet improves
performance by caching schemas in memory instead of accessing them from a
file or a URL. Each schema in the XmlSchemaSet is identified by the
namespace URI and schema location specified when the schema was added to
the XmlSchemaSet. The XmlReaderSettings.Schemas property assigns the
XmlSchemaSet object to use.
The following example validates an XML document by using schemas stored in
the XmlSchemaSet. The namespace in the XML file, urn:bookstore-schema,
identifies which schema in the XmlSchemaSet to use for validation.
[Visual Basic]
' Create the XmlSchemaSet object.
Dim sc as New XmlSchemaSet()
' Add the schema to the collection.
sc.Add("urn:bookstore-schema", "books.xsd")
' Set the validation settings.
Dim settings As New XmlReaderSettings()
settings.ValidationType = ValidationType.Schema
settings.Schemas = sc
AddHandler settings.ValidationEventHandler, AddressOf ValidationCallBack
' Create the XmlReader object and read the document.
Dim reader as XmlReader = XmlReader.Create("booksSchemaFail.xml", settings)
While reader.Read()
' ...
End While
' Display any validation errors.
Private Shared Sub ValidationCallBack(sender as Object, e as ValidationEventArgs)
Console.WriteLine("Validation Error: {0}", e.Message)
End Sub

220

Unit 7: Reading and Writing XML Data

[C#]
// Create the XmlSchemaSet object
XmlSchemaSet sc = new XmlSchemaSet();
// Add the schema to the collection.
sc.Add("urn:bookstore-schema", "books.xsd");
// Set the validation settings.
XmlReaderSettings settings = new XmlReaderSettings();
settings.ValidationType = ValidationType.Schema;
settings.Schemas = sc;
settings.ValidationEventHandler += ValidationCallBack;
// Create the XmlReader object and read the document.
XmlReader reader = XmlReader.Create("booksSchemaFail.xml", settings);
while (reader.Read())
{
// ...
}
// Display any validation errors.
private static void ValidationCallBack(object sender, ValidationEventArgs e)
{
Console.WriteLine("Validation Error: {0}", e.Message);
}

Unit 7: Reading and Writing XML Data

221

How to Read an XML Document


This document is adapted from the articles that you can find at the following
locations:
In the Microsoft Visual Studio 2005 documentation at

XmlReader class
mshelp://MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.NETDEVFX.v20.en/cpr
ef/html/T_System_Xml_XmlReader.htm

XmlReader.Read method
mshelp://MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.NETDEVFX.v20.en/cpr
ef/html/M_System_Xml_XmlReader_Read.htm

How to: Read an XML Document


The XmlReader class provides forward-only, read-only access to a stream of
Extensible Markup Language (XML) data. The current node refers to the node
on which the reader is positioned. The reader is advanced by using any of the
read methods and properties that reflect the value of the current node.
When you create an XmlReader object, you must call the Read method to read
the first node. The following example reads an XML file and displays each
node. The example uses a case/switch construct to separate out each of the
different element types.

222

Unit 7: Reading and Writing XML Data

[Visual Basic]
Imports System
Imports System.Xml
Public Class Sample
Public Shared Sub Main()
Dim reader As XmlReader = Nothing
Try
' Load the reader with the data file and ignore all white space nodes.
Dim settings As New XmlReaderSettings()
settings.IgnoreWhitespace = True
reader = XmlReader.Create("items.xml", settings)
' Parse the file and display each of the nodes.
While reader.Read()
Select Case reader.NodeType
Case XmlNodeType.Element
Console.Write("<{0}>", reader.Name)
Case XmlNodeType.Text
Console.Write(reader.Value)
Case XmlNodeType.CDATA
Console.Write("<![CDATA[{0}]]>", reader.Value)
Case XmlNodeType.ProcessingInstruction
Console.Write("<?{0} {1}?>", reader.Name, reader.Value)
Case XmlNodeType.Comment
Console.Write("<!--{0}-->", reader.Value)
Case XmlNodeType.XmlDeclaration
Console.Write("<?xml version='1.0'?>")
Case XmlNodeType.DocumentType
Console.Write("<!DOCTYPE {0} [{1}]", _
reader.Name, reader.Value)
Case XmlNodeType.EndElement
Console.Write("</{0}>", reader.Name)
End Select
End While
Finally
If reader IsNot Nothing Then
reader.Close()
End If
End Try
End Sub
End Class

Unit 7: Reading and Writing XML Data

223

[C#]
using System;
using System.Xml;
public class Sample
{
public static void Main()
{
XmlReader reader = null;
try
{
// Load the reader with the data file and ignore all white space nodes.
XmlReaderSettings settings = new XmlReaderSettings();
settings.IgnoreWhitespace = true;
reader = XmlReader.Create("items.xml", settings);
// Parse the file and display each of the nodes.
while (reader.Read())
{
switch (reader.NodeType)
{
case XmlNodeType.Element:
Console.Write("<{0}>", reader.Name);
break;
case XmlNodeType.Text:
Console.Write(reader.Value);
break;
case XmlNodeType.CDATA:
Console.Write("<![CDATA[{0}]]>", reader.Value);
break;
case XmlNodeType.ProcessingInstruction:
Console.Write("<?{0} {1}?>", reader.Name, reader.Value);
break;
case XmlNodeType.Comment:
Console.Write("<!--{0}-->", reader.Value);
break;
case XmlNodeType.XmlDeclaration:
Console.Write("<?xml version='1.0'?>");
break;
case XmlNodeType.DocumentType:
Console.Write("<!DOCTYPE {0} [{1}]",
reader.Name, reader.Value);
break;
case XmlNodeType.EndElement:
Console.Write("</{0}>", reader.Name);
break;
}
}
}
finally
{
if (reader != null)
reader.Close();
}
}
}

224

Unit 7: Reading and Writing XML Data

To read strongly typed data, use the XmlConvert class. For example, the
following Microsoft Visual C# code reads in data and converts it from a
String to a Double.
[Visual Basic]
Dim price As Double = XmlConvert.ToDouble(reader.Value)
[C#]
double price = XmlConvert.ToDouble(reader.Value);

XmlReader throws an XmlException on XML parse errors. After an exception


is thrown, the state of the reader is not predictable. For example, the reported
node type may be different than the actual node type of the current node.

Reading Attributes
The following example shows how to use the MoveToAttribute method to
display all attributes on the current node.
[Visual Basic]
Public Sub DisplayAttributes(ByVal reader As XmlReader)
If reader.HasAttributes Then
Console.WriteLine("Attributes of <" & reader.Name & ">")
Dim i As Integer
For i = 0 To reader.AttributeCount - 1
reader.MoveToAttribute(i)
Console.Write(" {0}={1}", reader.Name, reader.Value)
Next
reader.MoveToElement()
End If
End Sub

' Move the reader back to the element node.

Unit 7: Reading and Writing XML Data

225

[C#]
public void DisplayAttributes(XmlReader reader)
{
if (reader.HasAttributes)
{
Console.WriteLine("Attributes of <" + reader.Name + ">");
for (int i = 0; i < reader.AttributeCount; i++)
{
reader.MoveToAttribute(i);
Console.Write(" {0}={1}", reader.Name, reader.Value);
}
reader.MoveToElement();
// Move the reader back to the element node.
}
}

Reading Text Content


The MoveToContent method is useful when you want to write code that can
skip over random XML markup without breaking. The following example reads
the value of a <price> element:
[Visual Basic]
If reader.MoveToContent() = XmlNodeType.Element AndAlso reader.Name = "price" Then
_price = reader.ReadString()
End If
[C#]
if (reader.MoveToContent() == XmlNodeType.Element && reader.Name == "price")
{
_price = reader.ReadString();
}

226

Unit 7: Reading and Writing XML Data

How to Send an E-Mail Message


This document is adapted from the article that you can find at the following
location:
In the Microsoft Visual Studio 2005 documentation at

SmtpClient Class
mshelp://MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.NETDEVFX.v20.en/cpr
ef/html/T_System_Net_Mail_SmtpClient.htm

How to: Send an E-Mail Message


The System.Net.Mail.SmtpClient class is used to send an e-mail message to a
Simple Mail Transfer Protocol (SMTP) server for delivery. The classes shown
in the following table are used to construct e-mail messages that can be sent by
using SmtpClient.
Class

Description

Attachment

Represents file attachments. This class allows you to attach files,


streams, or text to an e-mail message.

MailAddress

Represents the e-mail addresses of the sender and recipients.

MailMessage

Represents an e-mail message.

To construct and send an e-mail message by using SmtpClient, you must


specify the following information:
The SMTP host server that you use to send e-mail messages
Credentials for authentication, if required by the SMTP server
The e-mail address of the sender
The e-mail address or addresses of the recipients
The message content
To include an attachment with an e-mail message, first create the attachment by
using the Attachment class and then add it to the message by using the
Attachments property on the MailMessage object. Depending on the e-mail
reader used by the recipients and the file type of the attachment, some recipients
might not be able to read the attachment. For clients that cannot display the
attachment in its original form, you can specify alternate views by using the
AlternateViews property on the MailMessage object.
You can use the application or machine configuration files to specify default
host, port, and credentials values for all SmtpClient objects.
To send the e-mail message and block while waiting for the e-mail message to
be transmitted to the SMTP server, use one of the synchronous Send methods
in the SmtpClient class.

Unit 7: Reading and Writing XML Data

227

To allow your programs main thread to continue executing while the e-mail is
transmitted, use one of the asynchronous SendAsync methods. The
SendCompleted event is raised when a SendAsync operation completes. To
receive this event, you must add a SendCompletedEventHandler delegate for
the SendCompleted event The SendCompletedEventHandler delegate must
reference a callback method that handles notification of SendCompleted
events. To cancel an asynchronous e-mail transmission, use the
SendAsyncCancel method.
Note If there is an e-mail transmission in progress and you call SendAsync or
Send again, you will receive an InvalidOperationException exception.

Sending an E-Mail Message Asynchronously


The following code example demonstrates sending an e-mail message
asynchronously.

228

Unit 7: Reading and Writing XML Data

[Visual
Imports
Imports
Imports
Imports
Imports
Imports

Basic]
System
System.Net
System.Net.Mail
System.Net.Mime
System.Threading
System.ComponentModel

Namespace Examples.SmptExamples.Async
Public Class SimpleAsynchronousExample
Private mailSent As Boolean = False
Public Sub SendCompletedCallback(ByVal sender As System.Object, _
ByVal e As AsyncCompletedEventArgs)
' Get the unique identifier for this asynchronous operation.
Dim token As String = CStr(e.UserState.ToString)
If e.Cancelled Then Console.WriteLine("[{0}] Send canceled.", token)
If e.Error IsNot Nothing Then
Console.WriteLine("[{0}] {1}", token, e.Error.ToString())
Else
Console.WriteLine("Message sent.")
End If
mailSent = True
End Sub
Public Sub Main(ByVal args() As String)
' Command line argument must the the SMTP host.
Dim client As New SmtpClient(args(0))
' Specify the e-mail sender.
' Create a mailing address that includes a UTF8 character
' in the display name.
Dim fromAddr As New MailAddress("jo@contoso.com", _
"Jo " & ChrW(&HD8) & " Clayton", _
System.Text.Encoding.UTF8)
' Set destinations for the e-mail message.
Dim toAddr As New MailAddress("ben@contoso.com")
' Specify the message content.
Dim message As New MailMessage(fromAddr, toAddr)
message.Body = "This is a test e-mail message."

Unit 7: Reading and Writing XML Data

229

(continued)
'Include some non-ASCII characters in body and subject.
Dim someArrows As New String( _
New Char() {ChrW(&H2190), ChrW(&H2191), ChrW(&H2192), ChrW(&H2193)})
message.Body &= Environment.NewLine & someArrows
message.BodyEncoding = System.Text.Encoding.UTF8
message.Subject = "test message 1" & someArrows
message.SubjectEncoding = System.Text.Encoding.UTF8
' Set the method that is called back when the send operation ends.
AddHandler client.SendCompleted, AddressOf SendCompletedCallback
' The userState can be any object that allows your callback
' method to identify this send operation.
' For this example, the userToken is a string constant.
Dim userState As String = "test message1"
client.SendAsync(message, userState)
Console.WriteLine("Sending message... press c to cancel mail. " & _
"Press any other key to exit.")
Dim answer As String = Console.ReadLine()
If answer.StartsWith("c") AndAlso mailSent = False Then
client.SendAsyncCancel()
End If
' Clean up.
message.Dispose()
Console.WriteLine("Goodbye.")
End Sub
End Class
End Namespace

230
[C#]
using
using
using
using
using
using

Unit 7: Reading and Writing XML Data

System;
System.Net;
System.Net.Mail;
System.Net.Mime;
System.Threading;
System.ComponentModel;

namespace Examples.SmptExamples.Async
{
public class SimpleAsynchronousExample
{
static private bool mailSent = false;
public static void SendCompletedCallback(object sender,
AsyncCompletedEventArgs e)
{
// Get the unique identifier for this asynchronous operation.
string token = (string) e.UserState;
if (e.Cancelled)
{
Console.WriteLine("[{0}] Send canceled.", token);
}
if (e.Error != null)
{
Console.WriteLine("[{0}] {1}", token, e.Error.ToString());
}
else
{
Console.WriteLine("Message sent.");
}
mailSent = true;
}
public static void Main(string[] args)
{
// Command line argument must the the SMTP host.
SmtpClient client = new SmtpClient(args[0]);
// Specify the e-mail sender.
// Create a mailing address that includes a UTF8 character
// in the display name.
MailAddress fromAddr = new MailAddress("jo@contoso.com",
"Jo " + (char)0xD8 + " Clayton",
System.Text.Encoding.UTF8);
// Set destinations for the e-mail message.
MailAddress toAddr = new MailAddress("ben@contoso.com");
// Specify the message content.
MailMessage message = new MailMessage(fromAddr, toAddr);
message.Body = "This is a test e-mail message.";

Unit 7: Reading and Writing XML Data


(continued)
// Include some non-ASCII characters in body and subject.
string someArrows = new string(
new char[] {'\u2190', '\u2191', '\u2192', '\u2193'});
message.Body += Environment.NewLine + someArrows;
message.BodyEncoding = System.Text.Encoding.UTF8;
message.Subject = "test message 1" + someArrows;
message.SubjectEncoding = System.Text.Encoding.UTF8;
// Set the method that is called back when the send operation ends.
client.SendCompleted += SendCompletedCallback;
// The userState can be any object that allows your callback
// method to identify this send operation.
// For this example, the userToken is a string constant.
string userState = "test message1";
client.SendAsync(message, userState);
Console.WriteLine("Sending message... press c to cancel mail. +
Press any other key to exit.");
string answer = Console.ReadLine();
if (answer.StartsWith("c") && mailSent == false)
{
client.SendAsyncCancel();
}
// Clean up.
message.Dispose();
Console.WriteLine("Goodbye.");
}
}
}

231

Unit 8: Processing XML Data by Using


DOM
How to Load an XML Document into a DOM Tree
This document is adapted from the articles that you can find at the following
locations:

Reading an XML Document into the DOM:


mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.VisualSt
udio.v80.en/WD_XML/html/a4fb291f-5630-49ba-a49a-5b66c3b71e49.htm

Creating XML Readers:


mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.VisualSt
udio.v80.en/WD_XML/html/441e5aac-dfa9-41ed-9336-cd541b11c2d1.htm

How to: Load an XML Document into a DOM Tree


Extensible Markup Language (XML) information is read into memory from
different formats. It can be read from a string, a stream, a URL, a text reader, or
a class derived from the XmlReader.
The XmlDocument type provides two methods for loading XML content. The
Load method brings the document into memory and has overloaded methods
available to take data from each of the possible source formats. There is also a
LoadXml method that reads XML from a string.
The XML document is loaded by using the Load method and a parameter of an
XmlReader. There are several overloaded versions of the Load method, which
accept the following parameters:

Stream

String

TextReader

XmlReader

By default, Load does not check if the XML is valid by using document type
definition (DTD) or schema validation. The Load method only checks if the
XML is well formed.

234

Unit 8: Processing XML Data by Using DOM

The following examples load an XML document named books.xml and


validates it against an XML schema document named books.xsd:
[Visual Basic]
Imports System.Xml
...
' Create the XmlDocument object.
Dim doc As New XmlDocument()
' Enable schema validation, using the books.xsd schema document.
Dim settings As New XmlReaderSettings()
settings.Schemas.Add(Nothing, "books.xsd")
settings.ValidationType = ValidationType.Schema
' Load the books.xml document, and validate it against the schema.
Dim reader As XmlReader = XmlReader.Create("books.xml", settings)
doc.Load(reader)

[C#]
using System.Xml;
...
// Create the XmlDocument object.
XmlDocument doc = new XmlDocument();
// Enable schema validation, using the books.xsd schema document.
XmlReaderSettings settings = new XmlReaderSettings();
settings.Schemas.Add(null, "books.xsd");
settings.ValidationType = ValidationType.Schema;
// Load the books.xml document, and validate it against the schema.
XmlReader reader = XmlReader.Create("books.xml", settings);
doc.Load(reader);

Unit 8: Processing XML Data by Using DOM

The following examples load XML data directly from a string.


[Visual Basic]
Imports System.Xml
...
Dim doc As New XmlDocument()
doc.LoadXml( _
"<root> & _
<child1 attr1='val1' attr2='val2'> text1 </child1> & _
<child2 attr3='val3'> text2 </child2> & _
</root>")

[C#]
using System.Xml;
...
XmlDocument doc = new XmlDocument();
doc.LoadXml(
"<root> +
<child1 attr1='val1' attr2='val2'> text1 </child1> +
<child2 attr3='val3'> text2 </child2> +
</root>");

235

236

Unit 8: Processing XML Data by Using DOM

How to Read XML Data by Using DOM


This document is adapted from the articles that you can find at the following
locations:

Accessing Attributes in the DOM:


mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.VisualSt
udio.v80.en/WD_XML/html/ce2df341-a1a4-4e97-8e1b-cd45b8e3e71e.htm

Node Collections in NamedNodeMaps and NodeLists:


mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.VisualSt
udio.v80.en/WD_XML/html/025954b8-7aa8-47c5-a1c1-f81064fb4d65.htm

XmlNode.ChildNodes property:
mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.NETDE
VFX.v20.en/cpref/html/P_System_Xml_XmlNode_ChildNodes.htm

XmlDocument class:
mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.NETDE
VFX.v20.en/cpref/html/T_System_Xml_XmlDocument.htm

How to: Read XML Data by Using DOM


The XmlDocument class implements the World Wide Web Consortium (W3C)
DOM Level 1 Core and the Core DOM Level 2. The DOM is an in-memory
(cache) tree representation of an Extensible Markup Language (XML)
document, and it enables the navigation and editing of a document.
The XmlDataDocument class extends XmlDocument and allows structured
data to be stored, retrieved, and manipulated through a relational DataSet. This
class allows components to mix XML and relational views of the underlying
data.
To read data in XML by using the DOM, you can retrieve sections of the
document by retrieving nodes in the document.

Accessing Node Collections in NamedNodeMaps and


NodeLists
You can retrieve a set of nodes and put it in an ordered or unordered collection.
When putting a set of nodes into an unordered collection, the set is called a
NamedNodeMap by the W3C. You can retrieve the data by name or index in
this type of collection. Putting a set of nodes in an ordered collection is called a
NodeList by the W3C, and the data can be retrieved by a zero-based index.
NamedNodeMaps and NodeLists are described by the W3C. In the Microsoft
.NET Framework, NamedNodeMap is implemented by the
XmlNamedNodeMap class, and NodeList is implemented by the
XmlNodeList class.

Unit 8: Processing XML Data by Using DOM

237

Retrieving Unordered Nodes by Name or by Index


The XmlNamedNodeMap is described in the W3C specification as the
NamedNodeMap, and it is required to handle an unordered set of nodes with the
ability to reference nodes by their name or index. The only way you have access
to an XmlNamedNodeMap is when an XmlNamedNodeMap is returned
through a method or property. There are three methods or properties that return
an XmlNamedNodeMap:

XmlElement.Attributes

XmlDocumentType.Entities

XmlDocumentType.Notations

For example, the XmlDocumentType.Entities property gets the collection of


XmlEntity nodes declared in the document type declaration. This collection is
returned as an XmlNamedNodeMap, and you can iterate through the collection
and display entity information.
The XmlAttributeCollection is derived from XmlNamedNodeMap. Using the
XmlNamedNodeMap for the attributes, you can get nodes for those attributes
based on their XML names. XmlNamedNodeMap provides an easy method for
manipulating the collection of attributes on an element node.
XmlNamedNodeMap can be contrasted directly with XmlNodeList, which
also implements the IEnumerable interface, but with an index accessor rather
than a string. Adding or removing from an attribute collection, whether using
the AttributeCollection or the XmlNamedNodeMap implementation,
modifies the attribute collection on the element.

238

Unit 8: Processing XML Data by Using DOM

The following code example shows how to copy an attribute and create a new
attribute:
[Visual Basic]
Imports System
Imports System.Xml
Public Class Test
Public Shared Sub Main()
Dim doc As New XmlDocument()
doc.LoadXml( _
"<root> & _
<child1 attr1='val1' attr2='val2'> text1 </child1> & _
<child2 attr3='val3'> text2 </child2> & _
</root>")
' Get the attributes of node "child2".
Dim ac As XmlAttributeCollection = _
doc.DocumentElement.ChildNodes(1).Attributes
' Get the 'attr1' from child1.
Dim attr As XmlAttribute = doc.DocumentElement.ChildNodes(0).Attributes(0)
' Add this attribute to the attributecollection "ac".
ac.SetNamedItem(attr)
' Create a new attribute and add to the collection.
Dim attr2 As XmlAttribute = doc.CreateAttribute("attr4")
attr2.Value = "val4"
ac.SetNamedItem(attr2)
End Sub
End Class

Unit 8: Processing XML Data by Using DOM

239

[C#]
using System;
using System.Xml;
public class Test
{
public static void Main()
{
XmlDocument doc = new XmlDocument();
doc.LoadXml(
"<root> +
<child1 attr1='val1' attr2='val2'> text1 </child1> +
<child2 attr3='val3'> text2 </child2> +
</root>");
// Get the attributes of node "child2".
XmlAttributeCollection ac = doc.DocumentElement.ChildNodes[1].Attributes;
// Get the 'attr1' from child1.
XmlAttribute attr = doc.DocumentElement.ChildNodes[0].Attributes[0];
// Add this attribute to the attributecollection "ac".
ac.SetNamedItem(attr);
// Create a new attribute and add to the collection.
XmlAttribute attr2 = doc.CreateAttribute( "attr4" );
attr2.Value = "val4";
ac.SetNamedItem(attr2);
}
}

Retrieving Ordered Nodes by Array Index


The W3C XML DOM also describes a NodeList, which has the ability to
handle an ordered list of nodes, as opposed to the unordered set handled by the
XmlNamedNodeMap. The NodeList in the.NET Framework is called
XmlNodeList. Methods, and properties that return an XmlNodeList are:

XmlNode.ChildNodes

XmlDocument.GetElementsByTagName

XmlElement.GetElementsByTagName

XmlNode.SelectNodes

240

Unit 8: Processing XML Data by Using DOM

The XmlNodeList has a Count property that can be used to write loops to
iterate over the nodes in the XmlNodeList, as shown in the following code
sample:
[Visual Basic]
Dim doc as New XmlDocument()
doc.Load("books.xml")
' Retrieve all book titles.
Dim root as XmlElement = doc.DocumentElement
Dim elemList as XmlNodeList = root.GetElementsByTagName("title")
Dim i as Integer
For i=0 To elemList.Count-1
' Display all book titles in the Node List.
Console.WriteLine(elemList.ItemOf(i).InnerXml)
Next
[C#]
XmlDocument doc = new XmlDocument();
doc.Load("books.xml");
// Retrieve all book titles.
XmlElement root = doc.DocumentElement;
XmlNodeList elemList = root.GetElementsByTagName("title");
for (int i=0; i < elemList.Count; i++)
{
// Display all book titles in the Node List.
Console.WriteLine(elemList[i].InnerXml);
}

Unit 8: Processing XML Data by Using DOM

In addition to the Count property, there is a GetEnumerator method that


provides a For Each style iteration over the collection of nodes in the
XmlNodeList. The following code example shows the use of the For Each
statement:
[Visual Basic]
Dim doc As New XmlDocument()
doc.Load("books.xml")
' Get book titles.
Dim root As XmlElement = doc.DocumentElement
Dim elemList As XmlNodeList = root.GetElementsByTagName("title")
For Each title As XmlNode in elemList
Console.WriteLine(title.InnerText)
Next
[C#]
XmlDocument doc = new XmlDocument();
doc.Load("books.xml");
// Get book titles.
XmlElement root = doc.DocumentElement;
XmlNodeList elemList = root.GetElementsByTagName("title");
foreach (XmlNode title in elemList)
{
Console.WriteLine(title.InnerText);
}

241

242

Unit 8: Processing XML Data by Using DOM

Accessing Child Nodes


The XmlNode.ChildNodes property is an XmlNodeList that contains all the
child nodes of the node. If there are no child nodes, this property returns an
empty XmlNodeList. The following example displays all the child nodes of the
root element:
[Visual
Imports
Imports
Imports

Basic]
System
System.IO
System.Xml

Public Class Sample


Public Shared Sub Main()
Dim doc As New XmlDocument()
doc.LoadXml("<book ISBN='1-861001-57-5'>" & _
"
<title>Pride And Prejudice</title>" & _
"
<price>19.95</price>" & _
"</book>")
Dim root As XmlNode = doc.DocumentElement
' Display the contents of the child nodes.
If root.HasChildNodes Then
Dim i As Integer
For i = 0 To root.ChildNodes.Count - 1
Console.WriteLine(root.ChildNodes(i).InnerText)
Next i
End If
End Sub
End Class

Unit 8: Processing XML Data by Using DOM

243

[C#]
using System;
using System.IO;
using System.Xml;
public class Sample
{
public static void Main()
{
XmlDocument doc = new XmlDocument();
doc.LoadXml("<book ISBN='1-861001-57-5'>" +
"
<title>Pride And Prejudice</title>" +
"
<price>19.95</price>" +
"</book>");
XmlNode root = doc.DocumentElement;
// Display the contents of the child nodes.
if (root.HasChildNodes)
{
for (int i = 0; i < root.ChildNodes.Count; i++)
{
Console.WriteLine(root.ChildNodes[i].InnerText);
}
}
}
}

Accessing Attributes
Attributes are properties of the element, not children of the element. This
distinction is important because of the methods used to navigate sibling, parent,
and child nodes of the XML DOM. For example, the PreviousSibling and
NextSibling methods are not used to navigate from an element to an attribute or
between attributes. Instead, an attribute is a property of an element and has
distinct methods of navigation.
When the current node is an element, use the HasAttributes property to see if
there are any attributes associated with the element. After it is known that an
element has attributes, there are multiple methods for accessing attributes. To
retrieve a single attribute from the element, you can use the GetAttribute and
GetAttributeNode methods of the XmlElement. You can also obtain all the
attributes into a collection. Obtaining the collection is useful if you need to
iterate over the collection. If you want all attributes from the element, use the
Attributes property of the element to retrieve all the attributes into a collection.

244

Unit 8: Processing XML Data by Using DOM

Retrieving All Attributes into a Collection


If you want all the attributes of an element node put into a collection, call the
XmlElement.Attributes property. This property gets the
XmlAttributeCollection that contains all the attributes of an element. The
XmlAttributeCollection class inherits from the XmlNamedNode map.
Therefore, the methods and properties available on the collection include those
that are available on a named node map, in addition to the methods and
properties specific to the XmlAttributeCollection class, such as the ItemOf
property or the Append method. Each item in the attribute collection represents
an XmlAttribute node. To find the number of attributes on an element, get the
XmlAttributeCollection and use the Count property to see how many
XmlAttribute nodes are in the collection.
The following code example shows how to retrieve an attribute collection and,
using the Count method for the looping index, iterate through it. The code then
shows how to retrieve a single attribute from the collection and display its
value, as shown in the following example:

Unit 8: Processing XML Data by Using DOM


[Visual
Imports
Imports
Imports

245

Basic]
System
System.IO
System.Xml

Public Class Sample


Public Shared Sub Main()
Dim doc as New XmlDocument()
doc.LoadXml("<book genre='novel' ISBN='1-861001-57-5' misc='sale item'>" & _
" <title>The Handmaid's Tale</title>" & _
" <price>14.95</price>" & _
"</book>")
' Move to an element.
Dim myElement as XmlElement = doc.DocumentElement
' Create an attribute collection from the element.
Dim attrColl as XmlAttributeCollection = myElement.Attributes
' Show the collection by iterating over it.
Console.WriteLine("Display all the attributes in the collection...")
Dim i as integer
For i = 0 to attrColl.Count - 1
Console.Write("{0} = ", attrColl.ItemOf(i).Name)
Console.Write("{0}", attrColl.ItemOf(i).Value)
Console.WriteLine()
Next
' Retrieve a single attribute from the collection; specifically, the
' attribute with the name "misc".
Dim attr as XmlAttribute = attrColl("misc")
' Retrieve the value from that attribute.
Dim miscValue as String = attr.InnerXml
Console.WriteLine("Display the attribute information.")
Console.WriteLine(miscValue)
End Sub
End Class

246

Unit 8: Processing XML Data by Using DOM

[C#]
using System;
using System.IO;
using System.Xml;
public class Sample
{
public static void Main()
{
XmlDocument doc = new XmlDocument();
doc.LoadXml("<book genre='novel' ISBN='1-861001-57-5' misc='sale item'>" +
" <title>The Handmaid's Tale</title>" +
" <price>14.95</price>" +
"</book>");
// Move to an element.
XmlElement myElement= doc.DocumentElement;
// Create an attribute collection from the element.
XmlAttributeCollection attrColl = myElement.Attributes;
// Show the collection by iterating over it.
Console.WriteLine("Display all the attributes in the collection...");
for (int i = 0; i < attrColl.Count; i++)
{
Console.WriteLine("{0} = {1}", attrColl[i].Name, attrColl[i].Value);
}
// Retrieve a single attribute from the collection; specifically, the
// attribute with the name "misc".
XmlAttribute attr = attrColl["misc"];
// Retrieve the value from that attribute.
String miscValue = attr.InnerXml;
Console.WriteLine("Display the attribute information.");
Console.WriteLine(miscValue);
}
}

The preceding examples display the following output:


Display all the attributes in the collection...
genre = novel
ISBN = 1-861001-57-5
misc = sale item
Display the attribute information.
sale item

The information in an attribute collection can be retrieved by name or index


number. The preceding example shows how to retrieve data by name. The
following example shows how to retrieve data by index number.
Because the XmlAttributeCollection is a collection and can be iterated over by
name or index, this example shows selecting the first attribute out of the
collection by using a zero-based index and using the following file,
baseuri.xml, as input.

Unit 8: Processing XML Data by Using DOM

247

In this example, the following document fragment is read into XmlDocument,


and information, such as the name and value, of the attributes is retrieved.
<!-- XML fragment -->
<book genre="novel">
<title>Pride And Prejudice</title>
</book>
[Visual
Imports
Imports
Imports

Basic]
System
System.IO
System.Xml

Public Class Sample


Public Shared Sub Main()
' Create the XmlDocument.
Dim doc As New XmlDocument()
doc.Load("http://localhost/baseuri.xml")
' Display information on the attribute node. The value
' returned for BaseURI is 'http://localhost/baseuri.xml'.
Dim attr As XmlAttribute = doc.DocumentElement.Attributes(0)
Console.WriteLine("Name of the attribute: {0}", attr.Name)
Console.WriteLine("Base URI of the attribute: {0}", attr.BaseURI)
Console.WriteLine("The value of the attribute: {0}", attr.InnerText)
End Sub
End Class
[C#]
using System;
using System.IO;
using System.Xml;
public class Sample
{
public static void Main()
{
// Create the XmlDocument.
XmlDocument doc = new XmlDocument();
doc.Load("http://localhost/baseuri.xml");
// Display information on the attribute node. The value
// returned for BaseURI is 'http://localhost/baseuri.xml'.
XmlAttribute attr = doc.DocumentElement.Attributes[0];
Console.WriteLine("Name of the attribute: {0}", attr.Name);
Console.WriteLine("Base URI of the attribute: {0}", attr.BaseURI);
Console.WriteLine("The value of the attribute: {0}", attr.InnerText);
}
}

248

Unit 8: Processing XML Data by Using DOM

Retrieving an Individual Attribute Node


To retrieve a single attribute node from an element, the
XmlElement.GetAttributeNode method is used. This method returns an object
of type XmlAttribute. After you have an XmlAttribute, all the methods and
properties available in the XmlAttribute class are available on that object.
In the following example, an XML document fragment is loaded into
XmlDocument, and the value of an attribute is displayed by using the
InnerXml property:
[Visual
Imports
Imports
Imports

Basic]
System
System.IO
System.Xml

Public Class Sample


Public Shared Sub Main()
Dim doc as New XmlDocument()
doc.LoadXml("<book genre='novel' ISBN='1-861001-57-5' misc='sale item'>" & _
"
<title>The Handmaid's Tale</title>" & _
"
<price>14.95</price>" & _
"</book>")
' Move to an element.
Dim root as XmlElement root = doc.DocumentElement
' Get an attribute.
Dim attr as XmlAttribute attr = root.GetAttributeNode("ISBN")
' Display the value of the attribute.
Dim attrValue as string attrValue = attr.InnerXml
Console.WriteLine(attrValue)
End Sub
End Class

Unit 8: Processing XML Data by Using DOM

249

[C#]
using System;
using System.IO;
using System.Xml;
public class Sample
{
public static void Main()
{
XmlDocument doc = new XmlDocument();
doc.LoadXml("<book genre='novel' ISBN='1-861003-78' misc='sale item'>" +
" <title>The Handmaid's Tale</title>" +
" <price>14.95</price>" +
"</book>");
// Move to an element.
XmlElement root = doc.DocumentElement;
// Get an attribute.
XmlAttribute attr = root.GetAttributeNode("ISBN");
// Display the value of the attribute.
String attrValue = attr.InnerXml;
Console.WriteLine(attrValue);
}
}

You can also do as shown in the previous example, where a single attribute
node is retrieved from the attribute collection. The following code example
shows how one line of code can be written to retrieve a single attribute by index
number from the root of the XML document tree, also known as the
DocumentElement property.
[Visual Basic]
If doc.DocumentElement.HasAttributes Then
Dim attr As XmlAttribute = doc.DocumentElement.Attributes(0)
' ...
End If
[C#]
if (doc.DocumentElement.HasAttributes)
{
XmlAttribute attr = doc.DocumentElement.Attributes[0];
// ...
}

250

Unit 8: Processing XML Data by Using DOM

How to Create a Document in a DOM Tree


This document is adapted from the articles that you can find at the following
locations:

Xml Document Creation:


mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.VisualSt
udio.v80.en/WD_XML/html/877e9c62-b082-4bfb-bc5b-f47297eb30ef.htm

CreateXmlDeclaration Method:
mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.NETDE
VFX.v20.en/cpref/html/M_System_Xml_XmlDocument_CreateXmlDeclara
tion_2_394cbffa.htm

CreateElement Method:
mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.NETDE
VFX.v20.en/cpref/html/M_System_Xml_XmlDocument_CreateElement_2_
394cbffa.htm

How to: Create an XML Document


There are two ways to create an Extensible Markup Language (XML)
document. One way is to create an XmlDocument with no parameters. The
other way is to create an XmlDocument and pass it an XmlNameTable as a
parameter.
The following example shows how to create a new, empty XmlDocument
using no parameters.
[Visual Basic]
Dim doc As New XmlDocument()
[C#]
XmlDocument doc = new XmlDocument();

After a document is created, you can load it with data from a string, a stream, a
URL, a text reader, or an XmlReader derived class by using the Load method.
There is also another load method, the LoadXML method, which reads XML
from a string.

Creating an XML Declaration


A valid XML document must have at the very least an XML declaration and a
root element. An XML document must also conform to the rules of being well
formed; for example, each and every element must be closed after it has been
opened.
The XmlDocument class has a CreateXmlDeclaration method that is used for
creating XML declarations. This class has the following parameters:

Version. The version must be 1.0.

Encoding. The value of the encoding attribute. This is the encoding that is
used when you save the XmlDocument to a file or a stream; therefore, it

Unit 8: Processing XML Data by Using DOM

251

must be set to a string supported by the Encoding classotherwise, Save


fails. If this is a null reference (Nothing in Microsoft Visual Basic) or
String.Empty, the Save method does not write an encoding attribute on the
XML declaration, and therefore, the default encoding, UTF-8, is used.
The attributes are exposed as special properties on the XmlDeclaration node
and not as XmlAttribute nodes.
Although this method creates the new object in the context of the document, it
does not automatically add the new object to the document tree. To add the new
object, you must explicitly call one of the node insert methods. According to the
World Wide Web Consortium (W3C) XML 1.0 Recommendation (see the W3C
Web site), the XmlDeclaration node must be the first node in the document.
The following example creates an XML declaration and adds it to the
document:
[Visual Basic]
Imports System.Xml
Public Class Sample
Public Shared Sub Main()
Dim doc As New XmlDocument()
doc.LoadXml("<book genre='novel' ISBN='1-861001-57-5'>" & _
"
<title>Pride And Prejudice</title>" & _
"</book>")
' Create an XML declaration.
Dim xmldecl As XmlDeclaration
xmldecl = doc.CreateXmlDeclaration("1.0", Nothing, Nothing)
' Add the new node to the document.
Dim root As XmlElement = doc.DocumentElement
doc.InsertBefore(xmldecl, root)
Console.WriteLine("Display the modified XML...")
doc.Save(Console.Out)
End Sub
End Class

252

Unit 8: Processing XML Data by Using DOM

[C#]
using System.Xml;
public class Sample
{
public static void Main()
{
XmlDocument doc = new XmlDocument();
doc.LoadXml("<book genre='novel' ISBN='1-861001-57-5'>" +
"
<title>Pride And Prejudice</title>" +
"</book>");
// Create an XML declaration.
XmlDeclaration xmldecl;
xmldecl = doc.CreateXmlDeclaration("1.0",null,null);
// Add the new node to the document.
XmlElement root = doc.DocumentElement;
doc.InsertBefore(xmldecl, root);
Console.WriteLine("Display the modified XML...");
doc.Save(Console.Out);
}
}

Creating Elements
To create an element in an XML document, call the
XmlDocument.CreateElement method. This method creates an element with
the specified name. The simplest overload takes one parameter:

Name. The qualified name of the element. If the name contains a colon, the
XmlNode.Prefix property reflects the part of the name preceding the colon,
and the XmlDocument.LocalName property reflects the part of the name
after the colon. The qualified name cannot include a prefix of xmlns.

There are two other overloads that take two parameters (qualifiedName and
namespaceURI) and three parameters (prefix, localName, and namespaceURI).
Although the CreateElement method creates the new object in the context of
the document, it does not automatically add the new object to the document
tree. To add the new object, you must explicitly call one of the node insert
methods.
According to the W3C XML 1.0 Recommendation, Element nodes are allowed
within Document and Element nodes and in EntityReference nodes when the
EntityReference node is not a child of an Attribute node.

Unit 8: Processing XML Data by Using DOM

The following example creates a new element and adds it to the document:
[Visual Basic]
Imports System.Xml
Public Class Sample
Public Shared Sub Main()
' Create the XmlDocument.
Dim doc As New XmlDocument()
doc.LoadXml("<book genre='novel' ISBN='1-861001-57-5'>" & _
"<title>Pride And Prejudice</title>" & _
"</book>")
' Create a new node and add it to the document.
' The text node is the content of the price element.
Dim elem As XmlElement = doc.CreateElement("price")
Dim text As XmlText = doc.CreateTextNode("19.95")
doc.DocumentElement.AppendChild(elem)
doc.DocumentElement.LastChild.AppendChild(text)
Console.WriteLine("Display the modified XML...")
doc.Save(Console.Out)
End Sub
End Class
[C#]
using System.Xml;
public class Sample
{
public static void Main()
{
// Create the XmlDocument.
XmlDocument doc = new XmlDocument();
doc.LoadXml("<book genre='novel' ISBN='1-861001-57-5'>" +
"<title>Pride And Prejudice</title>" +
"</book>");
// Create a new node and add it to the document.
// The text node is the content of the price element.
XmlElement elem = doc.CreateElement("price");
XmlText text = doc.CreateTextNode("19.95");
doc.DocumentElement.AppendChild(elem);
doc.DocumentElement.LastChild.AppendChild(text);
Console.WriteLine("Display the modified XML...");
doc.Save(Console.Out);
}
}

253

254

Unit 8: Processing XML Data by Using DOM

How to Modify XML Data by Using DOM


This document is adapted from the articles that you can find at the following
locations:

Inserting Nodes into an XML Document:


mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.VisualSt
udio.v80.en/WD_XML/html/891e54f5-54f6-4bb4-b624-9d1b6a1f1216.htm

Removing Nodes, Content and Values from an XML Document:


mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.VisualSt
udio.v80.en/WD_XML/html/d818a29a-6ee5-4725-97c4-78cf60a156b6.htm

Modifying Nodes, Content and Values in an XML Document:


mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.VisualSt
udio.v80.en/WD_XML/html/761773e0-db72-4986-b9f5-a522213d8397.htm

ParentNode method:
mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.NETDE
VFX.v20.en/cpref/html/P_System_Xml_XmlNode_ParentNode.htm

CloneNode method:
mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.NETDE
VFX.v20.en/cpref/html/M_System_Xml_XmlDocumentFragment_CloneNo
de_1_d8a57f8b.htm

Event Handling in an XML Document Using the


XmlNodeChangeEventArgs:
mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.VisualSt
udio.v80.en/WD_XML/html/0fe844e3-5b6f-4fe7-ad15-22459501738b.htm

Unit 8: Processing XML Data by Using DOM

255

How to: Modify an XML Document by Using DOM


You can modify a document by inserting new nodes. The inserted nodes can be
newly created, existing in the same document, or they can be imported from
another document.

Creating New Nodes in the DOM Tree


The XmlDocument has a Create method for all of the node types. Supply the
method with a name when required and content or other parameters for those
nodes that have content (for example, a text node), and the node will be created.
The following methods are ones that need a name and a few other parameters
filled to create an appropriate node:

CreateComment

CreateCDataSection

CreateDocumentFragment

CreateDocumentType

CreateElement

CreateProcessingInstruction

CreateTextNode

CreateXmlDeclaration

CreateWhitespace

CreateSignificantWhitespace

Other node types have more requirements than just providing data to
parameters.
After new nodes are created, there are several methods available to insert them
into the tree. The table lists the methods with a description of where the new
node will appear in the Extensible Markup Language (XML) DOM.
Method

Node Placement

InsertBefore

Is inserted before the reference node.

InsertAfter

Is inserted after the reference node.

AppendChild

Adds the node to the end of the list of child nodes for the given
node. If the node being added is an XmlDocumentFragment,
the entire contents of the document fragment are moved into the
child list of this node.

PrependChild

Adds the node to the beginning of the list of child nodes of the
given node. If the node being added is an
XmlDocumentFragment, the entire contents of the document
fragment are moved into the child list of this node.

Append

Appends an XmlAttribute node to the end of the attribute


collection that is associated with an element.

256

Unit 8: Processing XML Data by Using DOM

Copying Existing Nodes


There are many methods and properties in the XML DOM that you can use to
select a node, including SelectSingleNode, ChildNodes, and Attributes. After
the node is selected, you can insert it into the tree by using one of the insert
methods that work for that particular node type. The only restriction to inserting
a node into the tree is that the document must still be well formed after the node
is inserted. When an existing node is inserted into the DOM tree, it is removed
from its original position and added to its target position.

Removing Nodes, Content, and Values


After an XML DOM is in memory, you can remove nodes from the tree, or
remove content and values from certain node types.
To remove a node from the XML DOM, use the RemoveChild Method to
remove a specific node. When you remove a node, the method removes the
subtree belonging to the node being removed; that is, if it is not a leaf node.
The following example shows how to remove the <title> element from an XML
fragment:
[Visual Basic]
Dim doc As New XmlDocument()
doc.LoadXml( _
"<book genre='novel' ISBN='1-861001-57-5'>" & _
"
<title>Pride And Prejudice</title>" & _
"</book>")
' Remove the title element.
Dim root As XmlNode = doc.DocumentElement
root.RemoveChild(root.FirstChild)
[C#]
XmlDocument doc = new XmlDocument();
doc.LoadXml(
"<book genre='novel' ISBN='1-861001-57-5'>" +
"
<title>Pride And Prejudice</title>" +
"</book>");
// Remove the title element.
XmlNode root = doc.DocumentElement;
root.RemoveChild(root.FirstChild);

To remove multiple nodes from the DOM, use the RemoveAll Method to
remove all the children and attributes, if applicable, of the current node.

Unit 8: Processing XML Data by Using DOM

257

Removing Attributes
There are many ways to remove attributes. One technique is to remove them
from the attribute collection; use the Attributes property to get the collection of
attributes for the element, and then call one of the following methods to remove
an attribute from the attribute collection:

Use the XmlAttributeCollection.Remove method to remove a specific


attribute.

Use the XmlAttributeCollection.RemoveAll method to remove all


attributes from the collection and leave the element with no attributes.

Use the XmlAttributeCollection.RemoveAt method to remove an attribute


from the attribute collection by using its index number.

The following methods remove attributes from the element node.

Use the XmlElement.RemoveAllAttributes method to remove the attribute


collection.

Use the XmlElement.RemoveAttribute method to remove a single


attribute by name from the collection.

Use the XmlElement.RemoveAttributeAt method to remove a single


attribute by index number from the collection.

One more alternative to removing attributes is to get the attribute from the
attribute collection and remove the attribute node directly. To get the attribute
from the attribute collection, you can specify the attribute by its local name, its
ordinal position, or its fully qualified name.
When called with an XmlAttribute, the RemoveAll sets the value of the
attribute to String.Empty, as an attribute cannot exist without a value.

Removing Node Content


For node types that inherit from XmlCharacterData, which are the
XmlComment, XmlText, XmlCDataSection, XmlWhitespace, and
XmlSignificantWhitespace node types, you can remove characters by using
the DeleteData method, which removes a range of characters from the node. If
you want to remove content completely, you remove the node that contains the
content. If you want to keep the node, but the content is incorrect, then modify
the content.

Modifying Nodes, Content, and Values


There are many ways you can modify the nodes and content in a document.
You can:

Change the value of nodes by using the XmlNode.Value method.

Modify an entire set of nodes by replacing the nodes with new nodes. This
modification is done by using the XmlNode.InnerXml property.

Replace existing nodes with new nodes by using the


XmlNode.ReplaceChild method.

Add additional characters to nodes that inherit from the XmlCharacter


class by using the XmlCharacter.AppendData method, the
XmlCharacter.InsertData method, or the XmlCharacter.ReplaceData
method.

258

Unit 8: Processing XML Data by Using DOM

Modify the content by removing a range of characters by using the


DeleteData method on node types that inherit from XmlCharacterData.

Update an attribute value by using the SetAttribute method. SetAttribute


creates a new attribute if one does not exist and updates an attribute value if
the attribute already exists.

A simple technique for changing the value of a node is to use the Value
property on the node. The following table lists the node types that this single
line of code works on and shows exactly what data for that node type will be
changed.
Node type

Data changed

Attribute

The value of the attribute.

CDATASection

The content of the CDATASection.

Comment

The content of the comment.

ProcessingInstruction

The content of the processing instruction, excluding the


target.

Text

The content of the text.

XmlDeclaration

The content of the declaration, excluding the <?xml and ?>


markup.

Whitespace

The value of the white space. You can set the value to be
one of the four recognized XML white space characters:
space, tab, carriage return, or linefeed.

SignificantWhitespace

The value of the significant white space. You can set the
value to be one of the four recognized XML white space
characters: space, tab, carriage return, or linefeed.

Any node type not listed in the table is not a valid node type to set a value on.
Setting a value on any other node type throws an InvalidOperationException.
The InnerXml property changes the markup of the child nodes for the current
node. Setting this property replaces the child nodes with the parsed contents of
the given string. The parsing is done in the current namespace context. In
addition, InnerXml removes redundant namespace declarations. As a result,
numerous cut and paste operations do not increase the size of your document
with redundant namespace declarations.
When using the ReplaceChild and RemoveChild methods, the methods return
the replaced or removed node. The replaced or removed node can then be
reinserted somewhere else in the XML DOM. The ReplaceChild method does
two validation checks on the node being inserted into the document. The first
check ensures that the node is becoming a child of a node that can have child
nodes of its type. The second check ensures that the node being inserted is not
an ancestor of the node it is becoming a child of. Violating either of these
conditions throws an InvalidOperationException exception.
It is valid to add or remove a read-only child from a node that can be edited.
However, attempts to modify the read-only node itself will throw an
InvalidOperationException exception.

Unit 8: Processing XML Data by Using DOM

259

Accessing Parent Nodes


The parentNode property returns the XmlNode that is the parent of the current
node. If a node has just been created and not yet added to the tree, or if it has
been removed from the tree, the parent is a null reference (Nothing in
Microsoft Visual Basic). For all other nodes, the value returned depends on
the NodeType of the node. The following table describes the possible return
values for the ParentNode property:
Node Type

Return Value of ParentNode

Attribute,
Document,
DocumentFragment,
Entity,

Returns a null reference (Nothing); these nodes do not have


parents.

Notation
CDATA

Returns the element or entity reference containing the


CDATA section.

Comment

Returns the element, entity reference, document type, or


document containing the comment.

DocumentType

Returns the document node.

Element

Returns the parent node of the element. If the element is the


root node in the tree, the parent is the document node.

EntityReference

Returns the element, attribute, or entity reference containing


the entity reference.

ProcessingInstruction

Returns the document, element, document type, or entity


reference containing the processing instruction.

Text

Returns the parent element, attribute, or entity reference


containing the text node.

260

Unit 8: Processing XML Data by Using DOM

Cloning Nodes
The CloneNode method is used to duplicate nodes. When overridden in a
derived class, this method creates a duplicate of the node. The CloneNode
method takes a Boolean parameter that specifies whether the clone operation is
recursive.
The following table describes the specific behavior for each XmlNodeType.
XmlNodeType

CloneNode(true)

CloneNode(false)

Attribute

Clones the attribute node,


including child nodes.

Clones the attribute node,


including child nodes.

CData

Clones the CData node,


including its data content.

Clones the CData node,


including its data content.

Comment

Clones the comment node,


including its text content.

Clones the comment node,


including its text content.

Document

Clones the document node,


including any child nodes.

Clones the document node.

DocumentFragment

Clones the document


fragment node, including any
child nodes.

Clones the document


fragment node.

DocumentType

Clones the document type


node.

Clones the document type


node.

Element

Clones the element node, its


attributes, and any child
nodes.

Clones the element node


and its attributes, including
any default attributes.

Entity

Entity nodes cannot be


cloned.

Entity nodes cannot be


cloned.

EntityReference

Clones the entity reference


node. The replacement text is
not included.

Clones the entity reference


node. The replacement text
is not included.

Notation

Notation nodes cannot be


cloned.

Notation nodes cannot be


cloned.

ProcessingInstruction

Clones the processing


instruction node, including
its target and data.

Clones the processing


instruction node, including
its target and data.

SignificantWhitespace

Clones the significant white


space node, including its data
value.

Clones the significant


white space node,
including its data value.

Text

Clones the text node,


including its data value.

Clones the text node,


including its data value.

Whitespace

Clones the white space node,


including its data value.

Clones the white space


node, including its data
value.

XmlDeclaration

Clones the XmlDeclaration


node, including its data
value.

Clones the XmlDeclaration


node, including its data
value.

All other node types.

These node types cannot be


cloned.

These node types cannot


be cloned.

Unit 8: Processing XML Data by Using DOM

261

The following example shows the difference between a deep and shallow clone:
[Visual Basic]
Imports System.Xml
Public Class Sample
Public Shared Sub Main()
Dim doc As New XmlDocument()
doc.LoadXml( _
"<book ISBN='1-861001-57-5'>" & _
" <title>Pride And Prejudice</title>" & _
" <price>19.95</price>" & _
"</book>")
Dim root As XmlNode = doc.FirstChild
' Create a deep clone. The cloned node includes the child nodes.
Dim deep As XmlNode = root.CloneNode(True)
Console.WriteLine(deep.OuterXml)
' Create a shallow clone. The cloned node does not include the child nodes,
' but does include its attribute.
Dim shallow As XmlNode = root.CloneNode(False)
Console.WriteLine(shallow.OuterXml)
End Sub
End Class

262

Unit 8: Processing XML Data by Using DOM

[C#]
using System.Xml;
public class Sample
{
public static void Main()
{
XmlDocument doc = new XmlDocument();
doc.LoadXml(
"<book ISBN='1-861001-57-5'>" +
" <title>Pride And Prejudice</title>" +
" <price>19.95</price>" +
"</book>");
XmlNode root = doc.FirstChild;
// Create a deep clone. The cloned node includes the child nodes.
XmlNode deep = root.CloneNode(true);
Console.WriteLine(deep.OuterXml);
// Create a shallow clone. The cloned node does not include the child nodes,
// but does include its attribute.
XmlNode shallow = root.CloneNode(false);
Console.WriteLine(shallow.OuterXml);
}
}

Unit 8: Processing XML Data by Using DOM

263

How to Save a DOM Tree to an XML Document


This document is adapted from the article that you can find at the following
location:

Saving and Writing a Document:


mshelp://MS.VSCC.v80/MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.VisualSt
udio.v80.en/WD_XML/html/097b0cb1-5743-4c3a-86ef-caf5cbe6750d.htm

How to: Save a DOM Tree to an XML Document


To save a DOM tree to an XML document, call the Save method on the
XmlDocument object.
The following example loads an XML document named books.xml into a DOM
tree, performs some tasks on the data in the DOM tree, and then saves the DOM
tree to a new XML document named newbooks.xml.
[Visual Basic]
Dim mydoc As New XmlDocument()
mydoc.Load("books.xml")
' Modify the data in the DOM tree.
' ...
' Save the DOM tree.
mydoc.Save("newbooks.xml")
[C#]
XmlDocument mydoc = new XmlDocument();
mydoc.Load("books.xml");
// Modify the data in the DOM tree.
// ...
// Save the DOM tree.
mydoc.Save("newbooks.xml");

When you save an XmlDocument, the saved document may be different from
the original in the following ways:

If the PreserveWhitespace property is set to true before the Save method


is called, white space in the document is preserved in the output; otherwise,
if this property is false, XmlDocument auto-indents the output.

All the white space between attributes is reduced to a single space character.

The white space between elements is changed. Significant white space is


preserved and insignificant white space is not. But when the document is
saved, it will use the XmlTextWriter Indenting mode by default to neatly
print the output to make it more readable.

264

Unit 8: Processing XML Data by Using DOM

The quote character used around attribute values is changed to double


quotes by default. You can use the QuoteChar property on
XmlTextWriter to set the quote character to either double quote or single
quote.

By default, general entities such as &amp; are preserved. However, if you


construct an XmlValidatingReader that has the default EntityHandling
setting of ExpandEntities, and you then call Load, the general entities will
be expanded and you lose general entities in the saved document.

By default, numeric character entities such as &#123; are expanded.

The byte order mark found in the input document is not preserved. UCS-2 is
saved as UTF-8 unless you explicitly create an Extensible Markup
Language (XML) declaration that specifies a different encoding.

If you want to write out the XmlDocument into a file or stream, the output
written out is the same as the content of the document. That is, the XML
declaration is written out only if there is one contained in the document,
and the encoding used when writing out the document is the same encoding
given in the declaration node, depending on which overload of the Save
method you use.

Você também pode gostar