Академический Документы
Профессиональный Документы
Культура Документы
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.
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.
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
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
viii
Index
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
Description
ProductID
int
Name
nvarchar(50)
ProductNumber
nvarchar(25)
MakeFlag
bit
0 = Product is purchased
1 = Product is made in-house
FinishedGoodsFlag
bit
Color
nvarchar(15)
Product color
SafetyStockLevel
smallint
ReorderPoint
smallint
StandardCost
money
ListPrice
money
Selling price
Size
nvarchar(5)
Product size
SizeUnitMeasureCode
nchar(3)
Description
WeightUnitMeasureCode
nchar(3)
Weight
decimal(8, 2)
Product weight
DaysToManufacture
int
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
ProductModelID
int
SellStartDate
datetime
SellEndDate
datetime
DiscontinuedDate
datetime
rowguid
uniqueidentifier
The ROWGUIDCOL
number that uniquely
identifies the record (for
merge replication)
ModifiedDate
datetime
You will use the following stored procedure to query data in the
Production.Product table:
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.
Description
Provider
Connection Timeout or
Connect Timeout
Data Source
(continued)
Parameter
Description
Integrated Security or
Trusted_Connection
User ID
Password
10
11
12
13
Description
<appSettings>
<connectionStrings>
<identity>
<sessionState>
14
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.
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 "
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>
...
"connectionStrings"
"Web-site-folder "
16
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
17
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:
18
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();
19
Description
InfoMessage
StateChange
20
[Visual Basic]
Dim aConnection As New SqlConnection(aConnectionString)
AddHandler aConnection.InfoMessage, AddressOf OnInfoMessage
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
21
22
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
Description
Action
1-10
11-16
17-19
20-25
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
24
[C#]
try
{
// Data-related code.
}
catch (SqlException ex)
{
Console.WriteLine("Exception severity {0}", ex.Class);
Console.WriteLine("Message: {0}", ex.Message);
DisplaySqlErrors(ex.Errors);
}
25
26
27
28
29
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
Default
Description
Connection Lifetime
Connection Reset
true
Enlist
true
100
31
(continued)
Property name
Default
Description
Pooling
true
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;
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
Default
Description
Connection Lifetime
Connection Reset
true
Enlist
true
33
(continued)
Property name
Default
Description
100
Pooling
true
34
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
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.
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
Description
ProductID
int
Name
nvarchar(50)
ProductNumber
nvarchar(25)
MakeFlag
bit
0 = Product is purchased
1 = Product is made in-house
FinishedGoodsFlag
bit
Color
nvarchar(15)
Product color
SafetyStockLevel
smallint
ReorderPoint
smallint
StandardCost
money
ListPrice
money
Selling price
Size
nvarchar(5)
Product size
SizeUnitMeasureCost
nchar(3)
WeightUnitMeasureCost
nchar(3)
Weight
decimal(8, 2)
Product weight
DaysToManufacture
int
36
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
ProductModelID
int
SellStartDate
datetime
SellEndDate
datetime
DiscontinuedDate
datetime
rowguid
uniqueidentifier
ROWGUIDCOL number
that uniquely identifies the
record (for merge
replication)
ModifiedDate
datetime
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.
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
Description
@prmLowerPrice
decimal
@prmUpperPrice
decimal
@prmAveragePriceInRange
decimal
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
Description
ProductID
int
Description
nvarchar(50)
Product description
ListPrice
money
SuggestionTimestamp
datetime
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
Description
@prmSuccess
bit
38
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
Description
@prmSuccess
bit
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
Description
@prmFilter
nvarchar(50)
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
Description
@prmDescription
nvarchar(50)
Product description
@prmListPrice
money
@prmSuggestionTimestamp
datetime
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
Description
@prmProductID
int
@prmDescription
nvarchar(50)
@prmListPrice
money
@prmSuggestionTimestamp
datetime
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
Description
@prmProductID
int
39
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
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
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.
}
}
42
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
[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.
}
}
43
Description
CommandBehavior.CloseConnection
CommandBehavior.SingleResult
CommandBehavior.SingleRow
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
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.
Return reader
End Function
End Class
45
46
[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);
}
return reader;
}
}
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
48
[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();
}
}
49
50
Description
ID
runat="server"
TypeName
SelectMethod
InsertMethod,
UpdateMethod,
DeleteMethod
51
52
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
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
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
54
[C#]
string sql = "SELECT Name, ListPrice " +
"FROM Production.Product " +
"WHERE Name LIKE @NamePart";
using (SqlConnection connection = new SqlConnection(aConnectionString))
{
connection.Open();
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
[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
57
58
Value
Server name
(local)
Authentication
59
60
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
61
62
[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.");
}
}
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
64
[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.");
}
}
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
Collation Types
ms-help://MS.SQLCC.v9/MS.SQLSVR.v9.en/udb9/html/0770f908-abc34401-9934-8621118e8335.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
Collation Types
There are two groups of collations in SQL Server 2005: Windows collations
and SQL collations.
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.
67
Case-sensitive (_CS)
Accent-sensitive (_AS)
Kana-sensitive (_KS)
Width-sensitive (_WS)
68
69
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
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 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;
71
[C#]
using System.Globalization;
using System.Threading;
Thread.CurrentThread.CurrentCulture =
CultureInfo.CreateSpecificCulture(Request.UserLanguages[0]);
[C#]
string formattedDate = String.Format({0:f}, aDateTime);
string formattedNumber = String.Format({0:n}, aNumber);
string formattedCurrency = String.Format({0:c}, aCurrency);
72
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}") %>'
... />
... />
... />
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.
Description
ProductID
int
Name
nvarchar(50)
ProductNumber
nvarchar(25)
MakeFlag
bit
0 = Product is purchased
1 = Product is made in-house
FinishedGoodsFlag
bit
Color
nvarchar(15)
Product color
SafetyStockLevel
smallint
ReorderPoint
smallint
StandardCost
money
ListPrice
money
Selling price
74
Description
Size
nvarchar(5)
Product size
SizeUnitMeasureCode
nchar(3)
WeightUnitMeasureCode
nchar(3)
Weight
decimal(8, 2)
Product weight
DaysToManufacture
int
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
ProductModelID
int
SellStartDate
datetime
SellEndDate
datetime
DiscontinuedDate
datetime
rowguid
uniqueidentifier
ROWGUIDCOL number
that uniquely identifies the
record (for merge
replication)
ModifiedDate
datetime
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
Description
@prmLowerReorderPoint
smallint
@prmUpperReorderPoint
smallint
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
Description
@prmName
nvarchar(50)
@prmProductNumber
nvarchar(25)
@prmMakeFlag
bit
0 = Product is purchased
1 = Product is made in-house
@prmFinishedGoodsFlag
bit
@prmSafetyStockLevel
smallint
@prmReorderPoint
smallint
@prmStandardCost
money
@prmListPrice
money
Selling price
@prmDaysToManufacture
int
@prmSellStartDate
datetime
76
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
Description
@prmProductID
int
@prmReorderPointIncrement
smallint
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
Description
@prmReorderPointIncrement
smallint
Description
ID
int
Increment
smallint
ModifiedDate
datetime
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
Description
@prmIncrement
smallint
77
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
78
79
80
Explicit Transactions
ms-help://MS.SQLCC.v9/MS.SQLSVR.v9.en/udb9/html/052c6ef6-88544d26-b6b5-0d4ccf6d1018.htm
Description
BEGIN TRANSACTION
COMMIT TRANSACTION
ROLLBACK TRANSACTION
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.
81
82
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
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
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
Description
Required
RequiresNew
Suppress
85
86
[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
End If
End Using
End Using
End Using
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();
}
}
}
}
}
}
}
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
Description
SalesPersonID
int
TerritoryID
int
Primary key
Read only
SalesQuota
money
Bonus
CommissionPct
money
smallmoney
Allows nulls
Allows nulls
90
Description
SalesYTD
money
SalesLastYear
money
SalesGrowth
money
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
You will use the following stored procedures to query and modify data in the
Sales.SalesPerson table:
uspGetSingleSalesPerson
uspGetSalesPersons
uspInsertSalesPerson
uspUpdateSalesPerson
uspDeleteSalesPerson
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
Description
@prmSalesPersonID
int
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
Description
@prmSalesPersonID
int
@prmTerritoryID
int
@prmSalesQuota
money
@prmBonus
money
@prmCommissionPct
smallmoney
@prmSalesYTD
money
@prmSalesLastYear
money
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
Description
@prmSalesPersonID
int
@prmTerritoryID
int
@prmSalesQuota
money
@prmBonus
money
@prmCommissionPct
smallmoney
@prmSalesYTD
money
@prmSalesLastYear
money
92
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
Description
@prmSalesPersonID
int
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
Description
CustomerID
int
Name
SalesPersonID
nvarchar(50)
int
Primary key
Read only
Maximum length: 50
Demographics
xml
rowguid
ModifiedDate
uniqueidentifier
datetime
Allows nulls
Allows nulls
Read only
Allows nulls
Read only
Allows nulls
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
Description
@prmCustomerID
int
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
Description
@prmCustomerID
int
@prmName
nvarchar(50)
@prmSalesPersonID
int
@prmDemographics
xml
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
Description
@prmCustomerID
int
@prmName
nvarchar(50)
@prmSalesPersonID
int
@prmDemographics
xml
94
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
Description
@prmCustomerID
int
95
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
96
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");
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};
98
[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.
Example
Comparison
Computation
UnitPrice * Quantity
Aggregation
Sum(Price)
[C#]
workTable.Columns.Add("Total", typeof(Double));
workTable.Columns.Add("SalesTax", typeof(Double), "Total * 0.086");
99
[C#]
DataColumn workCol = workTable.Columns.Add("CustomerID", typeof(Int32));
workCol.AutoIncrement = true;
workCol.AutoIncrementSeed = 200;
workCol.AutoIncrementStep = 3;
100
[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;
[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;
101
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
SetNull
SetDefault
None
102
[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);
Description
Cascade
None
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);
104
[C#]
custDS.Relations.Add("CustOrders",
custDS.Tables["Customers"].Columns["CustID"],
custDS.Tables["Orders"].Columns["CustID"]);
105
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
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"
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
Description
Current
The parameter uses the current value of the column. This is the
default.
Default
Original
Proposed
[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;
109
110
[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.
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)
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
[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();
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
[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.
115
116
[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);
}
[C#]
custDS.RejectChanges();
117
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
118
[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]);
}
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());
}
[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
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);
}
121
[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
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
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.
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
[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"]);
}
}
}
127
Description
Unchanged
Added
Modified
Deleted
Detached
128
[C#]
DataTable addedRows
= custTable.GetChanges(DataRowState.Added);
DataTable modifiedRows = custTable.GetChanges(DataRowState.Modified);
DataTable deletedRows = custTable.GetChanges(DataRowState.Deleted);
[C#]
DataRow custRow = custTable.Rows[0];
string custID = custRow["CustomerID", DataRowVersion.Original].ToString();
129
Description
Current
The current values for the row. This row version does
not exist for rows with a RowState of Deleted.
Default
Original
The original values for the row. This row version does
not exist for rows with a RowState of Added.
Proposed
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
[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();
}
131
Description
ColumnChanged
ColumnChanging
RowChanged
RowChanging
RowDeleted
RowDeleting
132
[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)
+=
+=
+=
+=
new
new
new
new
133
DataColumnChangeEventHandler(OnColumnChanged);
DataColumnChangeEventHandler(OnColumnChanging);
DataRowChangeEventHandler(OnRowChanged);
DataRowChangeEventHandler(OnRowChanging);
134
[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
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");
}
}
136
[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();
137
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
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
AddWithKey
Error
Ignore
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()
140
[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();
141
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
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
[C#]
DataView custDV = new DataView(custDS.Tables["Customers"],
"Country = 'USA'",
"ContactName",
DataViewRowState.CurrentRows);
[C#]
DataView custDV = custDS.Tables["Customers"].DefaultView;
143
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 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.
Description
CurrentRows
Added
Deleted
ModifiedCurrent
ModifiedOriginal
None
No rows.
OriginalRows
Unchanged
144
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;
145
146
[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);
}
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
Description
ProductCategoryID
int
Name
nvarchar(50)
Category description
rowguid
uniqueidentifier
ModifiedDate
datetime
148
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
Description
ProductSubcategoryID
int
ProductCategoryID
int
Name
nvarchar(50)
Subcategory description
rowguid
uniqueidentifier
ModifiedDate
datetime
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
Description
ProductID
int
Name
nvarchar(50)
ProductNumber
nvarchar(25)
MakeFlag
bit
0 = Product is purchased
FinishedGoodsFlag
bit
nvarchar(15)
Product color
SafetyStockLevel
smallint
ReorderPoint
smallint
StandardCost
money
149
(continued)
Column name
Description
ListPrice
money
Selling price
Size
nvarchar(5)
Product size
SizeUnitMeasureCost
nchar(3)
WeightUnitMeasureCost
nchar(3)
Weight
decimal(8, 2)
Product weight
DaysToManufacture
int
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
ProductModelID
int
SellStartDate
datetime
SellEndDate
datetime
DiscontinuedDate
datetime
rowguid
uniqueidentifier
ROWGUIDCOL number
that uniquely identifies the
record (for merge
replication)
ModifiedDate
datetime
150
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
Description
ProductReviewID
int
ProductID
int
ReviewerName
nvarchar(50)
ReviewDate
datetime
EmailAddress
nvarchar(50)
Rating
int
Comments
nvarchar(3850)
Reviewers comments
ModifiedDate
datetime
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
Description
AppointmentID
int
SalesPersonID
int
DateAndTime
datetime
Arrangements
nvarchar(100)
Notes
nvarchar(250)
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
Description
@prmSalesPersonID
int
@prmFromDate
datetime
152
153
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.
154
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.
Refresh the DataTable. Selecting this option refreshes the data in the table
after executing INSERT and UPDATE statements.
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.
View this dataset in the Data Sources window and drag items onto
Microsoft Windows Forms to create data-bound controls.
156
In order to complete this walkthrough, you will need access to the Northwind
sample database (Microsoft SQL Server or Access version).
157
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.
158
159
160
[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");
161
162
[C#]
try
{
customersTableAdapter.Update(this.northwindDataSet.Customers);
}
catch (System.Exception ex)
{
MessageBox.Show("Update failed");
}
163
164
[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.
}
}
165
166
167
168
169
170
Selecting Add New Data Source from the Data menu or from the Data
Sources window.
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.
171
Result in project
Database
Web Service
Object
172
173
174
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").
175
176
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.)
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
Description
ProductCategoryID
int
Name
nvarchar(50)
Category description
rowguid
uniqueidentifier
ModifiedDate
datetime
178
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
Description
ProductSubcategoryID
int
ProductCategoryID
int
Name
nvarchar(50)
Subcategory description
rowguid
uniqueidentifier
ModifiedDate
datetime
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
Description
ProductID
int
Name
nvarchar(50)
ProductNumber
nvarchar(25)
MakeFlag
bit
0 = Product is purchased
FinishedGoodsFlag
bit
nvarchar(15)
Product color
SafetyStockLevel
smallint
ReorderPoint
smallint
StandardCost
money
179
(continued)
Column name
Description
ListPrice
money
Selling price
Size
nvarchar(5)
Product size
SizeUnitMeasureCost
nchar(3)
WeightUnitMeasureCost
nchar(3)
Weight
decimal(8, 2)
Product weight
DaysToManufacture
int
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
ProductModelID
int
SellStartDate
datetime
SellEndDate
datetime
DiscontinuedDate
datetime
rowguid
uniqueidentifier
ROWGUIDCOL number
that uniquely identifies the
record (for merge
replication)
ModifiedDate
datetime
180
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
Description
ProductReviewID
int
ProductID
int
ReviewerName
nvarchar(50)
ReviewDate
datetime
EmailAddress
nvarchar(50)
Rating
int
Comments
nvarchar(3850)
Reviewers comments
ModifiedDate
datetime
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
Description
AppointmentID
int
SalesPersonID
int
DateAndTime
datetime
Arrangements
nvarchar(100)
Notes
nvarchar(250)
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
Description
@prmSalesPersonID
int
@prmFromDate
datetime
182
[C#]
string xmlDS = custDS.GetXml();
Description
IgnoreSchema
WriteSchema
Writes the current contents of the dataset as XML data with the
dataset structure as inline XML Schema.
DiffGram
183
[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();
Description
Element
Attribute
SimpleContent
Hidden
184
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
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
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;
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
Description
Auto
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.
189
(continued)
XmlReadMode
Description
IgnoreSchema
InferSchema
DiffGram
Fragment
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
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
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>
192
[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();
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
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.
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
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"});
XmlWriter class
mshelp://MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.NETDEVFX.v20.en/cpr
ef/html/T_System_Xml_XmlWriter.htm
198
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
NewLineHandling
NewHandling.Replace
NewLineOnAttributes
false
OmitXmlDeclaration
false
200
XmlWriter
mshelp://MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.NETDEVFX.v20.en/cpr
ef/html/T_System_Xml_XmlWriter.htm
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");
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
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();
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();
204
205
MessageQueue class
mshelp://MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.NETDEVFX.v20.en/cpr
ef/html/T_System_Messaging_MessageQueue.htm
206
207
208
209
210
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.
211
212
[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.
}
}
}
213
214
[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
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
217
You are able to specify which features you want supported on the created
XmlReader 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.
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
ValidationType
ValidationType.None
XmlResolver
218
XmlReader Scenarios
The following table describes some common scenarios and which settings on
the XmlReaderSettings class to apply.
Scenario
XmlReaderSettings
ConformanceLevel =
ConformanceLevel.Document
ConformanceLevel =
ConformanceLevel.Fragment
ProhibitDtd = false
ValidationType =
ValidationType.Schema
ValidationType = ValidationType.DTD
ValidationType =
ValidationType.Schema
ValidationFlags &=
~XmlSchemaValidationFlags.IgnoreInl
ineSchema
ValidationType =
ValidationType.Schema
Schemas = XmlSchemaSet to use
219
220
[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);
}
221
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
222
[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
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
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);
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
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.
}
}
226
SmtpClient Class
mshelp://MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.NETDEVFX.v20.en/cpr
ef/html/T_System_Net_Mail_SmtpClient.htm
Description
Attachment
MailAddress
MailMessage
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.
228
[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."
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
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.";
231
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
[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);
[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
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
237
XmlElement.Attributes
XmlDocumentType.Entities
XmlDocumentType.Notations
238
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
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);
}
}
XmlNode.ChildNodes
XmlDocument.GetElementsByTagName
XmlElement.GetElementsByTagName
XmlNode.SelectNodes
240
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);
}
241
242
Basic]
System
System.IO
System.Xml
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
245
Basic]
System
System.IO
System.Xml
246
[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);
}
}
247
Basic]
System
System.IO
System.Xml
248
Basic]
System
System.IO
System.Xml
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
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
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.
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
251
252
[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.
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
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
255
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
InsertAfter
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
256
To remove multiple nodes from the DOM, use the RemoveAll Method to
remove all the children and attributes, if applicable, of the current node.
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:
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.
Modify an entire set of nodes by replacing the nodes with new nodes. This
modification is done by using the XmlNode.InnerXml property.
258
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
CDATASection
Comment
ProcessingInstruction
Text
XmlDeclaration
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.
259
Attribute,
Document,
DocumentFragment,
Entity,
Notation
CDATA
Comment
DocumentType
Element
EntityReference
ProcessingInstruction
Text
260
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
CData
Comment
Document
DocumentFragment
DocumentType
Element
Entity
EntityReference
Notation
ProcessingInstruction
SignificantWhitespace
Text
Whitespace
XmlDeclaration
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
[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);
}
}
263
When you save an XmlDocument, the saved document may be different from
the original in the following ways:
All the white space between attributes is reduced to a single space character.
264
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.