<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/css" href="http://commons.oreilly.com/wiki/skins/common/feed.css?97"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
		<id>http://commons.oreilly.com/wiki/index.php?title=Visual_Basic_2005:_A_Developer's_Notebook/Files%2C_Databases%2C_and_XML&amp;action=history&amp;feed=atom</id>
		<title>Visual Basic 2005: A Developer's Notebook/Files, Databases, and XML - Revision history</title>
		<link rel="self" type="application/atom+xml" href="http://commons.oreilly.com/wiki/index.php?title=Visual_Basic_2005:_A_Developer's_Notebook/Files%2C_Databases%2C_and_XML&amp;action=history&amp;feed=atom"/>
		<link rel="alternate" type="text/html" href="http://commons.oreilly.com/wiki/index.php?title=Visual_Basic_2005:_A_Developer%27s_Notebook/Files%2C_Databases%2C_and_XML&amp;action=history"/>
		<updated>2013-06-19T10:32:46Z</updated>
		<subtitle>Revision history for this page on the wiki</subtitle>
		<generator>MediaWiki 1.11.0</generator>

	<entry>
		<id>http://commons.oreilly.com/wiki/index.php?title=Visual_Basic_2005:_A_Developer%27s_Notebook/Files%2C_Databases%2C_and_XML&amp;diff=9296&amp;oldid=prev</id>
		<title>Docbook2Wiki: Initial conversion from Docbook</title>
		<link rel="alternate" type="text/html" href="http://commons.oreilly.com/wiki/index.php?title=Visual_Basic_2005:_A_Developer%27s_Notebook/Files%2C_Databases%2C_and_XML&amp;diff=9296&amp;oldid=prev"/>
				<updated>2008-03-11T22:57:33Z</updated>
		
		<summary type="html">&lt;p&gt;Initial conversion from Docbook&lt;/p&gt;

			&lt;table style=&quot;background-color: white; color:black;&quot;&gt;
			&lt;col class='diff-marker' /&gt;
			&lt;col class='diff-content' /&gt;
			&lt;col class='diff-marker' /&gt;
			&lt;col class='diff-content' /&gt;
			&lt;tr&gt;
				&lt;td colspan='2' style=&quot;background-color: white; color:black;&quot;&gt;←Older revision&lt;/td&gt;
				&lt;td colspan='2' style=&quot;background-color: white; color:black;&quot;&gt;Revision as of 22:57, 11 March 2008&lt;/td&gt;
			&lt;/tr&gt;
		&lt;/table&gt;</summary>
		<author><name>Docbook2Wiki</name></author>	</entry>

	<entry>
		<id>http://commons.oreilly.com/wiki/index.php?title=Visual_Basic_2005:_A_Developer%27s_Notebook/Files%2C_Databases%2C_and_XML&amp;diff=9182&amp;oldid=prev</id>
		<title>Docbook2Wiki: Initial conversion from Docbook</title>
		<link rel="alternate" type="text/html" href="http://commons.oreilly.com/wiki/index.php?title=Visual_Basic_2005:_A_Developer%27s_Notebook/Files%2C_Databases%2C_and_XML&amp;diff=9182&amp;oldid=prev"/>
				<updated>2008-03-11T22:31:16Z</updated>
		
		<summary type="html">&lt;p&gt;Initial conversion from Docbook&lt;/p&gt;

			&lt;table style=&quot;background-color: white; color:black;&quot;&gt;
			&lt;col class='diff-marker' /&gt;
			&lt;col class='diff-content' /&gt;
			&lt;col class='diff-marker' /&gt;
			&lt;col class='diff-content' /&gt;
			&lt;tr&gt;
				&lt;td colspan='2' style=&quot;background-color: white; color:black;&quot;&gt;←Older revision&lt;/td&gt;
				&lt;td colspan='2' style=&quot;background-color: white; color:black;&quot;&gt;Revision as of 22:31, 11 March 2008&lt;/td&gt;
			&lt;/tr&gt;
		&lt;/table&gt;</summary>
		<author><name>Docbook2Wiki</name></author>	</entry>

	<entry>
		<id>http://commons.oreilly.com/wiki/index.php?title=Visual_Basic_2005:_A_Developer%27s_Notebook/Files%2C_Databases%2C_and_XML&amp;diff=9117&amp;oldid=prev</id>
		<title>Docbook2Wiki: Initial conversion from Docbook</title>
		<link rel="alternate" type="text/html" href="http://commons.oreilly.com/wiki/index.php?title=Visual_Basic_2005:_A_Developer%27s_Notebook/Files%2C_Databases%2C_and_XML&amp;diff=9117&amp;oldid=prev"/>
				<updated>2008-03-11T22:24:43Z</updated>
		
		<summary type="html">&lt;p&gt;Initial conversion from Docbook&lt;/p&gt;

			&lt;table style=&quot;background-color: white; color:black;&quot;&gt;
			&lt;col class='diff-marker' /&gt;
			&lt;col class='diff-content' /&gt;
			&lt;col class='diff-marker' /&gt;
			&lt;col class='diff-content' /&gt;
			&lt;tr&gt;
				&lt;td colspan='2' style=&quot;background-color: white; color:black;&quot;&gt;←Older revision&lt;/td&gt;
				&lt;td colspan='2' style=&quot;background-color: white; color:black;&quot;&gt;Revision as of 22:24, 11 March 2008&lt;/td&gt;
			&lt;/tr&gt;
		&lt;/table&gt;</summary>
		<author><name>Docbook2Wiki</name></author>	</entry>

	<entry>
		<id>http://commons.oreilly.com/wiki/index.php?title=Visual_Basic_2005:_A_Developer%27s_Notebook/Files%2C_Databases%2C_and_XML&amp;diff=8998&amp;oldid=prev</id>
		<title>Docbook2Wiki: Initial conversion from Docbook</title>
		<link rel="alternate" type="text/html" href="http://commons.oreilly.com/wiki/index.php?title=Visual_Basic_2005:_A_Developer%27s_Notebook/Files%2C_Databases%2C_and_XML&amp;diff=8998&amp;oldid=prev"/>
				<updated>2008-03-11T21:52:04Z</updated>
		
		<summary type="html">&lt;p&gt;Initial conversion from Docbook&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;{{Visual Basic 2005: A Developer's Notebook/TOC}}&lt;br /&gt;
.NET 1.0 revolutionized Visual Basic data access with a whole new object model for interacting with files, connecting to databases, and manipulating XML. In .NET 2.0, the revolution continues with a slew of minor improvements, some new features, and a tool for generating data access code automatically, all designed to make life better for the VB programmer.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;tip&amp;quot;&amp;gt;&lt;br /&gt;
'''Tip'''&lt;br /&gt;
&lt;br /&gt;
What you ''won't'' learn about in this chapter are the new .NET Framework features designed for SQL Server 2005. These include using .NET code to program user-defined data types and stored procedures, SQL Server 2005 notifications, and multiple active recordsets (MARS). For more information about these SQL Server 2005 features, please pay a visit to the MSDN SQL Server 2005 Developer Center at ''http://msdn.microsoft.com/SQL/2005''.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Get Drive Information ==&lt;br /&gt;
&lt;br /&gt;
.NET includes handy &amp;lt;tt&amp;gt;DirectoryInfo&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;FileInfo&amp;lt;/tt&amp;gt; classes for gathering information about files and directories. However, in .NET 1.x there wasn't any way to get a list of all the drives on your computer without using unmanaged calls to the Windows API. Thankfully, the &amp;lt;tt&amp;gt;DriveInfo&amp;lt;/tt&amp;gt; class finally debuts in .NET 2.0.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;note&amp;quot;&amp;gt;&lt;br /&gt;
'''Note'''&lt;br /&gt;
&lt;br /&gt;
''The DriveInfo class lets you easily retrieve information about the drives on your computer''.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;tip&amp;quot;&amp;gt;&lt;br /&gt;
'''Tip'''&lt;br /&gt;
&lt;br /&gt;
Before going any further, start by importing the &amp;lt;tt&amp;gt;System.IO&amp;lt;/tt&amp;gt; namespace. The file access classes in .NET 2.0 (&amp;lt;tt&amp;gt;FileInfo&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;DirInfo&amp;lt;/tt&amp;gt;, and &amp;lt;tt&amp;gt;DriveInfo&amp;lt;/tt&amp;gt;) all exist there. Whether you access them directly or through the &amp;lt;tt&amp;gt;My&amp;lt;/tt&amp;gt; object, you'll need to import this namespace.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== How do I do that? ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;My.Computer.FileSystem&amp;lt;/tt&amp;gt; provides a quick way to get a list of all the drives on the current computer. All you need to do is loop through the &amp;lt;tt&amp;gt;Drives&amp;lt;/tt&amp;gt; collection, which exposes a collection of &amp;lt;tt&amp;gt;System.IO.DriveInfo&amp;lt;/tt&amp;gt; objects.&lt;br /&gt;
&lt;br /&gt;
For example, to see all the drive letters on the current computer, enter the following code:&lt;br /&gt;
&lt;br /&gt;
 ' Display a list of drives.&lt;br /&gt;
 For Each Drive As DriveInfo In My.Computer.FileSystem.Drives&lt;br /&gt;
     Console.WriteLine(Drive.Name)&lt;br /&gt;
 Next&lt;br /&gt;
&lt;br /&gt;
This writes a list of drive letter names (&amp;quot;A:\&amp;quot;, &amp;quot;C:\&amp;quot;, &amp;quot;D:\&amp;quot;, and so on). You can also create a &amp;lt;tt&amp;gt;DriveInfo&amp;lt;/tt&amp;gt; object for a specific directory by using the &amp;lt;tt&amp;gt;My.Computer.FileSystem.GetDriveInfo( )&amp;lt;/tt&amp;gt; method, and specifying the letter of the drive you want to examine as a string. For example, try the following code to take a closer look at drive C:&lt;br /&gt;
&lt;br /&gt;
 Console.WriteLine(&amp;quot;The label for drive C:\ is &amp;quot; &amp;amp; _&lt;br /&gt;
   My.Computer.FileSystem.GetDriveInfo(&amp;quot;C&amp;quot;).VolumeLabel&lt;br /&gt;
&lt;br /&gt;
This example displays the volume label that's set for the drive. You can also examine other properties of the &amp;lt;tt&amp;gt;DriveInfo&amp;lt;/tt&amp;gt; object to get more information (such as the &amp;lt;tt&amp;gt;DriveType&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;TotalFreeSpace&amp;lt;/tt&amp;gt;, and &amp;lt;tt&amp;gt;TotalSize&amp;lt;/tt&amp;gt;). But keep in mind that you can't retrieve this information for a removable drive if there's no media present (for example, if the CD or diskette isn't in the drive). To guard against this possibility, check to make sure the &amp;lt;tt&amp;gt;DriveType&amp;lt;/tt&amp;gt; doesn't include &amp;lt;tt&amp;gt;DriveType.Fixed&amp;lt;/tt&amp;gt; before trying to get more detail.&lt;br /&gt;
&lt;br /&gt;
[[Visual Basic 2005: A Developer's Notebook/Files, Databases, and XML#vbadn-CHP-5-EX-1|Example 5-1]] puts these concepts together with a complete, simple console application that displays information about all the drives on your computer.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;vbadn-CHP-5-EX-1&amp;quot;&amp;gt;&lt;br /&gt;
'''Example 5-1. Displaying information about all the drives on a computer'''&lt;br /&gt;
&lt;br /&gt;
 Imports System.IO&lt;br /&gt;
     &lt;br /&gt;
 Module DriveInfoTest&lt;br /&gt;
     &lt;br /&gt;
     Public Sub Main( )&lt;br /&gt;
         Console.WriteLine(&amp;quot;Drives on this computer: &amp;quot;)&lt;br /&gt;
         For Each Drive As DriveInfo In My.Computer.FileSystem.Drives&lt;br /&gt;
             ' Display drive information.&lt;br /&gt;
             Console.WriteLine(Drive.Name)&lt;br /&gt;
     &lt;br /&gt;
             Console.WriteLine(vbTab &amp;amp; &amp;quot;Type: &amp;quot; &amp;amp; Drive.DriveType.ToString( ))&lt;br /&gt;
     &lt;br /&gt;
             If (Drive.DriveType And DriveType.Fixed) = DriveType.Fixed Then&lt;br /&gt;
                 Console.WriteLine(vbTab &amp;amp; &amp;quot;Format: &amp;quot; &amp;amp; _&lt;br /&gt;
                   Drive.DriveFormat.ToString( ))&lt;br /&gt;
                 Console.WriteLine(vbTab &amp;amp; &amp;quot;Label: &amp;quot; &amp;amp; Drive.VolumeLabel)&lt;br /&gt;
                 Console.WriteLine(vbTab &amp;amp; &amp;quot;Total Size: &amp;quot; &amp;amp; Drive.TotalSize)&lt;br /&gt;
                 Console.WriteLine(vbTab &amp;amp; &amp;quot;Free Space: &amp;quot; &amp;amp; Drive.TotalFreeSpace)&lt;br /&gt;
             End If&lt;br /&gt;
             Console.WriteLine( )&lt;br /&gt;
         Next&lt;br /&gt;
     End Sub&lt;br /&gt;
     &lt;br /&gt;
 End Module&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When you run this code, you'll see the following output:&lt;br /&gt;
&lt;br /&gt;
 Drives on this computer:&lt;br /&gt;
 A:\&lt;br /&gt;
         Type: Removable&lt;br /&gt;
     &lt;br /&gt;
 C:\&lt;br /&gt;
         Type: Fixed&lt;br /&gt;
         Format: NTFS&lt;br /&gt;
         Label: Applications&lt;br /&gt;
         Total Size: 15726702592&lt;br /&gt;
         Free Space: 2788483072&lt;br /&gt;
     &lt;br /&gt;
 D:\&lt;br /&gt;
 ...&lt;br /&gt;
&lt;br /&gt;
=== What about... ===&lt;br /&gt;
&lt;br /&gt;
...getting information about the rest of the filesystem? The .NET Framework has always made it easy to get directory and file information using &amp;lt;tt&amp;gt;DirectoryInfo&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;FileInfo&amp;lt;/tt&amp;gt; objects. Once you have instantiated a &amp;lt;tt&amp;gt;DriveInfo&amp;lt;/tt&amp;gt; object, you can use its &amp;lt;tt&amp;gt;RootDirectory&amp;lt;/tt&amp;gt; property to get a &amp;lt;tt&amp;gt;DirectoryInfo&amp;lt;/tt&amp;gt; object that wraps the root directory (e.g., ''C:\''). You can then use methods like &amp;lt;tt&amp;gt;DirectoryInfo.GetFiles( )&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;DirectoryInfo.GetDirectories( )&amp;lt;/tt&amp;gt; to retrieve the files and subdirectories contained in the root directory.&lt;br /&gt;
&lt;br /&gt;
=== Where can I learn more? ===&lt;br /&gt;
&lt;br /&gt;
For more information about all the properties of the filesystem information classes, look for the &amp;quot;DriveInfo,&amp;quot; &amp;quot;DirectoryInfo,&amp;quot; and &amp;quot;FileInfo&amp;quot; index entries in the MSDN class library reference. You can also refer to other labs in this chapter, which show new shortcuts available with the &amp;lt;tt&amp;gt;My.Computer.FileSystem&amp;lt;/tt&amp;gt; object. These include:&lt;br /&gt;
&lt;br /&gt;
* &amp;quot;Get File and Directory Information,&amp;quot; which shows how you can quickly get information about a specific file or directory without directly creating a &amp;lt;tt&amp;gt;FileInfo&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;DirectoryInfo&amp;lt;/tt&amp;gt; object.&lt;br /&gt;
* &amp;quot;Copy, Move, and Delete Files,&amp;quot; which shows how you can easily shuffle files and directories from one place to another.&lt;br /&gt;
* &amp;quot;Read and Write Files,&amp;quot; which shows the quickest way to extract text content from a file or write to a file.&lt;br /&gt;
&lt;br /&gt;
== Get File and Directory Information ==&lt;br /&gt;
&lt;br /&gt;
In VB 2005, you can access all the file and directory information you need from a single starting point: the new &amp;lt;tt&amp;gt;My.Computer.FileSystem&amp;lt;/tt&amp;gt; object.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;note&amp;quot;&amp;gt;&lt;br /&gt;
'''Note'''&lt;br /&gt;
&lt;br /&gt;
''The new My.Computer. FileSystem object lets you get file and directory information with a bare minimum of code''.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== How do I do that? ===&lt;br /&gt;
&lt;br /&gt;
Here are four key methods of &amp;lt;tt&amp;gt;My.Computer.FileSystem&amp;lt;/tt&amp;gt; that you can use to get file and directory information. Every method has the same signature—it takes a single string parameter whose value is the complete path of the file or directory that's the subject of your query. The methods are:&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;tt&amp;gt;FileExists( )&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Returns &amp;lt;tt&amp;gt;True&amp;lt;/tt&amp;gt; if the file exists.&lt;br /&gt;
;&amp;lt;tt&amp;gt;DirectoryExists( )&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Returns &amp;lt;tt&amp;gt;True&amp;lt;/tt&amp;gt; if the directory exists.&lt;br /&gt;
;&amp;lt;tt&amp;gt;GetFileInfo( )&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Returns a &amp;lt;tt&amp;gt;FileInfo&amp;lt;/tt&amp;gt; object. You can examine its various properties to get information such as file size, attributes, and so on.&lt;br /&gt;
;&amp;lt;tt&amp;gt;GetDirectoryInfo( )&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Returns a &amp;lt;tt&amp;gt;DirectoryInfo&amp;lt;/tt&amp;gt; object. You can examine its various properties to get information such as directory size, attributes, and so on.&lt;br /&gt;
&lt;br /&gt;
The code snippet shown in [[Visual Basic 2005: A Developer's Notebook/Files, Databases, and XML#vbadn-CHP-5-EX-2|Example 5-2]] first determines whether a file exists and then displays some information when it does.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;vbadn-CHP-5-EX-2&amp;quot;&amp;gt;&lt;br /&gt;
'''Example 5-2. Retrieving information about a specific file'''&lt;br /&gt;
&lt;br /&gt;
 Imports System.IO&lt;br /&gt;
     &lt;br /&gt;
 Module FileInfoTest&lt;br /&gt;
     &lt;br /&gt;
     Public Sub Main( )&lt;br /&gt;
         ' Get a file in a &amp;quot;special directory.&amp;quot;&lt;br /&gt;
         Dim Info As FileInfo&lt;br /&gt;
         Info = My.Computer.FileSystem.GetFileInfo(&amp;quot;c:\Windows\explorer.exe&amp;quot;)&lt;br /&gt;
     &lt;br /&gt;
         ' Show the access/update times.&lt;br /&gt;
         Console.WriteLine(&amp;quot;Created: &amp;quot; &amp;amp; Info.CreationTime)&lt;br /&gt;
         Console.WriteLine(&amp;quot;Last Modified: &amp;quot; &amp;amp; Info.LastWriteTime)&lt;br /&gt;
         Console.WriteLine(&amp;quot;Last Accessed: &amp;quot; &amp;amp; Info.LastAccessTime)&lt;br /&gt;
     &lt;br /&gt;
         ' Check if the file is read-only. When testing file attributes,&lt;br /&gt;
         ' you need to use bitwise arithmetic, because the FileAttributes&lt;br /&gt;
         ' collection usually contains more than one attribute at a time.&lt;br /&gt;
         Dim ReadOnlyFile As Boolean&lt;br /&gt;
         ReadOnlyFile = Info.Attributes And FileAttributes.ReadOnly&lt;br /&gt;
         Console.WriteLine(&amp;quot;Read-Only: &amp;quot; &amp;amp; ReadOnlyFile)&lt;br /&gt;
     &lt;br /&gt;
         ' Show the size.&lt;br /&gt;
         Console.WriteLine(&amp;quot;Size (bytes): &amp;quot; &amp;amp; Info.Length)&lt;br /&gt;
     End Sub&lt;br /&gt;
     &lt;br /&gt;
 End Module&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here's the type of output you'll see:&lt;br /&gt;
&lt;br /&gt;
 Created: 3/30/2004 7:35:17 PM&lt;br /&gt;
 Last Modified: 8/29/2002 4:41:24 AM&lt;br /&gt;
 Last Accessed: 4/28/2004 10:59:38 AM&lt;br /&gt;
 Read-Only: False&lt;br /&gt;
 Size (bytes): 104032&lt;br /&gt;
 Version: 6.0.1106&lt;br /&gt;
&lt;br /&gt;
=== What about... ===&lt;br /&gt;
&lt;br /&gt;
...searching for directories and files? The &amp;lt;tt&amp;gt;My.Computer.FileSystem&amp;lt;/tt&amp;gt; object also provides a &amp;lt;tt&amp;gt;GetDirectories( )&amp;lt;/tt&amp;gt; method to retrieve the names of all the subdirectories in a directory and a &amp;lt;tt&amp;gt;GetFiles()&amp;lt;/tt&amp;gt; method to retrieve the names of all files in a given directory.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;note&amp;quot;&amp;gt;&lt;br /&gt;
'''Note'''&lt;br /&gt;
&lt;br /&gt;
''In early beta versions, Visual Basic included new FolderProperties and FileProperties classes that duplicated the DirectoryInfo and FileInfo classes. Fortunately, Microsoft decided not to reinvent the wheel, and went back to the . NET 1.x standards''.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Both methods offer additional flexibility via an overloaded version that accepts additional parameters. You can specify an array with one or more filter strings (for example, use &amp;lt;tt&amp;gt;*.doc&amp;lt;/tt&amp;gt; to find all the files with the extension ''.doc''). You can also supply a Boolean &amp;lt;tt&amp;gt;includeSubFolders&amp;lt;/tt&amp;gt; parameter that, if &amp;lt;tt&amp;gt;True&amp;lt;/tt&amp;gt;, searches for matching files or directories in every contained subdirectory.&lt;br /&gt;
&lt;br /&gt;
Here's an example of an advanced search that finds all the ''.exe'' files in the ''c:\windows'' directory:&lt;br /&gt;
&lt;br /&gt;
 ' Get all the EXE files in the Windows directory.&lt;br /&gt;
 For Each File As String In My.Computer.FileSystem.GetFiles( _&lt;br /&gt;
   &amp;quot;c:\windows\&amp;quot;, True, &amp;quot;*.exe&amp;quot;)&lt;br /&gt;
     Info = My.Computer.FileSystem.GetFileInfo(File)&lt;br /&gt;
     Console.WriteLine(Info.Name &amp;amp; &amp;quot; in &amp;quot; &amp;amp; Info.Directory.Name)&lt;br /&gt;
 Next&lt;br /&gt;
&lt;br /&gt;
Note that the &amp;lt;tt&amp;gt;GetFiles( )&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;GetDirectories( )&amp;lt;/tt&amp;gt; methods just return strings. If you want more information, you need to create a &amp;lt;tt&amp;gt;FileInfo&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;DirectoryInfo&amp;lt;/tt&amp;gt; object for the file or directory, as shown above.&lt;br /&gt;
&lt;br /&gt;
There is one caveat: when you perform a search with the &amp;lt;tt&amp;gt;GetFiles( )&amp;lt;/tt&amp;gt; method, the matching file list is first created and ''then'' returned to your code. In other words, if you're performing a time-consuming search, you won't receive a single result until the entire search is finished.&lt;br /&gt;
&lt;br /&gt;
== Copy, Move, and Delete Files ==&lt;br /&gt;
&lt;br /&gt;
In addition to helping you gather information about the directories and files on your system, the &amp;lt;tt&amp;gt;My.Computer.FileSystem&amp;lt;/tt&amp;gt; object also gives you quick access to a number of methods for performing common file-management tasks, such as copying, moving, and deleting files.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;note&amp;quot;&amp;gt;&lt;br /&gt;
'''Note'''&lt;br /&gt;
&lt;br /&gt;
''In VB 2005, you can perform common file-management tasks with a single line of code''.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== How do I do that? ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;My.Computer.FileSystem&amp;lt;/tt&amp;gt; object provides several self-contained methods for performing common file-management operations. They are:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;tt&amp;gt;CopyFile( )&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;CopyDirectory( )&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;MoveFile( )&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;MoveDirectory( )&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;RenameFile( )&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;RenameDirectory()&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;DeleteFile( )&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;DeleteDirectory()&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The way you use each of these methods is fairly straightforward. You supply two parameters: a source path and, if required, a target filename or path. For example, you can rename a file with this line of code:&lt;br /&gt;
&lt;br /&gt;
 My.Computer.FileSystem.RenameFile(&amp;quot;c:\myfile.txt&amp;quot;, &amp;quot;newname.txt&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
These methods are also available in overloaded versions that give you additional features. We'll take a look at those next.&lt;br /&gt;
&lt;br /&gt;
The move and copy methods of &amp;lt;tt&amp;gt;FileSystem&amp;lt;/tt&amp;gt; are available in a variety of overloaded versions. If you need to overwrite an existing file or directory, be sure to use a version that includes the Boolean parameter &amp;lt;tt&amp;gt;overwrite&amp;lt;/tt&amp;gt; and set it to &amp;lt;tt&amp;gt;True&amp;lt;/tt&amp;gt;. Otherwise, you'll receive an exception and the operation won't be completed. Here's an example of one such option:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;note&amp;quot;&amp;gt;&lt;br /&gt;
'''Note'''&lt;br /&gt;
&lt;br /&gt;
''In some beta versions, the user interface for moving or deleting a file doesn't appear, even when you choose to see it. However, the underlying task (moving or deleting a file) is always performed correctly''.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 My.Computer.FileSystem.CopyDirectory(&amp;quot;c:\MyFiles&amp;quot;, _&lt;br /&gt;
   &amp;quot;c:\CopyOfMyFiles&amp;quot;, True)&lt;br /&gt;
&lt;br /&gt;
Interestingly, among the copying and deleting methods are versions that accept the &amp;lt;tt&amp;gt;showUI&amp;lt;/tt&amp;gt; Boolean parameter. If that parameter is set to &amp;lt;tt&amp;gt;True&amp;lt;/tt&amp;gt;, the operation works exactly as if a user had initiated the delete or copy operation in Windows Explorer: dialog boxes appear asking the user to confirm the request to overwrite or delete files, and a progress indicator appears with a Cancel button when a file copy or delete operation is in progress (unless the operation completes very quickly). You can even specify what should happen when the user clicks Cancel (either an exception is thrown or nothing at all happens) using the &amp;lt;tt&amp;gt;onUserCancel&amp;lt;/tt&amp;gt; parameter.&lt;br /&gt;
&lt;br /&gt;
[[Visual Basic 2005: A Developer's Notebook/Files, Databases, and XML#vbadn-CHP-5-EX-3|Example 5-3]] provides a complete console application that lets you test this behavior.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;vbadn-CHP-5-EX-3&amp;quot;&amp;gt;&lt;br /&gt;
'''Example 5-3. Moving and deleting files with Windows UI'''&lt;br /&gt;
&lt;br /&gt;
 Imports System.IO&lt;br /&gt;
     &lt;br /&gt;
 Module FileManagement&lt;br /&gt;
     &lt;br /&gt;
     Public Sub Main( )&lt;br /&gt;
         ' Create a large test file (100 MB).&lt;br /&gt;
         Dim TestFile As String = &amp;quot;c:\test.bin&amp;quot;&lt;br /&gt;
         Console.WriteLine(&amp;quot;Creating file...&amp;quot;)&lt;br /&gt;
         Dim fs As FileStream = File.OpenWrite(TestFile)&lt;br /&gt;
         For i As Integer = 1 To 100000000&lt;br /&gt;
             fs.WriteByte(0)&lt;br /&gt;
         Next&lt;br /&gt;
         fs.Close( )&lt;br /&gt;
     &lt;br /&gt;
         ' Create the target directory.&lt;br /&gt;
         Console.WriteLine(&amp;quot;Creating directory...&amp;quot;)&lt;br /&gt;
         Dim TargetDir As String = &amp;quot;c:\TestDir&amp;quot;&lt;br /&gt;
         My.Computer.FileSystem.CreateDirectory(TargetDir)&lt;br /&gt;
         Dim TargetFile As String = Path.Combine(TargetDir, &amp;quot;test.bin&amp;quot;)&lt;br /&gt;
     &lt;br /&gt;
         Console.WriteLine(&amp;quot;Moving file...&amp;quot;)&lt;br /&gt;
         ' Try moving the file. Set the following parameters:&lt;br /&gt;
         '    showUI = UIOption.AllDialogs&lt;br /&gt;
         ' (Show all the Windows UI, not just error messages.)&lt;br /&gt;
         '    onUserCancel = UICancelOption.ThrowException&lt;br /&gt;
         ' (Generate an error if the user clicks Cancel.)&lt;br /&gt;
         Try&lt;br /&gt;
             My.Computer.FileSystem.MoveFile(TestFile, TargetFile, _&lt;br /&gt;
               UIOption.AllDialogs, UICancelOption.ThrowException)&lt;br /&gt;
             Console.WriteLine(&amp;quot;File moved.&amp;quot;)&lt;br /&gt;
         Catch Err As Exception&lt;br /&gt;
             Console.WriteLine(&amp;quot;You canceled the operation.&amp;quot;)&lt;br /&gt;
     &lt;br /&gt;
             ' Remove the original file.&lt;br /&gt;
             My.Computer.FileSystem.DeleteFile(TestFile)&lt;br /&gt;
         End Try&lt;br /&gt;
     &lt;br /&gt;
         Console.WriteLine(&amp;quot;Press Enter to continue.&amp;quot;)&lt;br /&gt;
         Console.ReadLine( )&lt;br /&gt;
     &lt;br /&gt;
         ' Delete the target directory. Set the following parameters:&lt;br /&gt;
         '    showUI = UIOption.AllDialogs&lt;br /&gt;
         ' (Show the confirmation and Windows UI dialog box.)&lt;br /&gt;
         '    sendToRecycleBin = RecycleOption.SendToRecycleBin&lt;br /&gt;
         ' (Delete the file permanently.&lt;br /&gt;
         '    onUserCancel = UICancelOption.DoNothing&lt;br /&gt;
         ' (Allow the user to cancel this operation.)&lt;br /&gt;
         My.Computer.FileSystem.DeleteDirectory(TargetDir, _&lt;br /&gt;
           UIOption.AllDialogs, RecycleOption.SendToRecycleBin, _&lt;br /&gt;
           UICancelOption.DoNothing)&lt;br /&gt;
     &lt;br /&gt;
         Console.WriteLine(&amp;quot;Cleanup finished.&amp;quot;)&lt;br /&gt;
     End Sub&lt;br /&gt;
     &lt;br /&gt;
 End Module&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As shown in this example, the &amp;lt;tt&amp;gt;DeleteFile( )&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;DeleteDirectory( )&amp;lt;/tt&amp;gt; methods have one additional frill available. By default, when you delete a file, it bypasses the Windows recycle bin. However, you can use an overloaded version of &amp;lt;tt&amp;gt;DeleteFile( )&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;DeleteDirectory( )&amp;lt;/tt&amp;gt; that accepts a &amp;lt;tt&amp;gt;sendToRecycleBin&amp;lt;/tt&amp;gt; parameter. Set this to &amp;lt;tt&amp;gt;True&amp;lt;/tt&amp;gt; to keep your file around as a safeguard.&lt;br /&gt;
&lt;br /&gt;
=== What about... ===&lt;br /&gt;
&lt;br /&gt;
...file operations that use special folders? The new &amp;lt;tt&amp;gt;My.Computer.FileSystem&amp;lt;/tt&amp;gt; object allows you to retrieve references to many system-defined folders through the &amp;lt;tt&amp;gt;SpecialDirectories&amp;lt;/tt&amp;gt; class. For example, you can quickly retrieve the path for temporary files, user documents, and the desktop. Here's an example:&lt;br /&gt;
&lt;br /&gt;
 Dim Desktop As String = My.Computer.FileSystem.SpecialDirectories.Desktop&lt;br /&gt;
 Console.WriteLine(&amp;quot;Your desktop is at: &amp;quot; &amp;amp; Desktop)&lt;br /&gt;
     &lt;br /&gt;
 Console.Write(&amp;quot;It's size is: &amp;quot;)&lt;br /&gt;
 Console.Write(My.Computer.FileSystem.GetDirectoryInfo(Desktop).Size)&lt;br /&gt;
 Console.WriteLine(&amp;quot; bytes&amp;quot;)&lt;br /&gt;
 Console.Write(&amp;quot;It contains: &amp;quot;)&lt;br /&gt;
 Console.Write(My.Computer.FileSystem.GetFiles(Desktop).Count)&lt;br /&gt;
 Console.WriteLine(&amp;quot; files&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;SpecialDirectories&amp;lt;/tt&amp;gt; class includes all the following properties, each of which returns a string with the corresponding fully qualified path:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;AllUsersApplicationData&amp;lt;/tt&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;tt&amp;gt;CurrentUserApplicationData&amp;lt;/tt&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;tt&amp;gt;Desktop&amp;lt;/tt&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;tt&amp;gt;MyDocuments&amp;lt;/tt&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;tt&amp;gt;MyMusic&amp;lt;/tt&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;tt&amp;gt;MyPictures&amp;lt;/tt&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;tt&amp;gt;Programs&amp;lt;/tt&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;tt&amp;gt;Temp&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Read and Write Files ==&lt;br /&gt;
&lt;br /&gt;
If you need to work with text files or raw binary data, VB 2005 provides a new solution that bypasses the lower-level classes of &amp;lt;tt&amp;gt;System.IO&amp;lt;/tt&amp;gt; for small files. Now you can read and write text in a single atomic operation using the &amp;lt;tt&amp;gt;My.Computer.FileSystem&amp;lt;/tt&amp;gt; object. Best of all, you no longer need to create streams, track your position, or clean up afterward.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;note&amp;quot;&amp;gt;&lt;br /&gt;
'''Note'''&lt;br /&gt;
&lt;br /&gt;
''At last, a way to read and write files without the complexities of streams and stream readers''.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== How do I do that? ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;My.Computer.FileIO&amp;lt;/tt&amp;gt; object provides the absolute quickest way to read or write the contents of a file. Its secret lies in a few self-contained methods. These include:&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;tt&amp;gt;ReadAllText( )&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Reads the content of a text file and returns it as a single string.&lt;br /&gt;
;&amp;lt;tt&amp;gt;ReadAllBytes( )&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Reads the content of any file and returns it as an array of bytes.&lt;br /&gt;
;&amp;lt;tt&amp;gt;WriteAllText( )&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Writes text as a string to a file in one atomic operation. You can either add to an existing file or create a new file, depending on whether you supply &amp;lt;tt&amp;gt;True&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;False&amp;lt;/tt&amp;gt; for the Boolean &amp;lt;tt&amp;gt;append&amp;lt;/tt&amp;gt; parameter.&lt;br /&gt;
;&amp;lt;tt&amp;gt;WriteAllBytes( )&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Writes a byte array to a file in a single operation. You can either add to an existing file or create a new file, depending on whether you supply &amp;lt;tt&amp;gt;True&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;False&amp;lt;/tt&amp;gt; for the Boolean &amp;lt;tt&amp;gt;append&amp;lt;/tt&amp;gt; parameter.&lt;br /&gt;
&lt;br /&gt;
[[Visual Basic 2005: A Developer's Notebook/Files, Databases, and XML#vbadn-CHP-5-EX-4|Example 5-4]] creates a simple text file and then reads it back into memory.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;vbadn-CHP-5-EX-4&amp;quot;&amp;gt;&lt;br /&gt;
'''Example 5-4. Write a file in one step and read a file in one step'''&lt;br /&gt;
&lt;br /&gt;
 Imports System.IO&lt;br /&gt;
     &lt;br /&gt;
 Module FileReadAndWrite&lt;br /&gt;
     &lt;br /&gt;
     Public Sub Main( )&lt;br /&gt;
         Dim Text As String = &amp;quot;This is line 1&amp;quot; &amp;amp; _&lt;br /&gt;
           vbNewLine &amp;amp; &amp;quot;This is line 2&amp;quot; &amp;amp; _&lt;br /&gt;
           vbNewLine &amp;amp; &amp;quot;This is line 3&amp;quot; &amp;amp; _&lt;br /&gt;
           vbNewLine &amp;amp; &amp;quot;This is line 4&amp;quot;&lt;br /&gt;
     &lt;br /&gt;
         ' Write the file.&lt;br /&gt;
         My.Computer.FileSystem.WriteAllText(&amp;quot;c:\test.txt&amp;quot;, Text, False)&lt;br /&gt;
     &lt;br /&gt;
         ' Read the file.&lt;br /&gt;
         Console.WriteLine(My.Computer.FileSystem.ReadAllText(&amp;quot;c:\test.txt&amp;quot;))&lt;br /&gt;
     End Sub&lt;br /&gt;
     &lt;br /&gt;
 End Module&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== What about... ===&lt;br /&gt;
&lt;br /&gt;
...the limitations of this approach? The methods that you'll find in the &amp;lt;tt&amp;gt;My.Computer.FileSystem&amp;lt;/tt&amp;gt; object are unmatched for sheer convenience, but they aren't always appropriate. Here are some reasons you might be better off using the lower-level classes of the &amp;lt;tt&amp;gt;System.IO&amp;lt;/tt&amp;gt; namespace:&lt;br /&gt;
&lt;br /&gt;
* You have an extremely large file, and you want to read and process its contents one piece at a time, rather than load the entire file into memory at once. This is a reasonable approach it you're dealing with a long document, for example.&lt;br /&gt;
* You want to use other data types, like numbers or dates. In order to use the &amp;lt;tt&amp;gt;My.Computer.FileIO&amp;lt;/tt&amp;gt; methods to handle numeric data, you'll need to first convert the numbers into strings or byte arrays manually using other .NET classes. On the other hand, if you use a &amp;lt;tt&amp;gt;FileStream&amp;lt;/tt&amp;gt; instead, you simply need to wrap it with a &amp;lt;tt&amp;gt;BinaryReader&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;BinaryWriter&amp;lt;/tt&amp;gt;.&lt;br /&gt;
* You want to use other stream-based .NET features, such as compression (explained in the next lab, &amp;quot;Compress and Decompress Data&amp;quot;), object serialization, or encryption.&lt;br /&gt;
&lt;br /&gt;
The core .NET classes for reading and writing files are found in the &amp;lt;tt&amp;gt;System.IO&amp;lt;/tt&amp;gt; namespace and haven't changed in .NET 2.0. The most useful of these are &amp;lt;tt&amp;gt;FileStream&amp;lt;/tt&amp;gt; (allows you to open a file directly for reading or writing), &amp;lt;tt&amp;gt;StreamReader&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;StreamWriter&amp;lt;/tt&amp;gt; (used for reading and writing text, one line at a time), and &amp;lt;tt&amp;gt;BinaryReader&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;BinaryWriter&amp;lt;/tt&amp;gt; (used for converting basic .NET data types to binary data and back). Look these classes up in the MSN Help for the traditional file-access techniques. Also, in the next lab, you'll see a more advanced example that uses &amp;lt;tt&amp;gt;FileStream&amp;lt;/tt&amp;gt; to encrypt data in a file.&lt;br /&gt;
&lt;br /&gt;
== Compress and Decompress Data ==&lt;br /&gt;
&lt;br /&gt;
Even with the ever-increasing capacity of hard drives and the falling price of computer memory, it still pays to save space. In .NET 2.0, a new &amp;lt;tt&amp;gt;System.IO.Compression&amp;lt;/tt&amp;gt; namespace makes it easy for a VB 2005 programmer to compress data as she writes it to a stream, and decompress data as she reads it from a stream.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;note&amp;quot;&amp;gt;&lt;br /&gt;
'''Note'''&lt;br /&gt;
&lt;br /&gt;
''Need to save space before you store data in a file or database? . NET 2.0 makes compression and decompression easy''.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== How do I do that? ===&lt;br /&gt;
&lt;br /&gt;
The new &amp;lt;tt&amp;gt;System.IO.Compression&amp;lt;/tt&amp;gt; namespace introduces two new stream classes: &amp;lt;tt&amp;gt;GZipStream&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;DeflateStream&amp;lt;/tt&amp;gt;, which, as you'd guess, are used to compress and decompress streams of data.&lt;br /&gt;
&lt;br /&gt;
The algorithms used by these classes are ''lossless'', which means that when you compress and decompress your data, you won't lose any information.&lt;br /&gt;
&lt;br /&gt;
To use compression, you need to understand that a compression stream ''wraps'' another stream. For example, if you want to write some compressed data to a file, you first create a &amp;lt;tt&amp;gt;FileStream&amp;lt;/tt&amp;gt; for the file. Then, you wrap the &amp;lt;tt&amp;gt;FileStream&amp;lt;/tt&amp;gt; with the &amp;lt;tt&amp;gt;GZipStream&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;DeflateStream&amp;lt;/tt&amp;gt;. Here's how it works:&lt;br /&gt;
&lt;br /&gt;
 Dim fsWrite As New FileStream(FileName, FileMode.Create)&lt;br /&gt;
 Dim CompressStream As New GZipStream(fsWrite, CompressionMode.Compress)&lt;br /&gt;
&lt;br /&gt;
Now, if you want to write data to the file, you use the &amp;lt;tt&amp;gt;GZipStream&amp;lt;/tt&amp;gt;. The &amp;lt;tt&amp;gt;GZipStream&amp;lt;/tt&amp;gt; compresses that data, and then writes the compressed data to the wrapped &amp;lt;tt&amp;gt;FileStream&amp;lt;/tt&amp;gt;, which then writes it to the underlying file. If you skip this process and write directly to the &amp;lt;tt&amp;gt;FileStream&amp;lt;/tt&amp;gt;, you'll end up writing uncompressed data instead.&lt;br /&gt;
&lt;br /&gt;
Like all streams, the &amp;lt;tt&amp;gt;GZipStream&amp;lt;/tt&amp;gt; only allows you to write raw bytes. If you want to write strings or other data types, you need to create a &amp;lt;tt&amp;gt;StreamWriter&amp;lt;/tt&amp;gt;. The &amp;lt;tt&amp;gt;StreamWriter&amp;lt;/tt&amp;gt; accepts basic .NET data types (like strings and integers) and converts them to bytes. Here's an example:&lt;br /&gt;
&lt;br /&gt;
 Dim Writer As New StreamWriter(CompressStream)&lt;br /&gt;
     &lt;br /&gt;
 ' Put a compressed line of text into the file.&lt;br /&gt;
 Writer.Write(&amp;quot;This is some text&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
Finally, once you're finished, make sure you flush the &amp;lt;tt&amp;gt;GZipStream&amp;lt;/tt&amp;gt; so that all the data ends up in the file:&lt;br /&gt;
&lt;br /&gt;
 Writer.Flush( )&lt;br /&gt;
 CompressStream.Flush( )&lt;br /&gt;
 fsWrite.Close( )&lt;br /&gt;
&lt;br /&gt;
The process of decompression works in a similar way. In this case, you create a &amp;lt;tt&amp;gt;FileStream&amp;lt;/tt&amp;gt; for the file you want to read, and then create a &amp;lt;tt&amp;gt;GZipStream&amp;lt;/tt&amp;gt; that decompresses the data. You then read the data using the &amp;lt;tt&amp;gt;GZipStream&amp;lt;/tt&amp;gt;, as shown here:&lt;br /&gt;
&lt;br /&gt;
 fsRead = New FileStream(FileName, FileMode.Open)&lt;br /&gt;
 Dim DecompressStream As New GZipStream(fsRead, CompressionMode.Decompress)&lt;br /&gt;
&lt;br /&gt;
[[Visual Basic 2005: A Developer's Notebook/Files, Databases, and XML#vbadn-CHP-5-EX-5|Example 5-5]] shows an end-to-end example that writes some compressed data to a file, displays the amount of space saved, and then decompresses the data.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;vbadn-CHP-5-EX-5&amp;quot;&amp;gt;&lt;br /&gt;
'''Example 5-5. Compress and decompress a sample file'''&lt;br /&gt;
&lt;br /&gt;
 Imports System.IO&lt;br /&gt;
     &lt;br /&gt;
 Module FileCompression&lt;br /&gt;
     &lt;br /&gt;
     Public Sub Main( )&lt;br /&gt;
         ' Read original file.&lt;br /&gt;
         Dim SourceFile As String&lt;br /&gt;
         SourceFile = My.Computer.FileSystem.CurrentDirectory &amp;amp; &amp;quot;\test.txt&amp;quot;&lt;br /&gt;
         Dim fsRead As New FileStream(SourceFile, FileMode.Open)&lt;br /&gt;
         Dim FileBytes(fsRead.Length - 1) As Byte&lt;br /&gt;
         fsRead.Read(FileBytes, 0, FileBytes.Length)&lt;br /&gt;
         fsRead.Close( )&lt;br /&gt;
     &lt;br /&gt;
         ' Write to a new compressed file.&lt;br /&gt;
         Dim TargetFile As String&lt;br /&gt;
         TargetFile = My.Computer.FileSystem.CurrentDirectory &amp;amp; &amp;quot;\test.bin&amp;quot;&lt;br /&gt;
         Dim fsWrite As New FileStream(TargetFile, FileMode.Create)&lt;br /&gt;
         Dim CompressStream As New GZipStream(fsWrite, CompressionMode.Compress)&lt;br /&gt;
         CompressStream.Write(FileBytes, 0, FileBytes.Length)&lt;br /&gt;
         CompressStream.Flush( )&lt;br /&gt;
         CompressStream.Close( )&lt;br /&gt;
         fsWrite.Close( )&lt;br /&gt;
     &lt;br /&gt;
         Console.WriteLine(&amp;quot;File compressed from &amp;quot; &amp;amp; _&lt;br /&gt;
           New FileInfo(SourceFile).Length &amp;amp; &amp;quot; bytes to &amp;quot; &amp;amp; _&lt;br /&gt;
           New FileInfo(TargetFile).Length &amp;amp; &amp;quot; bytes.&amp;quot;)&lt;br /&gt;
     &lt;br /&gt;
         Console.WriteLine(&amp;quot;Press Enter to decompress.&amp;quot;)&lt;br /&gt;
         Console.ReadLine( )&lt;br /&gt;
     &lt;br /&gt;
         fsRead = New FileStream(TargetFile, FileMode.Open)&lt;br /&gt;
         Dim DecompressStream As New GZipStream(fsRead, CompressionMode.Decompress)&lt;br /&gt;
         Dim Reader As New StreamReader(CType(DecompressStream, Stream))&lt;br /&gt;
         Console.WriteLine(Reader.ReadToEnd( ))&lt;br /&gt;
         Reader.Close( )&lt;br /&gt;
         fsRead.Close( )&lt;br /&gt;
     End Sub&lt;br /&gt;
     &lt;br /&gt;
 End Module&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== What about... ===&lt;br /&gt;
&lt;br /&gt;
...unzipping ''.zip'' files? Unfortunately, the .NET 2.0 compression streams can't deal with ZIP files, file archives that are commonly used to shrink batches of files (often before storing them for the long term or attaching them to an email message). If you need this specific ability, you'll probably be interested in the freely downloadable #ziplib (available at ''http://www.icsharpcode.net/OpenSource/SharpZipLib'').&lt;br /&gt;
&lt;br /&gt;
=== Where can I learn more? ===&lt;br /&gt;
&lt;br /&gt;
For more information about the &amp;lt;tt&amp;gt;GZipStream&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;DeflateStream&amp;lt;/tt&amp;gt; algorithms, look them up in the MSDN Help. You can also look up the &amp;quot;compression&amp;quot; index entry for a Windows application example that uses these classes.&lt;br /&gt;
&lt;br /&gt;
== Collect Statistics on Your Data Connections ==&lt;br /&gt;
&lt;br /&gt;
Most programmers like to look at statistics. Considered carefully, they can suggest the underlying cause of a long-standing problem, explain the performance problems of an application, or suggest possible optimization techniques. If you're using the SQL Server provider, you can make use of a new &amp;lt;tt&amp;gt;SqlConnection.RetrieveStatistics()&amp;lt;/tt&amp;gt; method to get a hashtable with a slew of diagnostic details about your database connection.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;note&amp;quot;&amp;gt;&lt;br /&gt;
'''Note'''&lt;br /&gt;
&lt;br /&gt;
''Want to find out what's really going on while you're connected to a database? In . NET 2.0, you can get ahold of much more information, but only if you're using SQL Server''.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== How do I do that? ===&lt;br /&gt;
&lt;br /&gt;
Before you can call &amp;lt;tt&amp;gt;RetrieveStatistics( )&amp;lt;/tt&amp;gt;, you need to instruct it to collect statistics by setting the &amp;lt;tt&amp;gt;SqlConnection.StatisticsEnabled&amp;lt;/tt&amp;gt; property to &amp;lt;tt&amp;gt;True&amp;lt;/tt&amp;gt;. Once you take this step, the &amp;lt;tt&amp;gt;SqlConnection&amp;lt;/tt&amp;gt; class will gather statistics for every database command you execute over the connection. If you perform multiple operations with the same connection, the statistics will be cumulative, even if you close the connection between each operation.&lt;br /&gt;
&lt;br /&gt;
To take a look at the statistics at any time, you call the &amp;lt;tt&amp;gt;RetrieveStatistics( )&amp;lt;/tt&amp;gt; method to retrieve a hashtable containing the accumulated data. The hashtable indexes its members with a descriptive name. For example, to retrieve the number of transactions you've performed, you'd write this code:&lt;br /&gt;
&lt;br /&gt;
 Dim Stats as Hashtable = con.RetrieveStatistics( )&lt;br /&gt;
 Console.Writeline(Stats(&amp;quot;Transactions&amp;quot;))&lt;br /&gt;
&lt;br /&gt;
To get a good idea of the different statistics available, try running [[Visual Basic 2005: A Developer's Notebook/Files, Databases, and XML#vbadn-CHP-5-EX-6|Example 5-6]], a console application that iterates over the statistics collection and displays the key name and value of each statistic it contains.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;vbadn-CHP-5-EX-6&amp;quot;&amp;gt;&lt;br /&gt;
'''Example 5-6. Retrieving all the connection statistics'''&lt;br /&gt;
&lt;br /&gt;
 Imports System.Data.SqlClient&lt;br /&gt;
     &lt;br /&gt;
 Module StatisticsTest&lt;br /&gt;
     &lt;br /&gt;
     Private ConnectString As String = _&lt;br /&gt;
       &amp;quot;Data Source=localhost;Initial Catalog=Northwind;Integrated Security=SSPI&amp;quot;&lt;br /&gt;
     Private con As New SqlConnection(ConnectString)&lt;br /&gt;
     &lt;br /&gt;
     Public Sub Main( )&lt;br /&gt;
         ' Turn on statistics collection.&lt;br /&gt;
         con.StatisticsEnabled = True&lt;br /&gt;
     &lt;br /&gt;
         ' Perform two sample commands.&lt;br /&gt;
         SampleCommand( )&lt;br /&gt;
         SampleCommand( )&lt;br /&gt;
     &lt;br /&gt;
         ' Retrive the hashtable with statistics.&lt;br /&gt;
         Dim Stats As Hashtable = con.RetrieveStatistics( )&lt;br /&gt;
     &lt;br /&gt;
         ' Display all the statistics.&lt;br /&gt;
         For Each Key As String In Stats.Keys&lt;br /&gt;
             Console.WriteLine(Key &amp;amp; &amp;quot; = &amp;quot; &amp;amp; Stats(Key))&lt;br /&gt;
         Next&lt;br /&gt;
     End Sub&lt;br /&gt;
     &lt;br /&gt;
     Private Sub SampleCommand( )&lt;br /&gt;
         con.Open( )&lt;br /&gt;
         Dim cmd As New SqlCommand(&amp;quot;SELECT * FROM Customers&amp;quot;, con)&lt;br /&gt;
         Dim reader As SqlDataReader = cmd.ExecuteReader( )&lt;br /&gt;
         reader.Close( )&lt;br /&gt;
         con.Close( )&lt;br /&gt;
     End Sub&lt;br /&gt;
     &lt;br /&gt;
 End Module&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here's the complete list of statistics produced by this code:&lt;br /&gt;
&lt;br /&gt;
 NetworkServerTime = 18&lt;br /&gt;
 BytesReceived = 46248&lt;br /&gt;
 Transactions = 0&lt;br /&gt;
 SumResultSets = 2&lt;br /&gt;
 SelectCount = 2&lt;br /&gt;
 PreparedExecs = 0&lt;br /&gt;
 ConnectionTime = 13&lt;br /&gt;
 CursorFetchCount = 0&lt;br /&gt;
 CursorUsed = 0&lt;br /&gt;
 Prepares = 0&lt;br /&gt;
 CursorFetchTime = 0&lt;br /&gt;
 UnpreparedExecs = 2&lt;br /&gt;
 SelectRows = 182&lt;br /&gt;
 ServerRoundtrips = 2&lt;br /&gt;
 CursorOpens = 0&lt;br /&gt;
 BuffersSent = 2&lt;br /&gt;
 ExecutionTime = 725&lt;br /&gt;
 BytesSent = 108&lt;br /&gt;
 BuffersReceived = 6&lt;br /&gt;
 IduRows = 0&lt;br /&gt;
 IduCount = 0&lt;br /&gt;
&lt;br /&gt;
To reset the values of the statistics collection to zero at any time, simply call the &amp;lt;tt&amp;gt;ResetStatistics()&amp;lt;/tt&amp;gt; method:&lt;br /&gt;
&lt;br /&gt;
 con.ResetStatistics( )&lt;br /&gt;
&lt;br /&gt;
=== What about... ===&lt;br /&gt;
&lt;br /&gt;
...making sense of the various statistics gathered and putting them to use? Unfortunately, the MSDN Help doesn't yet provide the full lowdown on the SQL Server statistics. However, several statistics are particularly useful and not too difficult to interpret:&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;tt&amp;gt;BytesReceived&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Gives a snapshot of the total number of bytes retrieved from the database server.&lt;br /&gt;
;&amp;lt;tt&amp;gt;ServerRoundtrips&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Indicates the number of distinct commands you've executed.&lt;br /&gt;
;&amp;lt;tt&amp;gt;ConnectionTime&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Indicates the cumulative amount of time the connection has been open.&lt;br /&gt;
;&amp;lt;tt&amp;gt;SumResultSets&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Indicates the number of queries you've performed.&lt;br /&gt;
;&amp;lt;tt&amp;gt;SelectRows&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Records the total number of rows retrieved in every query you've executed. (In the previous example this is 182, because each query retrieved 91 rows.)&lt;br /&gt;
&lt;br /&gt;
And for an example where statistics are used to profile different approaches to database code, refer to the next lab, &amp;quot;Batch DataAdapter Commands for Better Performance.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== Batch DataAdapter Commands for Better Performance ==&lt;br /&gt;
&lt;br /&gt;
Many databases are able to execute commands in batches, reducing the total number of calls you need to make. For example, if you submit 10 update commands in a single batch, your code only needs to make 1 trip to the server (instead of 10). Cutting down the number of round-trips can increase performance, particularly on networks that have a high degree of latency. In .NET 2.0, the &amp;lt;tt&amp;gt;SqlDataAdapter&amp;lt;/tt&amp;gt; is enhanced to use batching for updating, inserting, and deleting records.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;note&amp;quot;&amp;gt;&lt;br /&gt;
'''Note'''&lt;br /&gt;
&lt;br /&gt;
''If you need an easy way to optimize DataSet updates, ADO. NET 's new batching can help you out''.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== How do I do that? ===&lt;br /&gt;
&lt;br /&gt;
In previous versions of .NET, you could batch direct commands by concatenating them in a single string, and separating each with a semicolon. This syntax requires support from the database provider, but it works perfectly well with SQL Server. Here's an example that inserts two rows into a table:&lt;br /&gt;
&lt;br /&gt;
 Dim TwoInserts As String =&amp;quot;INSERT INTO Shippers&amp;quot; &amp;amp;_&lt;br /&gt;
   &amp;quot;(CompanyName, Phone) VALUES &amp;quot;ACME&amp;quot;, &amp;quot;212-111-1111;&amp;quot; &amp;amp; _&lt;br /&gt;
   &amp;quot;INSERT INTO Shippers (CompanyName, Phone)&amp;quot; &amp;amp;_&lt;br /&gt;
   VALUES &amp;quot;Grey Matter&amp;quot;, &amp;quot;416-123-4567&amp;quot;&lt;br /&gt;
     &lt;br /&gt;
 Dim cmd As New SqlCommand(TwoInsert)&lt;br /&gt;
 cmd.ExecuteNonQuery( )&lt;br /&gt;
&lt;br /&gt;
As useful as this feature is, previous versions of .NET didn't provide any way to batch commands to one of the most important ADO.NET provider objects—the data adapter. The data-adapter object scans a &amp;lt;tt&amp;gt;DataSet&amp;lt;/tt&amp;gt;, and executes insert, delete, and update commands whenever it finds a new, removed, or changed row. Each of these commands is executed separately, which means that if your &amp;lt;tt&amp;gt;DataSet&amp;lt;/tt&amp;gt; contains three new rows, the data adapter will make three round-trips to the server.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;note&amp;quot;&amp;gt;&lt;br /&gt;
'''Note'''&lt;br /&gt;
&lt;br /&gt;
''It makes good sense to have batching support in the data adapter, because the data adapter is often used to commit more than one modification at a time''.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
.NET 2.0 improves the picture with a new &amp;lt;tt&amp;gt;SqlDataAdapter.UpdateBatchSize&amp;lt;/tt&amp;gt; property. By default, the value of this property is set to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt;, which causes each insert, update, or delete command to be executed separately. If you set the &amp;lt;tt&amp;gt;UpdateBatchSize&amp;lt;/tt&amp;gt; to a larger number, the data adapter will group its commands into batches.&lt;br /&gt;
&lt;br /&gt;
[[Visual Basic 2005: A Developer's Notebook/Files, Databases, and XML#vbadn-CHP-5-EX-7|Example 5-7]] is a console application, &amp;lt;tt&amp;gt;BatchedDataAdapterTest&amp;lt;/tt&amp;gt;, that puts this technique to the test. &amp;lt;tt&amp;gt;BatchedDataAdapterTest&amp;lt;/tt&amp;gt; retrieves data from the &amp;lt;tt&amp;gt;Orders&amp;lt;/tt&amp;gt; table in the Northwind database and then makes changes to each row. To make life interesting, the module applies this update not once, but twice—once without batching, and once with batch sizes set to 15. &amp;lt;tt&amp;gt;BatchedDataAdapterTest&amp;lt;/tt&amp;gt; displays connection statistics for each approach, allowing you to compare their performance.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;vbadn-CHP-5-EX-7&amp;quot;&amp;gt;&lt;br /&gt;
'''Example 5-7. Updates with and without batching'''&lt;br /&gt;
&lt;br /&gt;
 Imports System.Data.SqlClient&lt;br /&gt;
     &lt;br /&gt;
 Module BatchedDataAdapterTest&lt;br /&gt;
     &lt;br /&gt;
     Private ConnectString As String = _&lt;br /&gt;
       &amp;quot;Data Source=localhost;Initial Catalog=Northwind;Integrated Security=SSPI&amp;quot;&lt;br /&gt;
     Private con As New SqlConnection(ConnectString)&lt;br /&gt;
     &lt;br /&gt;
     Public Sub Main( )&lt;br /&gt;
         ' Turn on statistics collection.&lt;br /&gt;
         con.StatisticsEnabled = True&lt;br /&gt;
     &lt;br /&gt;
         Dim Query As String = &amp;quot;SELECT * FROM Orders&amp;quot;&lt;br /&gt;
         Dim cmd As New SqlCommand(Query, con)&lt;br /&gt;
         Dim Adapter As New SqlDataAdapter(cmd)&lt;br /&gt;
         Dim CommandBuilder As New SqlCommandBuilder(Adapter)&lt;br /&gt;
         &lt;br /&gt;
         Dim ds As New DataSet&lt;br /&gt;
         con.Open( )&lt;br /&gt;
         Adapter.Fill(ds, &amp;quot;Orders&amp;quot;)&lt;br /&gt;
         con.Close( )&lt;br /&gt;
     &lt;br /&gt;
         ' Perform an update without batching.&lt;br /&gt;
         ChangeRows(ds)&lt;br /&gt;
         con.ResetStatistics( )&lt;br /&gt;
         Adapter.Update(ds, &amp;quot;Orders&amp;quot;)&lt;br /&gt;
         Console.WriteLine(&amp;quot;Statistics without batching....&amp;quot;)&lt;br /&gt;
         DisplayStatistics( )&lt;br /&gt;
     &lt;br /&gt;
         ' Perform an update with batching (15 row batches).&lt;br /&gt;
         ChangeRows(ds)&lt;br /&gt;
         con.ResetStatistics( )&lt;br /&gt;
         '''Adapter.UpdateBatchSize = 15'''&lt;br /&gt;
         ' When performing a batch update you must explicitly&lt;br /&gt;
         con.Open( )&lt;br /&gt;
         Adapter.Update(ds, &amp;quot;Orders&amp;quot;)&lt;br /&gt;
         con.Close( )&lt;br /&gt;
     &lt;br /&gt;
         Console.WriteLine(&amp;quot;Statistics with batching....&amp;quot;)&lt;br /&gt;
         DisplayStatistics( )&lt;br /&gt;
     End Sub&lt;br /&gt;
     &lt;br /&gt;
     Public Sub ChangeRows(ByVal ds As DataSet)&lt;br /&gt;
         For Each Row As DataRow In ds.Tables(&amp;quot;Orders&amp;quot;).Rows&lt;br /&gt;
             Row(&amp;quot;ShippedDate&amp;quot;) = DateTime.Now&lt;br /&gt;
         Next&lt;br /&gt;
     End Sub&lt;br /&gt;
     &lt;br /&gt;
     Public Sub DisplayStatistics( )&lt;br /&gt;
         ' Retrive the hasthable with statistics.&lt;br /&gt;
         Dim Stats As Hashtable = con.RetrieveStatistics( )&lt;br /&gt;
     &lt;br /&gt;
         ' Display all the statistics.&lt;br /&gt;
         For Each Key As String In Stats.Keys&lt;br /&gt;
             Console.WriteLine(Key &amp;amp; &amp;quot; = &amp;quot; &amp;amp; Stats(Key))&lt;br /&gt;
         Next&lt;br /&gt;
         Console.WriteLine( )&lt;br /&gt;
     End Sub&lt;br /&gt;
     &lt;br /&gt;
 End Module&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When you run this application, the rows will be updated, and a list of statistics will appear. Take a close look at these statistics, paying special attention to the number of round-trips made to the database, the total connection time, and the amount of data required to complete the updates. Here's a portion of the output generated by one run of the application that highlights some of the more important numbers:&lt;br /&gt;
&lt;br /&gt;
 Statistics without batching....&lt;br /&gt;
 ConnectionTime = 5682&lt;br /&gt;
 UnpreparedExecs = 831&lt;br /&gt;
 ServerRoundtrips = 831&lt;br /&gt;
 BytesSent = 2637094&lt;br /&gt;
     &lt;br /&gt;
 Statistics with batching....&lt;br /&gt;
 ConnectionTime = 6319&lt;br /&gt;
 UnpreparedExecs = 56&lt;br /&gt;
 ServerRoundtrips = 56&lt;br /&gt;
 BytesSent = 1668160&lt;br /&gt;
&lt;br /&gt;
This listing reports that, in the batched update, 831 rows were updated in 56 batches of 15 commands each. As you can see, batching reduced the amount of data that needed to be sent (by packing it more effectively into batches), which is one of the most important metrics of database scalability. On the other hand, the overall performance of the application hardly changed at all, and the connection time even increased slightly. Clearly, to make a meaningful decision about whether to use batching, you need to profile your application in a real-world scenario.&lt;br /&gt;
&lt;br /&gt;
=== What about... ===&lt;br /&gt;
&lt;br /&gt;
...the quirks and limitations of batched updates? Currently, only the &amp;lt;tt&amp;gt;SqlDataAdapter&amp;lt;/tt&amp;gt; supports batching, although other providers may implement this functionality in the future. The actual implementation details will differ for each provider—in the case of the &amp;lt;tt&amp;gt;SqlDataAdapter&amp;lt;/tt&amp;gt;, the provider uses the &amp;lt;tt&amp;gt;sp_executesql&amp;lt;/tt&amp;gt; system stored procedure to execute the batch. As for quirks, you'll notice a change to how the &amp;lt;tt&amp;gt;RowUpdated&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;RowUpdating&amp;lt;/tt&amp;gt; events of the &amp;lt;tt&amp;gt;SqlDataAdapter&amp;lt;/tt&amp;gt; work. When batching is enabled, these events fire once for every batch, not once for every row. That means that when the &amp;lt;tt&amp;gt;RowUpdated&amp;lt;/tt&amp;gt; event fires, you can determine the number of rows affected, but not the row-by-row details of the changes made. This loss of information can make it more difficult to handle errors that occur somewhere inside a batch.&lt;br /&gt;
&lt;br /&gt;
The ideal batch size depends on a variety of low-level factors, including the network architecture and the size of the rows. The best advice is to test your application with different batch settings. If you want all updates to be done in a single batch of unlimited size, set the &amp;lt;tt&amp;gt;UpdateBatchSize&amp;lt;/tt&amp;gt; property to 0.&lt;br /&gt;
&lt;br /&gt;
== Bulk-Copy Rows from One Table to Another ==&lt;br /&gt;
&lt;br /&gt;
Most SQL Server gurus are familiar with the BCP command-line utility, which allows you to move vast amounts of information from one SQL Server database to another. BCP comes in handy any time you need to load a large number of records at once, but it's particularly useful when you need to transfer data between servers. In .NET 2.0, the &amp;lt;tt&amp;gt;SqlClient&amp;lt;/tt&amp;gt; namespace includes a new &amp;lt;tt&amp;gt;SqlBulkCopy&amp;lt;/tt&amp;gt; class that allows you to perform a bulk-copy operation programmatically.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;note&amp;quot;&amp;gt;&lt;br /&gt;
'''Note'''&lt;br /&gt;
&lt;br /&gt;
''The new SqlBulkCopy class gives you the most efficient way to copy large amounts of data between tables or databases''.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== How do I do that? ===&lt;br /&gt;
&lt;br /&gt;
The key ingredient in a bulk-copy operation is the new &amp;lt;tt&amp;gt;SqlBulkCopy&amp;lt;/tt&amp;gt; class. It performs all of its work when you call the &amp;lt;tt&amp;gt;WriteToServer()&amp;lt;/tt&amp;gt; method, which can be used in two ways:&lt;br /&gt;
&lt;br /&gt;
* You can submit your data as a &amp;lt;tt&amp;gt;DataTable&amp;lt;/tt&amp;gt; or an array of &amp;lt;tt&amp;gt;DataRow&amp;lt;/tt&amp;gt; objects. This makes sense if you want to insert a batch of records from a file you created earlier. It also works well if you're creating a server-side component (like a web service) that receives a disconnected &amp;lt;tt&amp;gt;DataSet&amp;lt;/tt&amp;gt; with the records that need to be loaded into a table.&lt;br /&gt;
* You can submit your data as an open &amp;lt;tt&amp;gt;DataReader&amp;lt;/tt&amp;gt; that draws records from another &amp;lt;tt&amp;gt;SqlConnection&amp;lt;/tt&amp;gt;. This approach is ideal if you want to transfer records from one database server to another.&lt;br /&gt;
&lt;br /&gt;
Before you call &amp;lt;tt&amp;gt;WriteToServer( )&amp;lt;/tt&amp;gt;, you need to create the connections and commands you need and set up ''mapping'' between the destination and source table. If your source and destination tables match exactly, no mapping is required. However, if the table names differ, you need to set the &amp;lt;tt&amp;gt;SqlBulkCopy.DestinationTableName&amp;lt;/tt&amp;gt; property to the name of the target table. Additionally, if the column names don't match or if there are fewer columns in the target table than there are in the source data, you also need to configure column mapping. To set column mapping, you add one mapping object for each column to the &amp;lt;tt&amp;gt;SqlBulkCopy.ColumnMappings&amp;lt;/tt&amp;gt; collection. Each mapping object specifies the name of the source column and the name of the corresponding target column.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;vbadn-CHP-5-FIG-1&amp;quot;&amp;gt;&lt;br /&gt;
'''Figure 5-1. Creating a CustomersShort table'''&lt;br /&gt;
&lt;br /&gt;
[[Image:Visual Basic 2005: A Developers Notebook_I_5_tt186.png|Creating a CustomersShort table]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To try this out, create a new SQL Server database named ''NorthwindCopy'' and a table named ''CustomersShort''. The ''CustomersShort'' table is designed to offer a subset of the information in the ''Customers'' table. You can create it by using a tool like SQL Server Enterprise Manager (see the column settings in [[Visual Basic 2005: A Developer's Notebook/Files, Databases, and XML#vbadn-CHP-5-FIG-1|Figure 5-1]]), or you can use the script included with the downloadable content for this chapter to create it automatically (look for the file ''GenerateNorthwindCopy.sql'').&lt;br /&gt;
&lt;br /&gt;
Once you've created ''CustomersShort'', you have a perfect table for testing a SQL Server bulk-copy operation. All you need to do is create two connections, define the mapping, and start the process. [[Visual Basic 2005: A Developer's Notebook/Files, Databases, and XML#vbadn-CHP-5-EX-8|Example 5-8]] has the code you need.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;vbadn-CHP-5-EX-8&amp;quot;&amp;gt;&lt;br /&gt;
'''Example 5-8. Using SQLBulkCopy'''&lt;br /&gt;
&lt;br /&gt;
 Imports System.Data.SqlClient&lt;br /&gt;
     &lt;br /&gt;
 Module Module1&lt;br /&gt;
     &lt;br /&gt;
     Private ConnectSource As String = _&lt;br /&gt;
      &amp;quot;Data Source=localhost;Initial Catalog=Northwind;Integrated Security=SSPI&amp;quot;&lt;br /&gt;
     Private ConnectTarget As String = _&lt;br /&gt;
      &amp;quot;Data Source=localhost;Initial Catalog=NorthwindCopy;&amp;quot; &amp;amp;_&lt;br /&gt;
     &lt;br /&gt;
     Public Sub Main( )&lt;br /&gt;
         ' Create the source and target connections.&lt;br /&gt;
         Dim conSource As New SqlConnection(ConnectSource)&lt;br /&gt;
         Dim conTarget As New SqlConnection(ConnectTarget)&lt;br /&gt;
     &lt;br /&gt;
         ' Create a command for counting the number of rows in a table.&lt;br /&gt;
         Dim cmdCount As New SqlCommand(&amp;quot;SELECT COUNT(*) FROM CustomersShort&amp;quot;, _&lt;br /&gt;
           conTarget)&lt;br /&gt;
     &lt;br /&gt;
         ' Initialize the SqlBulkCopy class with mapping information.&lt;br /&gt;
         Dim BCP As New SqlClient.SqlBulkCopy(conTarget)&lt;br /&gt;
         BCP.DestinationTableName = &amp;quot;CustomersShort&amp;quot;&lt;br /&gt;
         BCP.ColumnMappings.Add(&amp;quot;CustomerID&amp;quot;, &amp;quot;ID&amp;quot;)&lt;br /&gt;
         BCP.ColumnMappings.Add(&amp;quot;CompanyName&amp;quot;, &amp;quot;Company&amp;quot;)&lt;br /&gt;
         BCP.ColumnMappings.Add(&amp;quot;ContactName&amp;quot;, &amp;quot;Contact&amp;quot;)&lt;br /&gt;
     &lt;br /&gt;
         ' Count the rows in CustomersShort.&lt;br /&gt;
         conTarget.Open( )&lt;br /&gt;
         Dim Rows As Integer = CInt(cmdCount.ExecuteScalar( ))&lt;br /&gt;
         Console.WriteLine(&amp;quot;CustomersShort has &amp;quot; &amp;amp; Rows &amp;amp; &amp;quot; rows.&amp;quot;)&lt;br /&gt;
         Console.WriteLine(&amp;quot;Starting bulk copy...&amp;quot;)&lt;br /&gt;
     &lt;br /&gt;
         ' Retrieve the rows you want to transfer.&lt;br /&gt;
         conSource.Open( )&lt;br /&gt;
         Dim cmd As New SqlCommand( _&lt;br /&gt;
           &amp;quot;SELECT CustomerID,CompanyName,ContactName FROM Customers&amp;quot;, conSource)&lt;br /&gt;
         Dim reader As SqlDataReader = cmd.ExecuteReader( )&lt;br /&gt;
     &lt;br /&gt;
         ' Write the data to the destination table.&lt;br /&gt;
         BCP.WriteToServer(reader)&lt;br /&gt;
     &lt;br /&gt;
         ' Clean up.&lt;br /&gt;
         BCP.Close( )&lt;br /&gt;
         reader.Close( )&lt;br /&gt;
         conSource.Close( )&lt;br /&gt;
     &lt;br /&gt;
         ' Count the rows in CustomersShort again.&lt;br /&gt;
         conSource.Open( )&lt;br /&gt;
         Rows = CInt(cmdCount.ExecuteScalar( ))&lt;br /&gt;
         Console.WriteLine(&amp;quot;Finished bulk copy.&amp;quot;)&lt;br /&gt;
         Console.WriteLine(&amp;quot;CustomersShort has &amp;quot; &amp;amp; Rows &amp;amp; &amp;quot; rows.&amp;quot;)&lt;br /&gt;
     &lt;br /&gt;
         conTarget.Close( )&lt;br /&gt;
         Console.ReadLine( )&lt;br /&gt;
     End Sub&lt;br /&gt;
     &lt;br /&gt;
 End Module&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When you run the code, you'll see output like this, indicating that the bulk-copy operation completed successfully:&lt;br /&gt;
&lt;br /&gt;
 CustomersShort has 0 rows.&lt;br /&gt;
 Starting bulk copy...&lt;br /&gt;
 Finished bulk copy.&lt;br /&gt;
 CustomersShort has 91 rows.&lt;br /&gt;
&lt;br /&gt;
=== What about... ===&lt;br /&gt;
&lt;br /&gt;
...other &amp;lt;tt&amp;gt;SqlBulkCopy&amp;lt;/tt&amp;gt; properties? &amp;lt;tt&amp;gt;SqlBulkCopy&amp;lt;/tt&amp;gt; provides two useful properties: &amp;lt;tt&amp;gt;BulkCopyTimeout&amp;lt;/tt&amp;gt; (which allows you to set how long you'll wait for an unresponsive server) and &amp;lt;tt&amp;gt;BatchSize&amp;lt;/tt&amp;gt; (which allows you to set how many operations are batched together, as described in the lab &amp;quot;Batch DataAdapter Commands for Better Performance&amp;quot;). Errors are handled in the same way as when you directly execute a &amp;lt;tt&amp;gt;SqlCommand&amp;lt;/tt&amp;gt;. In other words, if an error happens on the server side (like a unique value conflict), the process will be interrupted immediately, and you'll receive a &amp;lt;tt&amp;gt;SqlClient&amp;lt;/tt&amp;gt; exception with the full details.&lt;br /&gt;
&lt;br /&gt;
=== Where can I learn more? ===&lt;br /&gt;
&lt;br /&gt;
For a complete list of class members, look up the &amp;lt;tt&amp;gt;SqlBulkCopy&amp;lt;/tt&amp;gt; class in the MSDN help library reference. Or, for information about the original BCP utility, look for the index entry &amp;quot;bcp utility&amp;quot; in the SQL Server Books Online help.&lt;br /&gt;
&lt;br /&gt;
== Write Database-Agnostic Code ==&lt;br /&gt;
&lt;br /&gt;
In developing ADO.NET, Microsoft set out to create a new data access architecture that would be more flexible, better performing, and more easily extensible than its previous COM-based OLE DB and ADO architectures. They did this by creating a model where every data source must supply its own ''data provider'': a set of managed classes that allow you to connect to a particular data source (e.g., SQL Server, Oracle), execute commands, and retrieve data. In order to ensure that these providers are consistent, each implements a standard set of interfaces. However, this approach creates major challenges for developers who want to write ''provider-agnostic'' code—for example, a basic database routine that can be used equally well with the SQL Server provider or the Oracle provider. Usually, you use provider-agnostic code because you aren't sure what type of database the final version of an application will use, or because you anticipate the need to migrate to a different database in the future.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;note&amp;quot;&amp;gt;&lt;br /&gt;
'''Note'''&lt;br /&gt;
&lt;br /&gt;
''Want a way to write database code that isn't bound to a specific data source? This challenge becomes a whole lot easier in . NET 2.0''.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
.NET 2.0 takes major steps to facilitate generic database coding by introducing a new ''factory model''. (A factory model is a pattern where one class has the exclusive responsibility for creating instances of other classes.) In this model, you can use a database provider factory to build the ADO.NET connections, commands, and many other types of objects required for a particular database. The factory automatically returns the type of object that you need for your data source (e.g., a &amp;lt;tt&amp;gt;SqlCommand&amp;lt;/tt&amp;gt; or an &amp;lt;tt&amp;gt;OracleCommand&amp;lt;/tt&amp;gt;), but when you write your code, you don't worry about these details. Instead, you write generic commands without regard to the particular details of the data source.&lt;br /&gt;
&lt;br /&gt;
=== How do I do that? ===&lt;br /&gt;
&lt;br /&gt;
In provider-agnostic code, you still use all the same strongly typed objects. However, your code manipulates these objects using common interfaces. For example, every command object, whether it's used for SQL Server or Oracle, implements the common &amp;lt;tt&amp;gt;IDbCommand&amp;lt;/tt&amp;gt; interface, which guarantees a basic set of methods and properties.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;note&amp;quot;&amp;gt;&lt;br /&gt;
'''Note'''&lt;br /&gt;
&lt;br /&gt;
''Because provider-agnostic code attempts to be as generic as possible, it's more difficult to properly optimize a database. As a result, this technique isn't suitable for most large-scale enterprise applications''.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Provider-agnostic code is structured so that you specify the type of database you're using early on, usually by reading some information from a configuration file. You use this information to retrieve a &amp;lt;tt&amp;gt;DbProviderFactory&amp;lt;/tt&amp;gt; for your database. Here's an example where the factory string is hardcoded:&lt;br /&gt;
&lt;br /&gt;
 Dim Factory As String = &amp;quot;System.Data.SqlClient&amp;quot;&lt;br /&gt;
 Dim Provider As DbProviderFactory&lt;br /&gt;
 Provider = DbProviderFactories.GetFactory(Factory)&lt;br /&gt;
&lt;br /&gt;
In this example, the code uses the shared &amp;lt;tt&amp;gt;GetFactory( )&amp;lt;/tt&amp;gt; method of the &amp;lt;tt&amp;gt;System.Data.Common.DbProviderFactories&amp;lt;/tt&amp;gt; class. It specifies a string that identifies the provider name. For example, if you use the string &amp;lt;tt&amp;gt;System.Data.SqlClient&amp;lt;/tt&amp;gt;, the &amp;lt;tt&amp;gt;GetFactory()&amp;lt;/tt&amp;gt; method returns a &amp;lt;tt&amp;gt;System.Data.SqlClient.SqlClientFactory&amp;lt;/tt&amp;gt; object. The &amp;lt;tt&amp;gt;DbProviderFactories&amp;lt;/tt&amp;gt; class can create factories for all the data providers included with .NET, because they are explicitly configured in the ''machine.config'' configuration file on the current computer. Essentially, the configuration record tells the &amp;lt;tt&amp;gt;DbProviderFactories&amp;lt;/tt&amp;gt; class to create a &amp;lt;tt&amp;gt;SqlClientFactory&amp;lt;/tt&amp;gt; when the programmer passes the exact string &amp;quot;System.Data.SqlClient.&amp;quot; If you develop your own provider, you can also register it to work in this way (although that task is beyond the scope of this lab).&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;SqlClientFactory&amp;lt;/tt&amp;gt; object has the built-in smarts to create all the objects used by the SQL Server provider. However, your code can be completely generic. Instead of interacting with the specific &amp;lt;tt&amp;gt;SqlClientFactory&amp;lt;/tt&amp;gt; class type, it should use the generic base class &amp;lt;tt&amp;gt;DbProviderFactory&amp;lt;/tt&amp;gt;. That way, your code can work with any type of &amp;lt;tt&amp;gt;DbProviderFactory&amp;lt;/tt&amp;gt;, and therefore support any database provider.&lt;br /&gt;
&lt;br /&gt;
Once you have the &amp;lt;tt&amp;gt;DbProviderFactory&amp;lt;/tt&amp;gt;, you can create other types of strongly typed ADO.NET objects using a set of common methods by using the &amp;lt;tt&amp;gt;Create&amp;lt;/tt&amp;gt; &amp;lt;tt&amp;gt;''Xxx''&amp;lt;/tt&amp;gt; &amp;lt;tt&amp;gt;()&amp;lt;/tt&amp;gt; methods. These include:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;CreateConnection( )&amp;lt;/tt&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;tt&amp;gt;CreateCommand( )&amp;lt;/tt&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;tt&amp;gt;CreateParameter( )&amp;lt;/tt&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;tt&amp;gt;CreateDataAdapter( )&amp;lt;/tt&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;tt&amp;gt;CreateCommandBuilder( )&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
All these methods create a provider-specific version of the object they name.&lt;br /&gt;
&lt;br /&gt;
To get a better understanding of how generic database code works, it helps to try out a complete example that can switch from one data provider to another on the fly. First of all, you need to create an application configuration file that stores all the provider-specific details. To do this, create a console application and open the ''app.config'' file. Add the following three settings, which specify the factory name, the connection string for the database, and the query to perform:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;utf-8&amp;quot; ?&amp;gt;&lt;br /&gt;
 &amp;lt;configuration&amp;gt;&lt;br /&gt;
   '''&amp;lt;appSettings&amp;gt;'''&lt;br /&gt;
                '''&amp;lt;add key=&amp;quot;Factory&amp;quot; value=&amp;quot;System.Data.SqlClient&amp;quot; /&amp;gt;'''&lt;br /&gt;
                '''&amp;lt;add key=&amp;quot;Connection&amp;quot; value='''&lt;br /&gt;
  &amp;quot;'''Data Source=localhost;Initial Catalog=Northwind;Integrated Security=SSPI&amp;quot; /&amp;gt;'''&lt;br /&gt;
                '''&amp;lt;add key=&amp;quot;Query&amp;quot; value=&amp;quot;SELECT * FROM Orders&amp;quot; /&amp;gt;'''&lt;br /&gt;
                '''&amp;lt;/appSettings&amp;gt;'''&lt;br /&gt;
 &amp;lt;/configuration&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This example uses the SQL Server provider to connect to the Northwind database and retrieve a list of all the records in the Orders table.&lt;br /&gt;
&lt;br /&gt;
Now you can retrieve the configuration file information and use it with the &amp;lt;tt&amp;gt;DbProviderFactories&amp;lt;/tt&amp;gt; class to create every ADO.NET provider object you need. In [[Visual Basic 2005: A Developer's Notebook/Files, Databases, and XML#vbadn-CHP-5-EX-9|Example 5-9]], the query is executed, a &amp;lt;tt&amp;gt;DataSet&amp;lt;/tt&amp;gt; is filled, and a list of OrderID values is displayed in the console window.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;vbadn-CHP-5-EX-9&amp;quot;&amp;gt;&lt;br /&gt;
'''Example 5-9. Using DbProviderFactories to write database-agnostic code'''&lt;br /&gt;
&lt;br /&gt;
 Imports System.Data.Common&lt;br /&gt;
 Imports System.Configuration&lt;br /&gt;
     &lt;br /&gt;
 Module GenericDatabaseTest&lt;br /&gt;
     &lt;br /&gt;
     Public Sub Main( )&lt;br /&gt;
         ' Get all the information from the configuration file.&lt;br /&gt;
         Dim Factory, Connection, Query As String&lt;br /&gt;
         Factory = ConfigurationManager.AppSettings(&amp;quot;Factory&amp;quot;)&lt;br /&gt;
         Connection = ConfigurationSettings.AppSettings(&amp;quot;Connection&amp;quot;)&lt;br /&gt;
         Query = ConfigurationManager.AppSettings(&amp;quot;Query&amp;quot;)&lt;br /&gt;
     &lt;br /&gt;
         ' Get the factory for this provider.&lt;br /&gt;
         Dim Provider As DbProviderFactory&lt;br /&gt;
         Provider = DbProviderFactories.GetFactory(Factory)&lt;br /&gt;
     &lt;br /&gt;
         ' Use the factory to create a connection.&lt;br /&gt;
         Dim con As DbConnection = Provider.CreateConnection( )&lt;br /&gt;
         con.ConnectionString = Connection&lt;br /&gt;
     &lt;br /&gt;
         ' Use the factory to create a data adapter&lt;br /&gt;
         ' and fill a DataSet.&lt;br /&gt;
         Dim Adapter As DbDataAdapter = Provider.CreateDataAdapter&lt;br /&gt;
         Adapter.SelectCommand = Provider.CreateCommand( )&lt;br /&gt;
         Adapter.SelectCommand.Connection = con&lt;br /&gt;
         Adapter.SelectCommand.CommandText = Query&lt;br /&gt;
         Dim ds As New DataSet&lt;br /&gt;
         Adapter.Fill(ds, &amp;quot;Orders&amp;quot;)&lt;br /&gt;
     &lt;br /&gt;
         ' Display the retrieved information.&lt;br /&gt;
         For Each Row As DataRow In ds.Tables(&amp;quot;Orders&amp;quot;).Rows&lt;br /&gt;
             Console.WriteLine(Row(&amp;quot;OrderID&amp;quot;))&lt;br /&gt;
         Next&lt;br /&gt;
     End Sub&lt;br /&gt;
     &lt;br /&gt;
 End Module&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mostly, this is a fairly pedestrian piece of data access logic. The only exciting part is that you can switch from one provider to another without modifying any of the code or recompiling. You just need to modify the provider information and connection string in the configuration file. For example, make these changes to the configuration file to access the same table through the slower OLE DB provider interface:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;utf-8&amp;quot; ?&amp;gt;&lt;br /&gt;
 &amp;lt;configuration&amp;gt;&lt;br /&gt;
   &amp;lt;appSettings&amp;gt;&lt;br /&gt;
     '''&amp;lt;add key=&amp;quot;Factory&amp;quot; value=&amp;quot;System.Data.OleDb&amp;quot; /&amp;gt;'''&lt;br /&gt;
                '''&amp;lt;add key=&amp;quot;Connection&amp;quot; value='''&lt;br /&gt;
  &amp;quot;'''Provider=SQLOLEDB;Data Source=localhost;Initial Catalog=Northwind;Integrated Security=SSPI&amp;quot; /&amp;gt;'''&lt;br /&gt;
     &amp;lt;add key=&amp;quot;Query&amp;quot; value=&amp;quot;SELECT * FROM Orders&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;/appSettings&amp;gt;&lt;br /&gt;
 &amp;lt;/configuration&amp;gt;&lt;br /&gt;
&lt;br /&gt;
After saving the configuration file, you can run the application again. It will work just as well, displaying the same list of order records.&lt;br /&gt;
&lt;br /&gt;
=== What about... ===&lt;br /&gt;
&lt;br /&gt;
...the challenges you'll encounter in writing database-agnostic programs? The new factory approach is a giant leap forward for those who want to write provider-agnostic code. However, a slew of problems (some minor and some more significant) still remain. These include:&lt;br /&gt;
&lt;br /&gt;
;Handling errors&lt;br /&gt;
: Every database provider has its own exception object (like &amp;lt;tt&amp;gt;SqlException&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;OracleException&amp;lt;/tt&amp;gt;), and these objects don't derive from a common base class. That means there's no way to write an exception handler that catches database exceptions generically. All you can do is write exception handlers that catch the base &amp;lt;tt&amp;gt;Exception&amp;lt;/tt&amp;gt; object.&lt;br /&gt;
;Provider-specific functionality&lt;br /&gt;
: Some features aren't exposed through the common interfaces. For example, SQL Server has the ability to execute FOR XML queries that return XML documents. To execute this type of query, you use the &amp;lt;tt&amp;gt;SqlCommand.ExecuteXmlReader( )&amp;lt;/tt&amp;gt; method. Unfortunately, this isn't a standard command method, so there's no way to access it through the &amp;lt;tt&amp;gt;IDbCommand&amp;lt;/tt&amp;gt; interface.&lt;br /&gt;
;Handling parameters&lt;br /&gt;
: Some providers (like SQL Server) recognize command parameters by their name. Others (like OLE DB) recognize command parameters by the order of their appearance. Minor differences like this can thwart provider-agnostic programming.&lt;br /&gt;
&lt;br /&gt;
=== Where can I learn more? ===&lt;br /&gt;
&lt;br /&gt;
Unfortunately, there isn't much documentation yet in the MSDN Help about provider-agnostic coding. However, you can get a good overview with additional examples from the Microsoft whitepaper at ''http://msdn.microsoft.com/library/en-us/dnvs05/html/vsgenerics.asp''.&lt;br /&gt;
&lt;br /&gt;
== Use the New XPathDocument and XPathNavigator ==&lt;br /&gt;
&lt;br /&gt;
.NET provides a range of options for dealing with XML in the &amp;lt;tt&amp;gt;System.Xml&amp;lt;/tt&amp;gt; namespaces. One common choice is &amp;lt;tt&amp;gt;XmlDocument&amp;lt;/tt&amp;gt;, which lets you navigate in-memory XML as a collection of node objects. For more efficient performance, the &amp;lt;tt&amp;gt;XmlWriter&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;XmlReader&amp;lt;/tt&amp;gt; classes offer a streamlined way to read and write a stream of XML. Unfortunately, neither solution is perfect. The &amp;lt;tt&amp;gt;XmlDocument&amp;lt;/tt&amp;gt; consumes too much memory, and navigating its structure requires too much code. Furthermore, because the &amp;lt;tt&amp;gt;XmlDocument&amp;lt;/tt&amp;gt; is based on a third-party standard (the XML DOM, or ''document object model''), it's difficult to improve it without breaking compatibility. On the other hand, the &amp;lt;tt&amp;gt;XmlWriter&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;XmlReader&amp;lt;/tt&amp;gt; are too restrictive, forcing you to access information linearly from start to finish. They also make it prohibitively difficult for a developer to provide an XML interface to non-XML data.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;note&amp;quot;&amp;gt;&lt;br /&gt;
'''Note'''&lt;br /&gt;
&lt;br /&gt;
''Talk about an improvement! The revamped XPathDocument sets a new standard for XML parsing in . NET''.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
.NET 2.0 proposes a solution with the &amp;lt;tt&amp;gt;System.Xml.XPath.XPathDocument&amp;lt;/tt&amp;gt;. The &amp;lt;tt&amp;gt;XPathDocument&amp;lt;/tt&amp;gt; is a cursor-based XML reader that aims to become the only XML interface you need to use. It gives you the freedom to move to any position in a document, and it provides blistering speed when used with other XML standards such as XQuery, XPath, XSLT, and XML Schema validation.&lt;br /&gt;
&lt;br /&gt;
=== How do I do that? ===&lt;br /&gt;
&lt;br /&gt;
To use an &amp;lt;tt&amp;gt;XPathDocument&amp;lt;/tt&amp;gt;, you begin by loading the document from a stream, &amp;lt;tt&amp;gt;XmlReader&amp;lt;/tt&amp;gt;, or URI (which can include a file path or an Internet address). To load the content, you can use the &amp;lt;tt&amp;gt;Load( )&amp;lt;/tt&amp;gt; method or a constructor argument—they both work in the same way. In this example, the &amp;lt;tt&amp;gt;XPathDocument&amp;lt;/tt&amp;gt; is filled with the content from a local file:&lt;br /&gt;
&lt;br /&gt;
 Dim Doc As New XPathDocument(&amp;quot;c:\MyDocument.xml&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
To actually move around an &amp;lt;tt&amp;gt;XPathDocument&amp;lt;/tt&amp;gt;, you need to create an &amp;lt;tt&amp;gt;XPathNavigator&amp;lt;/tt&amp;gt; by calling the &amp;lt;tt&amp;gt;CreateNavigator( )&amp;lt;/tt&amp;gt; method.&lt;br /&gt;
&lt;br /&gt;
 Dim Navigator As XPathNavigator = Doc.CreateNavigator( )&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;XPathNavigator&amp;lt;/tt&amp;gt; includes a generous group of methods for navigating the structure of the XML document. Some of the methods include:&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;tt&amp;gt;MoveToRoot( )&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Jumps to the root, or document element that contains all the other elements.&lt;br /&gt;
;&amp;lt;tt&amp;gt;MoveToID( )&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Moves to an element that has a specific ID, as identified with the ID attribute.&lt;br /&gt;
;&amp;lt;tt&amp;gt;MoveToNext( )&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Moves to the next node at the same level (technically called a sibling).&lt;br /&gt;
;&amp;lt;tt&amp;gt;MoveToPrevious( )&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Moves to the previous node at the same level (technically called a sibling).&lt;br /&gt;
;&amp;lt;tt&amp;gt;MoveToFirstChild( )&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Moves down a level to the first node contained by the current node.&lt;br /&gt;
;&amp;lt;tt&amp;gt;MoveToParent( )&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Moves up a level to the parent that contains the current node.&lt;br /&gt;
&lt;br /&gt;
Once you're positioned on an element, you can read the element name from the &amp;lt;tt&amp;gt;Name&amp;lt;/tt&amp;gt; property. You can retrieve the contained text content from the &amp;lt;tt&amp;gt;Value&amp;lt;/tt&amp;gt; property.&lt;br /&gt;
&lt;br /&gt;
Now that you've learned this much, it's worth trying a basic example. In it, we'll use an XML document that contains a product catalog based on Microsoft's ASP.NET Commerce Starter Kit. This XML file (which is available with the downloadable content for this chapter) has the structure shown in [[Visual Basic 2005: A Developer's Notebook/Files, Databases, and XML#vbadn-CHP-5-EX-10|Example 5-10]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;vbadn-CHP-5-EX-10&amp;quot;&amp;gt;&lt;br /&gt;
'''Example 5-10. Sample XML for a product catalog'''&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;?xml version=&amp;quot;1.0&amp;quot; standalone=&amp;quot;yes&amp;quot;?&amp;gt;&lt;br /&gt;
 &amp;lt;Products&amp;gt;&lt;br /&gt;
   &amp;lt;Product&amp;gt;&lt;br /&gt;
     &amp;lt;ProductID&amp;gt;356&amp;lt;/ProductID&amp;gt;&lt;br /&gt;
     &amp;lt;ModelName&amp;gt;Edible Tape&amp;lt;/ModelName&amp;gt;&lt;br /&gt;
     &amp;lt;ModelNumber&amp;gt;STKY1&amp;lt;/ModelNumber&amp;gt;&lt;br /&gt;
     &amp;lt;UnitCost&amp;gt;3.99&amp;lt;/UnitCost&amp;gt;&lt;br /&gt;
     &amp;lt;CategoryName&amp;gt;General&amp;lt;/CategoryName&amp;gt;&lt;br /&gt;
   &amp;lt;/Product&amp;gt;&lt;br /&gt;
   &amp;lt;Product&amp;gt;&lt;br /&gt;
     &amp;lt;ProductID&amp;gt;357&amp;lt;/ProductID&amp;gt;&lt;br /&gt;
     &amp;lt;ModelName&amp;gt;Escape Vehicle (Air)&amp;lt;/ModelName&amp;gt;&lt;br /&gt;
     &amp;lt;ModelNumber&amp;gt;P38&amp;lt;/ModelNumber&amp;gt;&lt;br /&gt;
     &amp;lt;UnitCost&amp;gt;2.99&amp;lt;/UnitCost&amp;gt;&lt;br /&gt;
     &amp;lt;CategoryName&amp;gt;Travel&amp;lt;/CategoryName&amp;gt;&lt;br /&gt;
   &amp;lt;/Product&amp;gt;&lt;br /&gt;
  ...&lt;br /&gt;
 &amp;lt;/Products&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Visual Basic 2005: A Developer's Notebook/Files, Databases, and XML#vbadn-CHP-5-EX-11|Example 5-11]] loads this document, creates an &amp;lt;tt&amp;gt;XPathNavigator&amp;lt;/tt&amp;gt;, and moves through the nodes, looking for the &amp;lt;tt&amp;gt;&amp;lt;ModelName&amp;gt;&amp;lt;/tt&amp;gt; element for each &amp;lt;tt&amp;gt;&amp;lt;Product&amp;gt;&amp;lt;/tt&amp;gt;. When that element is found, its value is displayed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;vbadn-CHP-5-EX-11&amp;quot;&amp;gt;&lt;br /&gt;
'''Example 5-11. Navigating an XML document with XPathNavigator'''&lt;br /&gt;
&lt;br /&gt;
 Imports System.Xml.XPath&lt;br /&gt;
 Imports System.Xml&lt;br /&gt;
     &lt;br /&gt;
 Module XPathNavigatorTest&lt;br /&gt;
     &lt;br /&gt;
     Sub Main( )&lt;br /&gt;
         ' Load the document.&lt;br /&gt;
         Dim Doc As New XPathDocument( _&lt;br /&gt;
           My.Computer.FileSystem.CurrentDirectory &amp;amp; _&lt;br /&gt;
           &amp;quot;\ProductList.xml&amp;quot;)&lt;br /&gt;
     &lt;br /&gt;
         ' Navigate the document with an XPathNavigator.&lt;br /&gt;
         Dim Navigator As XPathNavigator = Doc.CreateNavigator( )&lt;br /&gt;
     &lt;br /&gt;
         ' Move to the root &amp;lt;Products&amp;gt; element.&lt;br /&gt;
         Navigator.MoveToFirstChild( )&lt;br /&gt;
     &lt;br /&gt;
         ' Move to the first contained &amp;lt;Product&amp;gt; element.&lt;br /&gt;
         Navigator.MoveToFirstChild( )&lt;br /&gt;
     &lt;br /&gt;
         ' Loop through all the &amp;lt;Product&amp;gt; elements.&lt;br /&gt;
         Do&lt;br /&gt;
             ' Search for the &amp;lt;ModelName&amp;gt; element inside &amp;lt;Product&amp;gt;&lt;br /&gt;
             ' and display its value.&lt;br /&gt;
             Navigator.MoveToFirstChild( )&lt;br /&gt;
             Do&lt;br /&gt;
                 If Navigator.Name = &amp;quot;ModelName&amp;quot; Then&lt;br /&gt;
                     Console.WriteLine(Navigator.Value)&lt;br /&gt;
                 End If&lt;br /&gt;
             Loop While Navigator.MoveToNext( )&lt;br /&gt;
     &lt;br /&gt;
             ' Move back to the &amp;lt;Product&amp;gt; element.&lt;br /&gt;
             Navigator.MoveToParent( )&lt;br /&gt;
         Loop While Navigator.MoveToNext( )&lt;br /&gt;
    End Sub&lt;br /&gt;
     &lt;br /&gt;
 End Module&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When you run this code, you'll see a display with a list of model names for all the products.&lt;br /&gt;
&lt;br /&gt;
Interestingly, the &amp;lt;tt&amp;gt;XPathNavigator&amp;lt;/tt&amp;gt; also provides ''strong typing'' for data values. Instead of retrieving the current value as a string using the &amp;lt;tt&amp;gt;Value&amp;lt;/tt&amp;gt; property, you can use one of the properties that automatically converts the value to another data type. Supported properties include:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;ValueAsBoolean&amp;lt;/tt&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;tt&amp;gt;ValueAsDateTime&amp;lt;/tt&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;tt&amp;gt;ValueAsDouble&amp;lt;/tt&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;tt&amp;gt;ValueAsInt&amp;lt;/tt&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;tt&amp;gt;ValueAsLong&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To try this out, you can rewrite the loop in [[Visual Basic 2005: A Developer's Notebook/Files, Databases, and XML#vbadn-CHP-5-EX-11|Example 5-11]] so that it converts the price to a double value and then displays a total with added sales tax:&lt;br /&gt;
&lt;br /&gt;
 Do&lt;br /&gt;
     If Navigator.Name = &amp;quot;ModelName&amp;quot; Then&lt;br /&gt;
         Console.WriteLine(Navigator.Value)&lt;br /&gt;
     '''ElseIf Navigator.Name = &amp;quot;UnitCost&amp;quot; Then'''&lt;br /&gt;
                '''Dim Price As Double = Navigator.ValueAsDouble * 1.15'''&lt;br /&gt;
                '''Console.WriteLine(vbTab &amp;amp; &amp;quot;Total with tax: &amp;quot; &amp;amp; Math.Round(Price, 2))'''&lt;br /&gt;
     End If&lt;br /&gt;
 Loop While Navigator.MoveToNext( )&lt;br /&gt;
&lt;br /&gt;
=== What about... ===&lt;br /&gt;
&lt;br /&gt;
...other ways to search an XML document with the &amp;lt;tt&amp;gt;XPathNavigator&amp;lt;/tt&amp;gt;? To simplify life, you can select a portion of the XML document to work with in an &amp;lt;tt&amp;gt;XPathNavigator&amp;lt;/tt&amp;gt;. To select this portion, you use the &amp;lt;tt&amp;gt;Select( )&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;SelectSingleNode( )&amp;lt;/tt&amp;gt; methods of the &amp;lt;tt&amp;gt;XPathNavigator&amp;lt;/tt&amp;gt; class. Both of these methods require an XPath expression that identifies the nodes you want to retrieve. (For more information about the XPath standard, see the &amp;quot;Introducing XPath&amp;quot; sidebar.)&lt;br /&gt;
&lt;br /&gt;
For example, the following code selects the &amp;lt;tt&amp;gt;&amp;lt;ModelName&amp;gt;&amp;lt;/tt&amp;gt; element for every product that's in the &amp;lt;tt&amp;gt;Tools&amp;lt;/tt&amp;gt; category:&lt;br /&gt;
&lt;br /&gt;
 ' Use an XPath expression to get just the nodes that interest you&lt;br /&gt;
 ' (in this case, all product names in the Tools category).&lt;br /&gt;
 Dim XPathIterator As XPathNodeIterator&lt;br /&gt;
 XPathIterator = Navigator.Select ( _&lt;br /&gt;
   &amp;quot;/Products/Product/ModelName[../CategoryName='Tools']&amp;quot;)&lt;br /&gt;
     &lt;br /&gt;
 Do While (XPathIterator.MoveNext( ))&lt;br /&gt;
     ' XPathIterator.Current is an XPathNavigator object pointed at the&lt;br /&gt;
     ' current node.&lt;br /&gt;
     Console.WriteLine(XPathIterator.Current.Value)&lt;br /&gt;
 Loop&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;tip&amp;quot;&amp;gt;&lt;br /&gt;
'''Tip'''&lt;br /&gt;
&lt;br /&gt;
The examples in this lab use an XML document with no namespace. However, namespaces are often used in programming scenarios to allow your program to uniquely identify the type of document it references. If your document uses namespaces, you need to use the &amp;lt;tt&amp;gt;XmlNamespaceManager&amp;lt;/tt&amp;gt; class and rewrite your XPath expressions to use a namespace prefix. If you'd like an example of this technique, refer to the downloadable samples for this lab, which demonstrate an example with a product catalog that uses XML namespaces.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;sidebar&amp;quot;&amp;gt;&lt;br /&gt;
'''Introducing XPath'''&lt;br /&gt;
&lt;br /&gt;
Basic XPath syntax uses a path-like notation to describe locations in a document. For example, the path &amp;lt;tt&amp;gt;/Products/Product/ModelName&amp;lt;/tt&amp;gt; indicates a &amp;lt;tt&amp;gt;ModelName&amp;lt;/tt&amp;gt; element that is nested inside a &amp;lt;tt&amp;gt;Product&amp;lt;/tt&amp;gt; element, which, in turn, is nested in a root &amp;lt;tt&amp;gt;Products&amp;lt;/tt&amp;gt; element. This is an absolute path (indicated by the fact that it starts with a single slash, representing the root of the document).&lt;br /&gt;
&lt;br /&gt;
You can also use relative paths, which search for nodes with a given name regardless of where they are. Relative paths start with two slashes. For example, &amp;lt;tt&amp;gt;//ModelName&amp;lt;/tt&amp;gt; will find all &amp;lt;tt&amp;gt;ModelName&amp;lt;/tt&amp;gt; elements no matter where they are in the document hierarchy. Other path characters that you can use include the period (.), which refers to the current node; the double period (&amp;lt;tt&amp;gt;.&amp;lt;/tt&amp;gt;.) to move up one level; and the asterisk (&amp;lt;tt&amp;gt;*&amp;lt;/tt&amp;gt;) to select any node.&lt;br /&gt;
&lt;br /&gt;
XPath gets really interesting when you start to add filter conditions. Filter conditions are added to a path in square brackets. For example, the XPath expression &amp;lt;tt&amp;gt;//Product[CategoryName='Tools']&amp;lt;/tt&amp;gt; finds all &amp;lt;tt&amp;gt;Product&amp;lt;/tt&amp;gt; elements that contain a &amp;lt;tt&amp;gt;CategoryName&amp;lt;/tt&amp;gt; element with the text &amp;quot;Tools.&amp;quot; You can use the full range of logical operators, such as less than and greater than (&amp;lt;tt&amp;gt;&amp;lt;&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;&amp;gt;&amp;lt;/tt&amp;gt;) or not equal to (&amp;lt;tt&amp;gt;!=&amp;lt;/tt&amp;gt;). For much more information about the wonderful world of XPath, refer to ''XML in a Nutshell'' (O'Reilly).&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Where can I learn more? ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;XPathNavigator&amp;lt;/tt&amp;gt; class is too detailed to cover completely in this lab. For more information, refer to both classes in the MSDN Help. Additionally, you can learn about XML standards like XPath, XQuery, and XML Schema from the excellent online tutorials at ''http://www.w3schools.com''.&lt;br /&gt;
&lt;br /&gt;
In addition, you'll find one more lab that can help you extend your &amp;lt;tt&amp;gt;XPathDocument&amp;lt;/tt&amp;gt; skills: &amp;quot;Edit an XML Document with XPathDocument,&amp;quot; which explains the editing features of the &amp;lt;tt&amp;gt;XPathDocument&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;warning&amp;quot;&amp;gt;&lt;br /&gt;
'''Warning'''&lt;br /&gt;
&lt;br /&gt;
The editable XPathNavigator has undergone extensive changes, and the features demonstrated in the next lab ([[Visual Basic 2005: A Developer's Notebook/Files, Databases, and XML#Edit an XML Document with XPathNavigator|Section 5.11]]) weren't working in the last build we tested. Although it's expected to return, features are sometimes cut even at this late stage. If the coding model changes, you'll find updated code in the downloadable examples for the book.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Edit an XML Document with XPathNavigator ==&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;XPathNavigator&amp;lt;/tt&amp;gt; is the XML interface of choice for Visual Basic 2005 applications. And in .NET 2.0, it doesn't just work as a view onto read-only XML data—it also allows you to change XML documents, such as by modifying text content, inserting new elements, or removing a branch of nodes.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;note&amp;quot;&amp;gt;&lt;br /&gt;
'''Note'''&lt;br /&gt;
&lt;br /&gt;
''The best feature of the XPathNavigator is its new support for editing and inserting content''.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== How do I do that? ===&lt;br /&gt;
&lt;br /&gt;
In the previous lab, [[Visual Basic 2005: A Developer's Notebook/Files, Databases, and XML#Use the New XPathDocument and XPathNavigator|Section 5.10]], you learned how to load XML data into an &amp;lt;tt&amp;gt;XPathDocument&amp;lt;/tt&amp;gt;, and then browse and search through it using an &amp;lt;tt&amp;gt;XPathNavigator&amp;lt;/tt&amp;gt;. If you want to make changes, you still start with the same &amp;lt;tt&amp;gt;XPathDocument&amp;lt;/tt&amp;gt;. The secret is that you also use a couple of additional methods in the &amp;lt;tt&amp;gt;XPathNavigator&amp;lt;/tt&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;tt&amp;gt;SetValue( )&amp;lt;/tt&amp;gt;&lt;br /&gt;
: This method inserts a new value in the current element, replacing the existing value.&lt;br /&gt;
;&amp;lt;tt&amp;gt;DeleteCurrent( )&amp;lt;/tt&amp;gt;&lt;br /&gt;
: This method removes the current node from the document.&lt;br /&gt;
&lt;br /&gt;
Remember, you have two basic choices for creating an &amp;lt;tt&amp;gt;XPathNavigator&amp;lt;/tt&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
;Use the &amp;lt;tt&amp;gt;XPathDocument.CreateNavigator( )&amp;lt;/tt&amp;gt; method&lt;br /&gt;
: This method returns an &amp;lt;tt&amp;gt;XPathNavigator&amp;lt;/tt&amp;gt; for the whole document. You can then move to the portion of the document you want to change.&lt;br /&gt;
;Use the &amp;lt;tt&amp;gt;XPathDocument.Select( )&amp;lt;/tt&amp;gt; method with an XPath expression&lt;br /&gt;
: This returns an &amp;lt;tt&amp;gt;XPathNodeIterator&amp;lt;/tt&amp;gt; that allows you to move through your results, retrieving an &amp;lt;tt&amp;gt;XPathNavigator&amp;lt;/tt&amp;gt; for each selected node.&lt;br /&gt;
&lt;br /&gt;
[[Visual Basic 2005: A Developer's Notebook/Files, Databases, and XML#vbadn-CHP-5-EX-12|Example 5-12]] modifies the XML document shown in [[Visual Basic 2005: A Developer's Notebook/Files, Databases, and XML#vbadn-CHP-5-EX-10|Example 5-10]]. It increases all the prices by 10% and then deletes nodes that don't fall into the Tools category. Finally, it displays the altered XML document.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;vbadn-CHP-5-EX-12&amp;quot;&amp;gt;&lt;br /&gt;
'''Example 5-12. Modifying an XML document with XPathNavigator'''&lt;br /&gt;
&lt;br /&gt;
 Imports System.Xml.XPath&lt;br /&gt;
 Imports System.Xml&lt;br /&gt;
     &lt;br /&gt;
 Module XPathNavigatorTest&lt;br /&gt;
     &lt;br /&gt;
     Sub Main( )&lt;br /&gt;
         ' Load the document.&lt;br /&gt;
         Dim Doc As New XPathDocument(My.Computer.FileSystem.CurrentDirectory &amp;amp; _&lt;br /&gt;
           &amp;quot;\ProductList.xml&amp;quot;)&lt;br /&gt;
     &lt;br /&gt;
         ' Use the XPathNavigator to make updates.&lt;br /&gt;
         Dim XPathIterator As XPathNodeIterator = Doc.Select(&amp;quot;//UnitCost&amp;quot;)&lt;br /&gt;
     &lt;br /&gt;
         ' Increase the price by 10%.&lt;br /&gt;
         For Each Editor As XPathNavigator In XPathIterator&lt;br /&gt;
             Editor.SetValue((1.1 * Editor.ValueAsDouble).ToString( ))&lt;br /&gt;
         Next&lt;br /&gt;
     &lt;br /&gt;
         ' Delete nodes that aren't in the Tools category.&lt;br /&gt;
         XPathIterator = Doc.Select(&amp;quot;/Products/Product[CategoryName!='Tools']&amp;quot;)&lt;br /&gt;
         For Each Editor As XPathNavigator In XPathIterator&lt;br /&gt;
             Editor.DeleteCurrent( )&lt;br /&gt;
         Next&lt;br /&gt;
     &lt;br /&gt;
         ' Show changes.&lt;br /&gt;
         XPathEditor.MoveToRoot( )&lt;br /&gt;
         Console.WriteLine(XPathEditor.OuterXml)&lt;br /&gt;
     End Sub&lt;br /&gt;
     &lt;br /&gt;
 End Module&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When you run this application, the XML for the changed document is displayed in the console window. You can also open the ''ProductList_new.xml'' file where the changes are saved.&lt;br /&gt;
&lt;br /&gt;
In many cases, you won't just want to change a value—you'll need a way to insert new elements or entire sections. The &amp;lt;tt&amp;gt;XPathNavigator&amp;lt;/tt&amp;gt; includes a handful of methods for inserting new elements and attributes in one shot. However, the easiest way to add a block of XML is to use an &amp;lt;tt&amp;gt;XmlWriter&amp;lt;/tt&amp;gt;. If you've worked with XML and .NET before, you probably recognize the &amp;lt;tt&amp;gt;XmlWriter&amp;lt;/tt&amp;gt;. The &amp;lt;tt&amp;gt;XmlWriter&amp;lt;/tt&amp;gt; was commonly used to write XML content directly to a file in .NET 1.x applications. The difference in .NET 2.0 is that the &amp;lt;tt&amp;gt;XPathEditor&amp;lt;/tt&amp;gt; allows you to use the &amp;lt;tt&amp;gt;XmlWriter&amp;lt;/tt&amp;gt; to write directly to your in-memory &amp;lt;tt&amp;gt;XPathDocument&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
All you need to do is start by calling one of the &amp;lt;tt&amp;gt;XPathEditor&amp;lt;/tt&amp;gt; methods that returns an &amp;lt;tt&amp;gt;XmlWriter&amp;lt;/tt&amp;gt;. These include the following, which differ on where each places the inserted XML:&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;tt&amp;gt;AppendChild( )&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Adds a new element inside the current element, after all existing child elements.&lt;br /&gt;
;&amp;lt;tt&amp;gt;PrependChild( )&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Adds a new element inside the current element, before any existing child elements.&lt;br /&gt;
;&amp;lt;tt&amp;gt;InsertAfter( )&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Adds a new element after the current element (and at the same level).&lt;br /&gt;
;&amp;lt;tt&amp;gt;InsertBefore( )&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Adds a new element just before the current element (and at the same level).&lt;br /&gt;
&lt;br /&gt;
[[Visual Basic 2005: A Developer's Notebook/Files, Databases, and XML#vbadn-CHP-5-EX-13|Example 5-13]] uses the &amp;lt;tt&amp;gt;AppendChild()&amp;lt;/tt&amp;gt; method to add a new product to the product list XML document.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;vbadn-CHP-5-EX-13&amp;quot;&amp;gt;&lt;br /&gt;
'''Example 5-13. Using the AppendChild( ) method to add a new element to an XML document'''&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;Imports System.Xml.XPath&lt;br /&gt;
Imports System.Xml&lt;br /&gt;
    &lt;br /&gt;
Module XPathNavigatorTest&lt;br /&gt;
    &lt;br /&gt;
    Sub Main( )&lt;br /&gt;
        ' Load the document.&lt;br /&gt;
        Dim Doc As New XPathDocument(My.Computer.FileSystem.CurrentDirectory &amp;amp; _&lt;br /&gt;
          &amp;quot;\ProductList.xml&amp;quot;)&lt;br /&gt;
    &lt;br /&gt;
        ' Create a new product.&lt;br /&gt;
        Dim XPathEditor As XPathNavigator = Doc.CreateEditor( )&lt;br /&gt;
        XPathEditor.MoveToRoot( )&lt;br /&gt;
        XPathEditor.MoveToFirstChild( )&lt;br /&gt;
    &lt;br /&gt;
        ' Use the XmlWriter to add a new &amp;lt;Product&amp;gt; complete with&lt;br /&gt;
        ' all child elements.&lt;br /&gt;
        Dim Writer As XmlWriter = XPathEditor.AppendChild&lt;br /&gt;
    &lt;br /&gt;
        ' Insert the opening &amp;lt;Product&amp;gt; tag.&lt;br /&gt;
        Writer.WriteStartElement(&amp;quot;Product&amp;quot;, _&lt;br /&gt;
          &amp;quot;http://www.ibuyspy.com/ProductCatalog&amp;quot;)&lt;br /&gt;
    &lt;br /&gt;
        ' The WriteElementString( ) method inserts a whole element at once.&lt;br /&gt;
        Writer.WriteElementString(&amp;quot;ProductID&amp;quot;, &amp;quot;999&amp;quot;)&lt;br /&gt;
        Writer.WriteElementString(&amp;quot;ModelName&amp;quot;, &amp;quot;Rubber Pants&amp;quot;)&lt;br /&gt;
        Writer.WriteElementString(&amp;quot;ModelNumber&amp;quot;, &amp;quot;NOZ999&amp;quot;)&lt;br /&gt;
        Writer.WriteElementString(&amp;quot;UnitCost&amp;quot;, &amp;quot;12.99&amp;quot;)&lt;br /&gt;
        Writer.WriteElementString(&amp;quot;CategoryName&amp;quot;, &amp;quot;Clothing&amp;quot;)&lt;br /&gt;
    &lt;br /&gt;
        ' Insert the closing &amp;lt;/Product&amp;gt; tag and close the writer.&lt;br /&gt;
        Writer.WriteEndElement( )&lt;br /&gt;
        Writer.Close( )&lt;br /&gt;
    &lt;br /&gt;
        ' Show changes.&lt;br /&gt;
        XPathEditor.MoveToRoot( )&lt;br /&gt;
        Console.WriteLine(XPathEditor.OuterXml)&lt;br /&gt;
    End Sub&lt;br /&gt;
    &lt;br /&gt;
End Module&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Running [[Visual Basic 2005: A Developer's Notebook/Files, Databases, and XML#vbadn-CHP-5-EX-13|Example 5-13]] generates the following XML, which is displayed in the console window and saved to the newly generated XML file:&lt;br /&gt;
&lt;br /&gt;
 ...&lt;br /&gt;
   &amp;lt;Product&amp;gt;&lt;br /&gt;
     &amp;lt;ProductID&amp;gt;999&amp;lt;/ProductID&amp;gt;&lt;br /&gt;
     &amp;lt;ModelName&amp;gt;Rubber Pants&amp;lt;/ModelName&amp;gt;&lt;br /&gt;
     &amp;lt;ModelNumber&amp;gt;NOZ999&amp;lt;/ModelNumber&amp;gt;&lt;br /&gt;
     &amp;lt;UnitCost&amp;gt;12.99&amp;lt;/UnitCost&amp;gt;&lt;br /&gt;
     &amp;lt;CategoryName&amp;gt;Clothing&amp;lt;/CategoryName&amp;gt;&lt;br /&gt;
   &amp;lt;/Product&amp;gt;&lt;br /&gt;
 ...&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;note&amp;quot;&amp;gt;&lt;br /&gt;
'''Note'''&lt;br /&gt;
&lt;br /&gt;
''You can create multiple navigator and editor objects to work with the same XPathDocument. However, the editors don't perform any locking, so you can't edit an XPathDocument on multiple threads at the same time unless you take your own safeguards''.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== What about... ===&lt;br /&gt;
&lt;br /&gt;
...validating your XML? The &amp;lt;tt&amp;gt;XPathNavigator&amp;lt;/tt&amp;gt; and the &amp;lt;tt&amp;gt;XmlWriter&amp;lt;/tt&amp;gt; both force you to write valid XML. However, it's also important to check XML documents to make sure they match specific rules. The best tool for this task is an XML ''schema document'' that defines the elements, structure, data types, and constraints for a document.&lt;br /&gt;
&lt;br /&gt;
The actual schema standard is beyond the scope of this chapter. (For a good introduction, refer to the tutorial at ''http://www.w3schools.com/schema''.) However, assuming you have a schema for your XML, you can validate your document at any time by calling &amp;lt;tt&amp;gt;XPathNavigator.CheckValidity()&amp;lt;/tt&amp;gt;. This method returns &amp;lt;tt&amp;gt;True&amp;lt;/tt&amp;gt; if the document conforms to the schema. Here's how to do it:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;' Load the document.&lt;br /&gt;
Dim Doc As New XPathDocument(&amp;quot;c:\ProductList.xml&amp;quot;)&lt;br /&gt;
    &lt;br /&gt;
' (Make updates).&lt;br /&gt;
    &lt;br /&gt;
' Load the schema.&lt;br /&gt;
' Technically, you can load a collection of schemas,&lt;br /&gt;
' one for each namespace in the document that you want to validate.&lt;br /&gt;
Dim Schemas As New XmlSchemaSet( )&lt;br /&gt;
Schemas.Add(&amp;quot;http://www.ibuyspy.com/ProductCatalog&amp;quot;, &amp;quot;c:\ProductListSchema.xsd&amp;quot;)&lt;br /&gt;
Schemas.Compile( )&lt;br /&gt;
    &lt;br /&gt;
' Validate with the schema.&lt;br /&gt;
' Instead of submitting a null reference (Nothing), you can supply&lt;br /&gt;
' a delegate that points to a callback method that will be triggered&lt;br /&gt;
' every time an error is found when the validation check is performed.&lt;br /&gt;
Dim Valid As Boolean&lt;br /&gt;
Valid = Doc.CreateNavigator( ).CheckValidity(Schemas, Nothing)&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Where can I learn more? ===&lt;br /&gt;
&lt;br /&gt;
For more information about editing the &amp;lt;tt&amp;gt;XPathDocument&amp;lt;/tt&amp;gt;, look up the &amp;quot;XPathNavigator class&amp;quot; index entry in the MSDN Help. If you've used earlier betas of .NET 2.0, which included the same features in a different class ( &amp;lt;tt&amp;gt;XPathEditableNavigator&amp;lt;/tt&amp;gt;), you may want to refer to ''http://blogs.msdn.com/dareobasanjo/archive/2004/09/03/225070.aspx'' for some explanation straight from Microsoft bloggers.&lt;/div&gt;</summary>
		<author><name>Docbook2Wiki</name></author>	</entry>

	</feed>