Professional ASP.NET 4 in C and VB

www.it-ebooks.info Related Wrox Books Beginning ASP.NET 4: in C# and VB ISBN: 978-0-470-50221-1 This introductory book offers helpful examples in a st...

7 downloads 102 Views 53MB Size


C#



One of the more interesting properties in the sample is the DriveType enumeration. This read-only enumeration tells you what the drive type is, for example CD-ROM, Fixed, Ram, or Removable. Figure 24-1 shows you what the page looks like when you view it in a browser. You can also enumerate through all the drives on the local file system by using the DriveInfo’s static GetDrives() method, which will provide you an array of DriveInfo objects. Listing 24-2 shows an example of enumerating through the local file system drives and adding each drive as a root node to a TreeView control.

Figure 24-1

Listing 24-2:  Enumerating through local file system drives

VB

Enumerate Local System Drives


C#



Notice that, in this sample, the drive object’s read-only IsReady property is used to test whether the drive is accessible. If you are enumerating drives, testing for this before attempting to access any of the other drive properties is always a good idea because removable drives and network drives may not always be available when your code is executed. Figure 24-2 shows what the page looks like when viewed in the browser.

The Directory and DirectoryInfo Classes You can build on the previous section’s examples and add the ability to browse through the system’s directory structure by using the System .IO namespace Directory and DirectoryInfo classes. .NET provides these classes for working with file system directories. The Directory class exposes static methods you can use to create, move, and delete any directory while the DirectoryInfo class represents a specific directory. The DirectoryInfo class allows you to perform the same actions as the Directory class and adds methods that allow you to enumerate the directories, child directories, and files.

Figure 24-2

Continuing the sample from Listing 24-2, Listing 24-3 shows you how to use the GetDirectories() method of the DirectoryInfo class to create a method that enumerates a directory’s children and adds them to a TreeView control to create a small directory browser. Listing 24-3:  Enumerating file system directories

Enumerate a Directory


www.it-ebooks.info

Working with Drives, Directories, and Files  ❘ 

933



C#



Figure 24-3 shows what the page should look like in the browser. You should now be able to browse the directory tree, much as you do in Windows Explorer, by opening and closing the TreeView nodes.

Figure 24-3

Notice that the example continuously creates new instances of the DirectoryInfo class each time the method executes in order to continue to enumerate the directory tree. You could also extend this example by displaying some additional properties as part of the Node text, such as the CreationTime or Attributes. One important item to note when using the GetDirectories() method is that using it can come with some performance costs. This is because each time the GetDirectories() method is called it automatically allocates an array structure large enough to accommodate all the child directories of the parent directory and populates that array with DirectoryInfo objects. The method will not return until this work has completed. If you execute this method on a directory with a large number of children, or are accessing directories exposed by a network share, this allocation and population can be slow, effectively locking your application while the work is performed. In those scenarios you can instead choose to use the EnumerateDirectories() method. This method returns an IEnumerable rather than an array, and therefore does not have the performance costs of the array allocation or population. To use the EnumerateDirectories() method, simply substitute it in Listing 24-3 for the () method call: VB

For Each d As System.IO.DirectoryInfo In directory.EnumerateDirectories()

C#

foreach (System.IO.DirectoryInfo d in directory.EnumerateDirectories())

www.it-ebooks.info

Working with drives, directories, and files

❘ 935

To perform a specific action such as creating, reading properties from, or deleting a directory, you don’t have to create an instance of the DirectoryInfo class. You can instead use the static methods exposed by the Directory class. Rather than creating an object instance that represents a specific path, the static methods exposed by the Directory class generally require you to pass the path as a method parameter. Listing 24 - 4 shows how you can use the static methods exposed by the Directory class to create, read properties from, and delete a directory. Remember to be very careful when deleting a folder from your hard drive. Permanently deleting important data from your system or changing the permissions of a resource is possible, and would result in your losing the ability to access the resource.

lisTing 24-4: Working with the static methods of the directory class Using Static Methods
Creation Time:
Last Access Time:
Last Write Time:


continues

www.it-ebooks.info

936  ❘  Chapter 24   File I/O and Streams

Listing 24-4  (continued)

C#



Running this sample creates a new directory called “Wrox” within the Web site. The sample then accesses and displays the new directory’s Creation Time, Last Access Time, and Last Write Time properties. Finally, the “Wrox” directory is deleted.

Using Relative Paths and Setting and Getting the Current Directory When an ASP.NET page is executed, the thread used to execute the code that generates the page by default has a current working directory. It uses this directory as its base directory if you have specified relative paths in your application. If you pass a relative filename into any System.IO class, the file is assumed to be located relative to the current working directory. The default working directory for the ASP.NET Development Server is a directory under your Visual Studio install root. If you installed Visual Studio in C:\Program Files, your ASP.NET Development Server working directory would be C:\Program Files\Common Files\Microsoft Shared\DevServer\10.0. You can find the location of your working directory by using the Directory class’s GetCurrentDirectory() method. In addition, you can change the current working directory using the Directory class’s SetCurrentDirectory() method. Listing 24-5 shows you how to set and then display your working directory. Listing 24-5:  Setting and displaying the application’s working directory

VB

Set and Display the Working Directory

www.it-ebooks.info

Working with Drives, Directories, and Files  ❘ 

937

Old Working Directory:
New Working Directory:


C#



Note that the directory parameter you specify in the SetCurrentDirectory() method must already exist; otherwise, ASP.NET will throw an exception. Knowing this, you should use the Exists() method of the Directory class to ensure the directory you are specifying does, in fact, already exist before you try to change the working directory. When you execute the code in Listing 24-5, you should see that it displays the original working directory, and then displays the new working directory after you change it. Figure 24-4 shows what the page looks like when executed.

Figure 24-4

File and FileInfo Now that you can display and browse a directory tree, you can expand the previous example even further by adding the files located in the currently selected directory to the display. The simplest way to display the directories files is to bind an array of FileInfo objects to a GridView. You can use the GetFiles() method of the DirectoryInfo class to obtain such an array. The FileInfo object, like the DirectoryInfo object, represents a single element in the file system (in this case a file) and allows you to perform actions on that file and access properties of the file. If you want to display only the filenames, you could use the Directory class’s GetFiles() method, which returns a simple string array of filenames. Listing 24-6 shows how to use the TreeView control’s SelectedNodeChanged event to bind your GridView with the file information.

www.it-ebooks.info

938  ❘  Chapter 24   File I/O and Streams

Listing 24-6:  Binding a GridView to directory files Binding a Gridview


continues

www.it-ebooks.info

940  ❘  Chapter 24   File I/O and Streams

Listing 24-6  (continued)

C#



Figure 24-5 shows what your Web page looks like after you have selected a directory and the grid has been bound to the FileInfo array.

Figure 24-5

The GetFiles() method, like the GetDirectories() method described earlier in this section, can come with some performance costs which you should be aware of. Like the GetDirectories() method, the GetFiles() method automatically allocates an array structure large enough to accommodate all the child files of the parent directory and populates that array with FileInfo objects. The method will not return

www.it-ebooks.info

941

942  ❘  Chapter 24   File I/O and Streams

until this work has completed. If you execute this method on a directory with a large number of children, or are accessing directories exposed by a network share, this allocation and population can be slow, effectively locking your application while the work is performed. In those scenarios you can instead choose to use the EnumerateFiles() method. This method returns an IEnumerable rather than an array, and therefore does not have the performance costs of the array allocation or population. To use the EnumerateFiles() method simply substitute it in Listing 24-6 for the GetFiles() method call. Although data binding to the GridView requires little work, you can also enumerate through the FileInfo array to display the information. Listing 24-7 shows you how to enumerate through the FileInfo array and display the properties to the page. Listing 24-7:  Manually enumerating directory files

VB

Enumerate a FileInfo
Name Last Write Time Attributes


C#



Listing 24-7 also shows that you can provide a file filter to the GetFiles() method. This allows you to limit the results from the method to specific file extensions or to files matching a specific filename portion.

Working with Paths Although working with files and directories has been rather easy, even going all the way back to good old ASP, one of the most problematic areas has always been working with paths. Many lines of code have been written by developers to deal with concatenating partial paths together, making sure files have extensions, evaluating those extensions, stripping filenames off paths, and even more. Thankfully, the .NET Framework provides you with a class just for dealing with paths. The System .IO.Path class exposes a handful of static methods that make dealing with paths a snap. Table 24-1 lists the static methods exposed by the Path class. Table 24-1 Method

Description

ChangeExtension

Changes the extension of the provided path string to the provided new extension.

Combine

Returns a single combined path from two partial path strings.

GetDirectoryName

Returns the directory or directories of the provided path.

GetExtension

Returns the extension of the provided path.

GetFileName

Returns the filename of the provided path.

GetFileNameWithoutExtension

Returns the filename without its extension of the provided path.

GetFullPath

Given a non-rooted path, returns a rooted pathname based on the current working directory. For example, if the path passed in is “temp” and the current working directory is C:\MyWebsite, the method returns C:\MyWebsite\temp.

GetInvalidFileNameChars

Returns an array of characters that are not allowed in filenames for the current system.

GetInvalidPathChars

Returns an array of characters that are not allowed in pathnames for the current system.

GetPathRoot

Returns the root path.

GetTempFileName

Returns a temporary filename, located in the temporary directory returned by GetTempPath.

GetTempPath

Returns the temporary directory name.

HasExtension

Returns a Boolean value indicating whether a path has an extension.

IsPathRooted

Returns a Boolean value indicating whether a path is rooted.

www.it-ebooks.info

944



chaPTer 24 File i/o And streAms

As an example of using the Path class, the application shown in Figure 24 - 6 lets you enter a path and then displays the component parts of the path such as the root path (logical drive), the directory, fi lename, and extension. The GetInvalidPathChars and GetInvalidFileNameChars methods return an array of characters that are not allowed in path and filenames, respectively. Although the specific invalid characters are dependent on the platform the application is running on, the arrays returned by these methods will most likely contain elements such as non printable characters, special Unicode characters, or characters from non -Latin – based character sets. The characters that your browser is capable of rendering will depend on your specific platform setup.

figure 24-6

The code in Listing 24 -8 shows how the various methods and constant properties of the Path class have been used to create the application shown in Figure 24 - 6. lisTing 24-8: Using the Path class

VB

Using the Path Class
Working with the Path Class

Enter a path name:


Root Path =
Directory =
Filename =
Filename (without extension) =
Extension =

Temporary Directory =
Directory Separator Character =

continues

www.it-ebooks.info

946  ❘  Chapter 24   File I/O and Streams

Listing 24-8  (continued)
Alt Directory Separator Character =
Volume Separator Character =
Path Separator Character =
Invalid Path Characters =
Invalid FileName Characters =


C#

<%@ Import Namespace="System.IO" %>

www.it-ebooks.info

Working with drives, directories, and files

❘ 947

file and directory Properties, attributes, and access control lists Finally, this section explains how you can access and modify fi le and directory properties, attributes, and Access Control Lists. Samples in this section use a simple text file called TextFile.txt to demonstrate the concepts. You can either create this file or substitute your own file in the sample code. A sample of this file is also included in the downloadable source code for this chapter. The samples assume the file has been added to the Web site and use the Server .MapPath method to determine the full filepath.

Properties and attributes Files and directories share certain properties that you can use to determine the age of a file or directory, when it was last modified, and what attributes have been applied. You can view these properties by opening the file’s Properties dialog. You open this dialog from Windows Explorer by either right- clicking on the fi le and selecting Properties from the context menu, or selecting Properties from the File menu. Figure 24 -7 shows the fi le’s Properties window for the text document. Both the DirectoryInfo and the FileInfo classes let you access these properties and modify them. Listing 24 -9 shows you an example of displaying the fi le properties.

figure 24-7

lisTing 24- 9: displaying and modifying the file properties

VB



www.it-ebooks.info

continues

948  ❘  Chapter 24   File I/O and Streams

Listing 24-9  (continued) Using the Path Class
Location:
Size:
Created:
Modified:
Accessed:
Attributes:


C#



Access Control Lists Although getting the properties and attributes is useful, what many developers need is the capability to actually change the Access Control Lists, or ACLs — pronounced Ackels — on directories and files. ACLs are the way resources such as directories and files are secured in the NTFS file system, which is the file system used by most recent versions of Windows. You can view a file’s ACLs by selecting the Security tab from the file’s Properties dialog. Figure 24-8 shows the ACLs set for the TextFile.txt file you created. Using the System.AccessControl namespace in the .NET Framework, you can query the file system for the ACL information and display it in a Web page. Listing 24-10 demonstrates how to use the Access Control classes to retrieve the ACLs for the TextFile.txt file included in the project.

Figure 24-8

www.it-ebooks.info

Working with Drives, Directories, and Files  ❘ 

949

Listing 24-10:  Access Control List information

VB

Displaying ACL Information

File Owner:

Access Rules:
Control Type Identity Inheritance Flags

continues

www.it-ebooks.info

950  ❘  Chapter 24   File I/O and Streams

Listing 24-10  (continued) Is Inherited Propagation Flags File System Rights



C#



Figure 24-9 shows what the page looks like when it is executed. Note that the Identity column might be different depending on who you are logged in as when you run the page and what security mode the application is running under (Integrated Windows Authentication, Basic, or Anonymous).

www.it-ebooks.info

Working with Drives, Directories, and Files  ❘ 

Figure 24-9

Now it’s time to take a look at actually modifying the ACL lists. In this example, you give a specific user explicit Full Control rights over the TextFile.txt file. You can use either an existing user or create a new test User account in Windows to run this sample. Listing 24-11 shows how to add an access rule to the TextFile.txt file. Listing 24-11:  Adding a rule to the Access Control List

VB

Dim sec As System.Security.AccessControl.FileSecurity = System.IO.File.GetAccessControl( Server.MapPath("TextFile.txt")) sec.AddAccessRule( New System.Security.AccessControl.FileSystemAccessRule( "WIN7\TestUser", System.Security.AccessControl.FileSystemRights.FullControl, System.Security.AccessControl.AccessControlType.Allow ) ) System.IO.File.SetAccessControl(Server.MapPath("TextFile.txt"),sec)

C#

System.Security.AccessControl.FileSecurity sec = System.IO.File.GetAccessControl( Server.MapPath("TextFile.txt")); sec.AddAccessRule( new System.Security.AccessControl.FileSystemAccessRule( @"WIN7\TestUser", System.Security.AccessControl.FileSystemRights.FullControl, System.Security.AccessControl.AccessControlType.Allow ) ); System.IO.File.SetAccessControl(Server.MapPath("TextFile.txt"),sec);

The sample starts by getting the collection of existing security settings for the file. Next, using the AddAccessRule method, a new FileSystemAccessRule is created and added to the files collection of security settings. Creating a new FileSystemAccessRule requires you to provide three constructor parameters, the user you want to assign this rule to (provided in DOMAIN\USERNAME format), the rights you want to assign to this rule, and the AccessControlType you want to give this rule.

www.it-ebooks.info

951

952  ❘  Chapter 24   File I/O and Streams

You can specify multiple rights to assign to the rule by using a bitwise Or operator, as shown in the following: new System.Security.AccessControl.FileSystemAccessRule( "DEMO7\TestUser", System.Security.AccessControl.FileSystemRights.Read | S ystem.Security.AccessControl.FileSystemRights.Write, System.Security.AccessControl.AccessControlType.Allow )

The preceding rule allows the TestUser account to read or write to the associated file system asset. You can also deny a specific user these rights by changing the AccessControlType value to Deny. After running Listing 24-11, take a look at the Security tab in the file’s Properties dialog, and you should see that the user has been added to the Access Control List and allowed Full Control. Figure 24-10 shows what the dialog should look like.

Figure 24-10

To remove the ACL you just added you run essentially the same code, but use the RemoveAccessRule method rather than the AddAccessRule method. Listing 24-12 shows this code. Listing 24-12:  Removing the rule from the Access Control List

VB

Dim sec As System.Security.AccessControl.FileSecurity = System.IO.File.GetAccessControl( Server.MapPath("TextFile.txt")) sec.RemoveAccessRule( new System.Security.AccessControl.FileSystemAccessRule( "WIN7\TestUser", System.Security.AccessControl.FileSystemRights.FullControl, System.Security.AccessControl.AccessControlType.Allow ) ) System.IO.File.SetAccessControl(Server.MapPath("TextFile.txt"),sec)

C#

System.Security.AccessControl.FileSecurity sec = System.IO.File.GetAccessControl( Server.MapPath("TextFile.txt")); sec.RemoveAccessRule(

www.it-ebooks.info

Reading and Writing Files  ❘ 

953

new System.Security.AccessControl.FileSystemAccessRule( @"WIN7\TestUser", System.Security.AccessControl.FileSystemRights.FullControl, System.Security.AccessControl.AccessControlType.Allow ) ); System.IO.File.SetAccessControl(Server.MapPath("TextFile.txt"),sec);

If you open the file Properties dialog again, you see that the user has been removed from the Access Control List.

Reading and Writing Files Now that you have learned how to manage the files on the local system, this section shows you how to use the .NET Framework to perform input/output (I/O) operations, such as reading and writing, on those files. The .NET Framework makes performing I/O very easy because it uses a common model of reading or writing I/O data; so regardless of the source, virtually the same code can be used. The model is based on two basic concepts: stream classes and reader/writer classes. Figure 24-11 shows the basic I/O model .NET uses and how streams, readers, and writers work together to make it possible to transfer data to and from any number of sources in any number of formats. Note that the diagram shows only some of the streams and reader/writer pairs in the .NET Framework. Physical Hardware File System

System Memory

Network

Stream Classes Stream Class FileStream

StringReader

NetworkStream

TextReader

StreamReader

MemoryStream

Reader Classes

BinaryReader

UnmanagedMemoryStream

StringWriter

TextWriter

StreamWriter

Writer Classes

BinaryWriter

Figure 24-11

In this section, you dive deeper into learning how streams, readers, and writers work and how .NET makes it easy to use them to transfer data.

Streams Regardless of the type of I/O operation you are performing in .NET, if you want to read or write data you eventually use a stream of some type. Streams are the basic mechanism .NET uses to transfer data to and from its underlying source, be it a file, communication pipe, or TCP/IP socket. The Stream class provides the basic functionality to read and write I/O data, but because the Stream class is marked as abstract, you most likely

www.it-ebooks.info

954  ❘  Chapter 24   File I/O and Streams

need to use one of the several classes derived from Stream. Each Stream derivation is specialized to make transferring data from a specific source easy. Table 24-2 lists some of the classes derived from the Stream class. Table 24-2 Class

Description

System.IO.FileStream

Reads and writes files on a file system, as well as other filerelated operating system handles (including pipes, standard input, standard output, and so on).

System.IO.MemoryStream

Creates streams that have memory as a backing store instead of a disk or a network connection. This can be useful in eliminating the need to write temporary files to disk or to store binary blob information in a database.

System.IO.UnmanagedMemoryStream

Supports access to unmanaged memory using the existing stream-based model and does not require that the contents in the unmanaged memory be copied to the heap.

System.IO.BufferedStream

Extends the Stream class by adding a buffering layer to read and write operations on another stream. The stream performs reads and writes in blocks (4096 bytes by default), which can result in improved efficiency.

System.Net.Sockets.NetworkStream

Implements the standard .NET Framework stream to send and receive data through network sockets. It supports both synchronous and asynchronous access to the network data stream.

System.Security.Cryptography. CryptoStream

Enables you to read and write data through cryptographic transformations.

System.IO.Compression.GZipStream

Enables you to compress data using the GZip data format.

System.IO.Compression. DeflateStream

Enables you to compress data using the Deflate algorithm. For more information, see the RFC 1951: DEFLATE 1.3 Specification.

System.Net.Security. NegotiateStream

Uses the Negotiate security protocol to authenticate the client, and optionally the server, in client-server communication.

System.Net.Security.SslStream

Necessary for client-server communication that uses the Secure Socket Layer (SSL) security protocol to authenticate the server and optionally the client.

As an example, you can use the FileStream to read a local system file from disk. Listing 24-13 shows how you can use the FileStream to read the contents of the TextFile.txt file used in previous sections of this chapter. Listing 24-13:  Using a FileStream to read a system file

VB



www.it-ebooks.info

reading and Writing files

❘ 955

Reading a Text File


C#



Several items of note appear in this code. First, notice that you are creating a byte array the length of the stream, using the Length property to properly size the array, and then passing it to the Read method. The Read method fi lls the byte array with the stream data, in this case reading the entire stream into the byte array. If you want to read only a chunk of the stream or to start at a specific point in the stream simply change the value of the parameters you pass to the Read method. Streams use byte arrays as the basic means of transporting data to and from the underlying data source. You use a byte array to read data in this sample and, later in the chapter, you learn how to create a byte array that contains data you can write to a stream. Second, note that you are explicitly closing the FileStream using the Close method. Streams must always be explicitly closed in order to release the resources they are using, which in this case is the file. Failing to explicitly close the stream can cause memory leaks, and it may also deny other users and applications access to the resource. A good way to ensure that your streams will always be closed after you are done using them is to wrap them in a Using statement. Using automatically calls the stream object’s Dispose method when the Using statement is closed. For the stream object, calling the Dispose method also automatically calls the stream’s Close method. Utilizing the Using statement with stream objects is a good way to ensure that even if you do forget to explicitly add a call to close the stream, the object will be closed and the underlying resources released before the object is disposed. Listing 24 -14 demonstrates how to modify the code from Listing 24 -13 to use a Using statement. lisTing 24-14: Using a Using statement when reading a file

VB

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Using fs As New System.IO.FileStream( Server.MapPath("TextFile.txt"), System.IO.FileMode.Open) Dim data(fs.Length) As Byte

continues

www.it-ebooks.info

956  ❘  Chapter 24   File I/O and Streams

Listing 24-14  (continued) fs.Read(data, 0, fs.Length) Me.lblResult.Text = ASCIIEncoding.Default.GetString(data) End Using End Sub

C#

protected void Page_Load(object sender, EventArgs e) { using (System.IO.FileStream fs = new System.IO.FileStream( Server.MapPath("TextFile.txt"), System.IO.FileMode.Open)) { byte[] data = new byte[fs.Length]; fs.Read(data, 0, (int)fs.Length); this.lblResult.Text = ASCIIEncoding.Default.GetString(data); } }

When using the FileStream to read files, you must pass two parameters to the FileStream constructor: the path to the file you want to read and a value from the FileMode enumeration indicating the type of access you want to use when opening the file. The FileMode enumeration lets you specify how the stream should be opened — for reading, writing, or both reading and writing. Thinking about how you will use the opened file can become very important when designing file access in your application. Here are some access issues you might want to consider when working with files using FileStream: ➤➤

Will you be reading, writing, or both?

➤➤

Are you creating a new file, or appending or truncating an existing file?

➤➤

Should other programs be allowed to access the file while you are using it?

➤➤

How are you going to read or write the data in the file? Are you looking for a specific location in the file, or simply reading the entire file from beginning to end?

The FileStream constructor includes a number of additional overloads that let you explicitly specify how you want to use the file. These overloads let you provide values from four enumerations found in the System.IO namespace that can help you set fine-grain control for how the FileStream accesses your file: ➤➤

FileMode: The FileMode enumeration lets you control whether the file is appended, truncated, ­created, or opened.

➤➤

FileAccess:  The FileAccess enumeration controls whether the file is opened for reading, writing, or both.

➤➤

FileOptions:  The FileOptions enumeration controls several other miscellaneous options, such as random or sequential access, file encryption, or asynchronous file writing.

➤➤

FileShare: The FileShare enumeration controls the access that other users and programs have to the file while your application is using it.

Listing 24-15 shows how you can use these enumerations in the FileStream constructor to configure the stream to allow you to append additional data to the text file you created earlier. Providing the Append value from the FileMode enumeration and the Write value from the FileAccess enumeration tells the stream exactly what access to allow.

www.it-ebooks.info

Reading and Writing Files  ❘ 

957

Listing 24-15:  Using I/O enumerations to control file behavior when writing a file

VB

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Dim fs As New System.IO.FileStream(Server.MapPath("TextFile.txt"), System.IO.FileMode.Append, System.IO.FileAccess.Write, System.IO.FileShare.Read, 8, System.IO.FileOptions.None) Dim data() As Byte = System.Text.Encoding.ASCII.GetBytes( "This is an additional string") fs.Write(data, 0, data.Length) fs.Flush() fs.Close() Me.lblResult.Text = ASCIIEncoding.Default.GetString(data) End Sub

C#

protected void Page_Load(object sender, EventArgs e) { System.IO.FileStream fs = new System.IO.FileStream(Server.MapPath("TextFile.txt"), System.IO.FileMode.Append, System.IO.FileAccess.Write, System.IO.FileShare.Read, 8, System.IO.FileOptions.None); byte[] data = System.Text.Encoding.ASCII.GetBytes( "This is an additional string"); fs.Write(data, 0, data.Length); fs.Flush(); fs.Close(); this.lblResult.Text = ASCIIEncoding.Default.GetString(data); }

You can write to the stream by encoding a string containing the information you want to write to a byte array and then using the stream’s Write method to write the byte array to the FileStreams buffer. After the write is complete, you can use the Flush method to instruct the FileStream to clear its buffer, causing the buffered data to be committed to the underlying data store (in this case the text file). Finally, as described earlier, you must close the FileStream, releasing any resources it is using. If you open the TextFile.txt file in Notepad, you should see your string has been appended to the existing text in the file. Using the Flush method in this scenario is optional because the Close method also calls Flush internally to commit the data to the data store. However, because the Flush method does not release the FileStream resources as Close does, it can be very useful if you are going to perform multiple write operations and do not want to release and then reacquire the resources for each write operation. As you can see, reading and writing to files using the .NET Framework is really quite easy. The good thing is that, as mentioned earlier, because .NET uses the same basic Stream model for a variety of data stores, you can use these same techniques for reading and writing to any of the Stream-derived classes shown earlier in this section. Listing 24-16 shows how you can use the same basic code to write to a MemoryStream, and Listing 24-17 demonstrates communicating with an NNTP server using the NetworkStream. Listing 24-16:  Writing to a MemoryStream

VB

C#

Dim data() As Byte = System.Text.Encoding.ASCII.GetBytes("This is a string") Dim ms As New System.IO.MemoryStream() ms.Write(data, 0, data.Length) ms.Close() byte[] data = System.Text.Encoding.ASCII.GetBytes("This is a string");

continues

www.it-ebooks.info

958  ❘  Chapter 24   File I/O and Streams

Listing 24-16  (continued) System.IO.MemoryStream ms = new System.IO.MemoryStream(); ms.Write(data, 0, data.Length); ms.Close();

Listing 24-17:  Reading from a NetworkStream

VB

Writing a Text File

www.it-ebooks.info

Reading and Writing Files  ❘ 



C#



Notice that the concept in both examples is virtually identical. In Listing 24-16, you create a byte array containing the data, create a MemoryStream object, and then use the Write method to write the data to the stream. Listing 24-17 performs the same basic actions, writing the “LIST” command to the NetworkStream, but adds a loop to read the results returned from the NNTP server in 1024-byte chunks.

www.it-ebooks.info

959

960  ❘  Chapter 24   File I/O and Streams

Readers and Writers Other main parts of I/O in the .NET Framework are Reader and Writer classes. These classes help abstract away the reading and writing of individual bytes to and from Streams as you were doing in the previous section, instead enabling you to focus more on the actual data you are working with, letting the framework transform the individual bytes into the appropriate data types. The .NET Framework provides a wide variety of reader and writer classes, each designed for reading or writing according to a specific set of rules. Table 24-3 shows a partial list of the readers available in the .NET Framework and the corresponding writer classes. Table 24-3 Class

Description

System.IO.TextReader

Abstract class that enables the reading of a sequential series of characters.

System.IO.StreamReader

Reads characters from a byte stream. Derived from TextReader.

System.IO.StringReader

Reads textual information as a stream of in-memory characters. Derived from TextReader.

System.IO.BinaryReader

Reads primitive data types as binary values from a stream.

System.Xml.XmlTextReader

Provides fast, non-cached, forward-only access to XML.

System.IO.TextWriter

Abstract class that enables the writing of a sequential series of characters.

System.IO.StreamWriter

Writes characters to a stream. Derived from TextWriter.

System.IO.StringWriter

Writes textual information as a stream of in-memory characters. Derived from TextWriter.

System.IO.BinaryWriter

Writes primitive data types in binary to a stream.

System.Xml.XmlTextWriter

Provides a fast, non-cached, forward-only way of generating XML streams or files.

To see a simple example of how the readers and writers abstract reading and writing data, Listing 24-18 shows you how to use the StreamReader and StreamWriter classes to write a string to a text file and then read the contents of that text file. Listing 24-18:  Reading and writing a text file with a StreamReader

VB

Dim streamwriter As New System.IO.StreamWriter( System.IO.File.Open(MapPath("TextFile.txt"), System.IO.FileMode.Open)) streamwriter.Write("This is a string") streamwriter.Close() Dim reader As New System.IO.StreamReader( System.IO.File.Open(MapPath("TextFile.txt"), System.IO.FileMode.Open)) Dim tmp As String = reader.ReadToEnd() reader.Close()

C#

System.IO.StreamWriter streamwriter = new System.IO.StreamWriter( System.IO.File.Open(MapPath("TextFile.txt"), System.IO.FileMode.Open)); streamwriter.Write("This is a string"); streamwriter.Close();

www.it-ebooks.info

Reading and Writing Files  ❘ 

961

System.IO.StreamReader reader = new System.IO.StreamReader( System.IO.File.Open(MapPath("TextFile.txt"), System.IO.FileMode.Open)); string tmp = reader.ReadToEnd(); reader.Close();

Notice that when you create a StreamReader, you must pass an existing stream instance as a constructor parameter. The reader uses this stream as its underlying data source. In this sample, you use the File class’s static Open method to open a writable FileStream for your StreamWriter. Also notice that you no longer have to deal with byte arrays. The StreamReader takes care of converting the data to a type that’s more user-friendly than a byte array. In this case, you are using the ReadToEnd method to read the entire stream and convert it to a string. The StreamReader provides a number of different methods for reading data that you can use depending on exactly how you want to read the data, from reading a single character using the Read method, to reading the entire file using the ReadToEnd method. Figure 24-12 shows the results of your writing when you open the file in Notepad.

Figure 24-12

Now use the BinaryReader and BinaryWriter classes to read and write primitive types to a file. The BinaryWriter writes primitive objects in their native format, so in order to read them using the BinaryReader, you must select the appropriate Read method. Listing 24-19 shows you how to do that; in this case, you are writing a value from a number of different primitive types to the text file and then reading the same value. Listing 24-19:  Reading and writing binary data Dim binarywriter As New System.IO.BinaryWriter( System.IO.File.Create(MapPath("binary.dat")))

VB

binarywriter.Write("a string") binarywriter.Write(&H12346789ABCDEF) binarywriter.Write(&H12345678) binarywriter.Write("c"c) binarywriter.Write(1.5F) binarywriter.Write(100.2D) binarywriter.Close() Dim binaryreader As New System.IO.BinaryReader( System.IO.File.Open(MapPath("binary.dat"), System.IO.FileMode.Open)) Dim a As String = binaryreader.ReadString() Dim l As Long = binaryreader.ReadInt64() Dim i As Integer = binaryreader.ReadInt32() Dim c As Char = binaryreader.ReadChar() Dim f As Double = binaryreader.ReadSingle() Dim d As Decimal = binaryreader.ReadDecimal() binaryreader.Close()

C#

System.IO.BinaryWriter binarywriter = new System.IO.BinaryWriter( System.IO.File.Create(MapPath("binary.dat"))); binarywriter.Write("a string"); binarywriter.Write(0x12346789abcdef); binarywriter.Write(0x12345678);

continues

www.it-ebooks.info

962  ❘  Chapter 24   File I/O and Streams

Listing 24-19  (continued) binarywriter.Write('c'); binarywriter.Write(1.5f); binarywriter.Write(100.2m); binarywriter.Close(); System.IO.BinaryReader binaryreader = new System.IO.BinaryReader( System.IO.File.Open(MapPath("binary.dat"), System.IO.FileMode.Open)); string a = binaryreader.ReadString(); long l = binaryreader.ReadInt64(); int i = binaryreader.ReadInt32(); char c = binaryreader.ReadChar(); float f = binaryreader.ReadSingle(); decimal d = binaryreader.ReadDecimal(); binaryreader.Close();

If you open this file in Notepad, you will see that the BinaryWriter has written the nonreadable binary data to the file. Figure 24-13 shows what the content of the file looks like. The BinaryReader provides a number of different methods for reading various kinds of primitive types from the stream. In this sample, you use a different Read method for each primitive type that you write to the file. Finally, notice that the basic usage of both the StreamReader/ StreamWriter and BinaryReader/BinaryWriter classes is virtually identical. You can apply the same basic ideas to use any of the reader or writer classes.

Figure 24-13

Encodings The StreamReader by default attempts to determine the encoding format of the file. If one of the supported encodings such as UTF-8 or UNICODE is detected, it is used. If the encoding is not recognized, the default encoding of UTF-8 is used. Depending on the constructor you call, you can change the default encoding used and optionally turn off encoding detection. The following example shows how you can control the encoding that the StreamReader uses. StreamReader reader = new StreamReader(MapPath(TextFile.txt"), System.Text.Encoding.Unicode);

The default encoding for the StreamWriter is also UTF-8, and you can override it in the same manner as with the StreamReader class.

I/O Shortcuts Although knowing how to create and use streams is always very useful and worth studying, the .NET Framework provides you with numerous shortcuts for common tasks like reading and writing to files. For instance, if you want to read the entire file, you can simply use one of the static Read All methods of the File class. Using these methods, you cause .NET to handle the process of creating the Stream and StreamReader for you, and simply return the resulting string of data. This is just one example of the shortcuts that the .NET Framework provides. Listing 24-20 shows some of the others, with explanatory comments. Listing 24-20:  Using the static methods of the File and Directory classes

VB

' Opens a file and returns a FileStream Using stream1 As System.IO.FileStream = System.IO.File.Open(MapPath("TextFile.txt"), System.IO.FileMode.Open)

www.it-ebooks.info

Reading and Writing Files  ❘ 

963

End Using ' Opens a file and returns a StreamReader for reading the data Using stream2 As System.IO.StreamReader = System.IO.File.OpenText(MapPath("TextFile.txt")) End Using ' Opens a filestream for reading Using stream3 As System.IO.FileStream = System.IO.File.OpenRead(MapPath("TextFile.txt")) End Using ' Opens a filestream for writing Using stream4 As System.IO.FileStream = System.IO.File.OpenWrite(MapPath("TextFile.txt")) End Using ' Reads the entire file and returns a string of data Dim data As String = System.IO.File.ReadAllText(MapPath("TextFile.txt")) ' Using a LINQ query to locate specific lines within the Textfile Dim lines1 = From line In System.IO.File.ReadLines(MapPath("TextFile.txt")) Where line.Contains("laoreet") Select line ' Using a LINQ query to locate specific lines within the Textfile Dim lines2 = From line In System.IO.File.ReadLines(MapPath("TextFile.txt")) Where line.Contains("laoreet") Select line ' Appends the text data to a new text file System.IO.File.AppendAllText(MapPath("TextFile1.txt"), data) ' Appends the IEnumerable of strings to a new text file System.IO.File.AppendAllLines(MapPath("TextFile1.txt"), lines1) ' Writes the string of data to a new file System.IO.File.WriteAllText(MapPath("TextFile2.txt"), data) ' Writes an IEnumerable of strings to a new text file System.IO.File.WriteAllLines(MapPath("TextFile2.txt"), lines2)

C#

// Opens a file and returns a FileStream using (System.IO.FileStream stream1 = System.IO.File.Open(MapPath("TextFile.txt"), System.IO.FileMode.Open)) {} // Opens a file and returns a StreamReader for reading the data using (System.IO.StreamReader stream2 = System.IO.File.OpenText(MapPath("TextFile.txt"))) {} // Opens a filestream for reading using (System.IO.FileStream stream3 = System.IO.File.OpenRead(MapPath("TextFile.txt"))) {} // Opens a filestream for writing using (System.IO.FileStream stream4 = System.IO.File.OpenWrite(MapPath("TextFile.txt"))) { } // Reads the entire file and returns a string of data string data = System.IO.File.ReadAllText(MapPath("TextFile.txt")); // Using a LINQ query to locate specific lines within the Textfile

continues

www.it-ebooks.info

964  ❘  Chapter 24   File I/O and Streams

Listing 24-20  (continued) var lines1 = from line in System.IO.File.ReadLines(MapPath("TextFile.txt")) where line.Contains("laoreet") select line; // Using a LINQ query to locate specific lines within the Textfile var lines2 = from line in System.IO.File.ReadLines(MapPath("TextFile.txt")) where line.Contains("laoreet") select line; // Appends the text data to a new text file System.IO.File.AppendAllText(MapPath("TextFile1.txt"), data); //Appends the IEnumerable of strings to a new text file System.IO.File.AppendAllLines(MapPath("TextFile1.txt"), lines1); // Writes the string of data to a new file System.IO.File.WriteAllText(MapPath("TextFile2.txt"), data); // Writes an IEnumerable of strings to a new text file System.IO.File.WriteAllLines(MapPath("TextFile2.txt"), lines2);

An interesting portion of Listing 24-20 to call out is the use of the LINQ queries with the ReadLines method. Using the LINQ queries allows you to query the lines of the text file for specific values; however, note that when using LINQ in this manner you are executing queries against objects that implement IDisposable. This means that when you perform an action that causes the query results to be enumerated, the underlying TextReader used to read the file contents will be closed and attempts to access it further will result in an exception being thrown.

Compressing Streams Introduced in the .NET 2.0 Framework, the System.IO.Compression namespace includes classes for compressing and decompressing data using either the gzip or Deflate formats. Both compression formats use the Deflate compression algorithm and are exposed through classes derived from the Stream class. Using the classes is similar to using the other Stream classes shown in this chapter.

DeflateStream The DeflateStream class creates and extracts archives compressed using the Deflate algorithm, a lossless data compression algorithm that uses a combination of LZ77 algorithm and Huffman coding. Listing 24-21 shows an example of compressing your text file using the DeflateStream class. Listing 24-21:  Compressing a file using DeflateStream Dim filename As String = Server.MapPath("TextFile.txt")

VB

If System.IO.File.Exists(filename) Then Using infile As System.IO.FileStream = System.IO.File.Open(filename, System.IO.FileMode.Open), outfile As System.IO.FileStream = System.IO.File.Create( System.IO.Path.ChangeExtension(filename, "zip")) Using cstream As System.IO.Compression.DeflateStream =

www.it-ebooks.info

Reading and Writing Files  ❘ 

965

New System.IO.Compression.DeflateStream(outfile, System.IO.Compression.CompressionMode.Compress) Dim data(infile.Length) As Byte Dim counter As Integer = 0 counter = infile.Read(data, 0, data.Length) While (counter <> 0) cstream.Write(data, 0, counter) counter = infile.Read(data, 0, data.Length) End While End Using End Using End If

C#

string filename = Server.MapPath("TextFile.txt"); if (System.IO.File.Exists(filename)) { using (System.IO.FileStream infile = System.IO.File.Open(filename, System.IO.FileMode.Open), outfile = System.IO.File.Create( System.IO.Path.ChangeExtension(filename, "zip"))) { using (System.IO.Compression.DeflateStream cstream = new System.IO.Compression.DeflateStream(outfile, System.IO.Compression.CompressionMode.Compress)) { byte[] data = new byte[infile.Length]; int counter = 0; while ((counter = infile.Read(data, 0, data.Length)) != 0) { cstream.Write(data, 0, counter); } } } }

The DeflateStream constructor requires two parameters: the stream to write the compressed data to and the CompressionMode enumeration, which tells the class whether you want to compress or decompress data. After the code runs, be sure there is a file called TextFile.zip in your Web site directory.

GZipStream The GZipStream class creates and extracts archives compressed using the Deflate algorithm. It also adds a cyclic redundancy check (CRC) value for detecting data corruption. Listing 24-22 shows an example of decompressing a file using the GZipStream method. Listing 24-22:  Decompressing a file using GZipStream Dim filename As String = Server.MapPath("TextFile.zip")

VB

If System.IO.File.Exists(filename) Then Using infile As System.IO.FileStream = System.IO.File.Open(filename, System.IO.FileMode.Open),

continues

www.it-ebooks.info

966  ❘  Chapter 24   File I/O and Streams

Listing 24-22  (continued) outfile As System.IO.FileStream = System.IO.File.Create( System.IO.Path.ChangeExtension(filename, "txt")) Using cstream As System.IO.Compression.GZipStream = New System.IO.Compression.GZipStream(outfile, System.IO.Compression.CompressionMode.Decompress) Dim data(infile.Length) As Byte Dim counter As Integer = 0 counter = cstream.Read(data, 0, data.Length) While (counter <> 0) outfile.Write(data, 0, counter) counter = cstream.Read(data, 0, data.Length) End While End Using End Using End If

C#

string filename = Server.MapPath("TextFile.zip"); if (System.IO.File.Exists(filename)) { using (System.IO.FileStream infile = System.IO.File.Open(filename, System.IO.FileMode.Open), outfile = System.IO.File.Create( System.IO.Path.ChangeExtension(filename, "txt"))) { using (System.IO.Compression.GZipStream cstream = new System.IO.Compression.GZipStream(infile, System.IO.Compression.CompressionMode.Decompress)) { byte[] data = new byte[infile.Length]; int counter; while ((counter = cstream.Read(data, 0, data.Length)) != 0) { outfile.Write(data, 0, counter); } } } }

Running this sample should result in a file called TextFile.txt being created in the Web site directory.

Memory-Mapped Files In this chapter you have looked at how you can use stream objects to read and write files, but using streams for this does have a couple problems that you might encounter in certain scenarios. ➤➤

First, when you open a file using a stream, reading the file contents is done sequentially. This can be a problem if you are searching for a specific section of a very large file because you have to read the entire file from the beginning in order to locate the content.

➤➤

Second, opening a file using a stream can lock the file, preventing other applications or threads from reading or writing to the file.

www.it-ebooks.info

Memory-Mapped Files  ❘ 

967

The .NET Framework includes the System.IO.MemoryMappedFiles namespace, which includes a number of classes that allow you to create memory-mapped files. Memory-mapped files can be useful when you encounter the limitations of the stream objects just described. Memory-mapped files allow you to create views that start in a random location over very large files, rather than reading the file sequentially from the beginning. Memory-mapped files also allow multiple processes to map to the same portion of a file without locking the file. Listing 24-23 shows how you can use the classes of the MemoryMappedFile namespace to create a view of a file. Listing 24-23:  Reading contents of a MemoryMappedFile

VB



C#



This sample loads the TextFile.txt file into a MemoryMappedFile class using the static CreateFromFile method, which creates a named memory-mapped file. It then creates a MemoryMappedViewAccessor over the file using the MemoryMappedFiles CreateViewAccessor method. This method’s parameters allow you to specify an offset where the view accessor should start, the length of the view, and the access rights the view will have to the file. The MemoryMappedFile class also allows you to create memory-mapped files that are not associated with a physical file on disk, but are rather simply a named portion of memory. You can do this using the class’s static CreateNew method, which accepts a name and a length parameter. After an application has created a named memory-mapped file, that file is available for other parts of the application, or even completely separate applications, to access. You can do this using the MemoryMappedFiles OpenExisting method. Finally, if you prefer working with a stream style API, the MemoryMappedFiles namespace does include a MemoryMappedViewStream object, which allows you to create views on top of the memory-mapped file, and access the view using the same core stream syntax you have seen before.

Working with Serial Ports The framework provides a number of APIs located in the System.IO.Ports namespace (which was introduced in .NET 2.0) that allow your application to read and write to a local serial port. This feature can be useful for communicating with legacy hardware that remains commonplace in industrial automation systems, scientific analysis systems, and other industrial settings. Listing 24-24 demonstrates how to write to one port and read from another. Listing 24-24:  Reading and writing text with the serial port

VB

Untitled Page


C#



The SerialPort class gives you control over most aspects of the serial port, including baud rate, parity, and stop bits. The code in Listing 24 -24 attempts to open the serial port COM5 and write a string of text to it. It also opens a second connection to COM6 to read data. If your application needs to use the SerialPort classes, using a Serial Port emulator can be useful to test your application without having the actual device connected. In writing applications using the serial port, I have found the Virtual Serial Port Driver from Eltima Software to be a great tool. It allows you to defi ne virtual serial port pairs connected via loopback and the properties of the serial ports.

www.it-ebooks.info

970  ❘  Chapter 24   File I/O and Streams

IPC Using Pipes The applications you write are often not isolated islands. They need to send and receive messages from other processes running both locally and on remote machines, known as interprocess communication (IPC). Pipes are a great way to allow this communication, and the System.IO.Pipes namespace includes classes that allow you to create both anonymous and named pipes. Listing 24-25 demonstrates sending a string message to another process using the NamedPipeClientStream class. Listing 24-25:  Sending messages using named pipes

VB

Using pipeClient As System.IO.Pipes.NamedPipeClientStream = New System.IO.Pipes.NamedPipeClientStream(".", "testpipe", System.IO.Pipes.PipeDirection.Out) pipeClient.Connect(10000) Try Dim bytes() As Byte = ASCIIEncoding.Default.GetBytes("Hello World") pipeClient.Write(bytes, 0, bytes.Length) Catch ex As System.IO.IOException Console.WriteLine("ERROR: {0}", ex.Message) End Try End Using

C#

using (System.IO.Pipes.NamedPipeClientStream pipeClient = new System.IO.Pipes.NamedPipeClientStream(".", "testpipe", System.IO.Pipes.PipeDirection.Out)) { pipeClient.Connect(10000); try { byte[] bytes = ASCIIEncoding.Default.GetBytes("Hello World"); pipeClient.Write(bytes, 0, bytes.Length); } catch (System.IO.IOException ex) { Console.WriteLine("ERROR: {0}", ex.Message); } }

In this sample an existing named pipe is connected to using the NamedPipeClientStream class. Three parameters are provided in the class constructor to configure the server’s name, the pipe name, and the direction of communication in the pipe. After the stream is created, you simply use standard stream techniques to write a series of bytes to it.

Network Communications Finally, this chapter takes you beyond your own systems and talks about how you can use the .NET Framework to communicate with other systems. The .NET Framework contains a rich set of classes in the System.Net namespace that allow you to communicate over a network using a variety of protocols and communications layers. You can perform all types of actions, from DNS resolution to programmatic HTTP posts to sending e-mail through SMTP.

www.it-ebooks.info

Network Communications  ❘ 

971

WebRequest and WebResponse The first series of classes to discuss are the WebRequest and WebResponse classes. You can use these two classes to develop applications that can make a request to a Uniform Resource Identifier (URI) and receive a response from that resource. The .NET Framework provides three derivatives of the WebRequest and WebResponse classes, each designed to communicate to a specific type of endpoint via HTTP, FTP, and file:// protocols.

HttpWebRequest and HttpWebResponse The first pair of classes are the HttpWebRequest and HttpWebResponse classes. As you can probably guess based on their names, these two classes are designed to communicate using the HTTP protocol. Perhaps the most famous use of the HttpWebRequest and HttpWebResponse classes is to write applications that can make requests to other Web pages via HTTP and parse the resulting text to extract data. This is known as screen scraping. For an example of using the HttpWebRequest and HttpWebResponse classes to screen scrape, you can use the following code to build a Web page that will serve as a simple Web browser. You also learn how to display another Web page inside of yours using an HttpWebRequest. In this example, you scrape the Wrox. com home page and display it in a panel on your Web page. Listing 24-26 shows the code. Listing 24-26:  Using an HttpWebRequest to retrieve a Web page

VB

Untitled Page

This is the wrox.com website:



continues

www.it-ebooks.info

972  ❘  Chapter 24   File I/O and Streams

Listing 24-26  (continued)

C#



Figure 24-14 shows what the Web page looks like when you execute the code in Listing 24-26. The HttpWebRequest to the Wrox.com home page returns a string containing the scraped HTML. The sample assigns the value of this string to the GroupingText property of the Panel control. When the final page is rendered, the browser renders the HTML that was scraped as literal content on the page.

Figure 24-14

www.it-ebooks.info

Network Communications  ❘ 

973

One other use of the HttpWebRequest and HttpWebResponse classes is to programmatically post data to another Web page, as shown in Listing 24-27. Listing 24-27:  Using an HttpWebRequest to post data to a remote Web page

VB

Untitled Page


C#



You can see that the preceding code posts a search query to the Technorati REST API and receives a chunk of XML as the response. As in the example shown earlier in Listing 24-25, you can use the XmlDataSource and the TreeView to display the XML response.

FtpWebRequest and FtpWebResponse The classes discussed are the FtpWebRequest and FtpWebResponse classes. These two classes were new additions to the .NET 2.0 Framework, and they make executing File Transfer Protocol (FTP) commands from your Web page easy. Using these classes, implementing an entire FTP client right from your Web application is now possible. Listing 24-28 shows an example of downloading a text file from the public Microsoft.com FTP site. (See Figure 24-15.) Listing 24-28:  Using an FtpWebRequest to download a file from an FTP site Using FTP from an ASP.NET webpage


C#



www.it-ebooks.info

975

976  ❘  Chapter 24   File I/O and Streams

Figure 24-15

FileWebRequest and FileWebResponse Next, look at the FileWebRequest and FileWebResponse classes. These classes provide a file system implementation of the WebRequest and WebResponse classes and are designed to make transferring files using the file:// protocol easy, as shown in Listing 24-29. Listing 24-29:  Using the FileWebRequest to write to a remote file

VB

C#

Dim uri As New Uri("file://DEMO7/Documents/lorum.txt") If (uri.Scheme = uri.UriSchemeFile) Then Dim request As System.Net.FileWebRequest = _ System.Net.FileWebRequest.Create(uri) Dim response As System.Net.FileWebResponse = request.GetResponse() Dim reader As New System.IO.StreamReader(response.GetResponseStream()) Dim tmp As String = reader.ReadToEnd() response.Close() End If Uri uri = new Uri("file://DEMO7/Documents/lorum.txt "); if (uri.Scheme == Uri.UriSchemeFile) { System.Net.FileWebRequest request = (System.Net.FileWebRequest) System.Net.FileWebRequest.Create(uri); System.Net.FileWebResponse response = (System.Net.FileWebResponse)request.GetResponse(); System.IO.StreamReader reader = new System.IO.StreamReader(response.GetResponseStream()); string tmp = reader.ReadToEnd(); response.Close(); }

www.it-ebooks.info

Summary  ❘ 

977

This listing shows a request for the lorum.txt file that exists in the Documents folder on the DEMO7 machine on the local network.

Sending Mail Finally, consider a feature common to many Web applications — the capability to send e-mail from a Web page. The capability to send mail was part of the 1.0 Framework and located in the System.Web.Mail namespace. In the 2.0 Framework, this functionality was enhanced and moved to the System.Net. Mail namespace. Listing 24-30 shows an example of sending an e-mail. Listing 24-30:  Sending mail from a Web page

VB

C#

Dim message As System.Net.Mail.MailMessage = New System.Net.Mail.MailMessage( "[email protected]", "[email protected]") message.Subject = "Sending Mail with ASP.NET" message.Body = "This sample email demonstrates sending email using ASP.NET" Dim smtp As System.Net.Mail.SmtpClient = New System.Net.Mail.SmtpClient("localhost") smtp.Send(message) System.Net.Mail.MailMessage message = new System.Net.Mail.MailMessage( "[email protected]", "[email protected]"); message.Subject = "Sending Mail with ASP.NET"; message.Body = "This sample email demonstrates sending email using ASP.NET"; System.Net.Mail.SmtpClient smtp = new System.Net.Mail.SmtpClient("localhost"); smtp.Send(message);

In this sample, you first create a MailMessage object, which is the class that contains the actual message you want to send. The MailMessage class requires the To and From address be provided to its constructor, and you can either provide the parameters as strings, or you can use the MailAddressCollection class to provide multiple recipients’ e-mail addresses. After you create the Message, you use the SmtpClient class to actually send the message to your local SMTP server. The SmtpClient class allows you to specify the SMTP server from which you want to relay your e-mail.

Summary In this chapter, you looked at some of the other classes in the .NET Framework. You looked at managing the local file system by using classes in the System.IO namespace such as DirectoryInfo and the FileInfo, and you learned how to enumerate the local file system and manipulate both directory and file properties and directory and file Access Control Lists. Additionally, the chapter discussed the rich functionality .NET provides for working with paths. The chapter also covered how the .NET Framework enables you to read and write data to a multitude of data locations, including the local file system, network file system, and even system memory through a common Stream architecture. The framework provides you with specialized classes to deal with each kind of data location. Additionally, the framework makes working with streams even easier by providing Reader and Writer classes. These classes hide much of the complexity of reading from and writing to underlying streams. Here, too, the framework provides you with a number of different Reader and Writer classes that give you the power to control exactly how your data is read or written, be it character, binary, string, or XML.

www.it-ebooks.info

978  ❘  Chapter 24   File I/O and Streams

You were also introduced to the features of .NET Framework that allow you to create memory-mapped files and communicate with serial ports and with other processes through named pipes. Finally, you learned about the variety of network communication options the .NET Framework provides. From making and sending Web requests over HTTP, FTP, and File, to sending mail, the .NET Framework offers you a full plate of network communication services.

www.it-ebooks.info

25

User and server Controls whaT’s in This chaPTer? ➤

Creating, interacting with, and loading user controls



Working with server controls



Optimizing your server controls for developers

In an object- oriented environment like .NET, the encapsulation of code into small, single-purpose, reusable objects is one of the keys to developing a robust system. For example, if your application deals with customers, you might want to consider creating a Customer object to represent a single instance of a Customer. This object would encapsulate all the properties and behaviors a Customer can perform in the system. The advantage of creating this object is that you create a single point with which other objects can interact, and a single point of code to create, debug, deploy, and maintain. Objects like a Customer object are typically known as a business objects because they encapsulate all the business logic needed for a specific entity in the system. .NET, being an object- oriented framework, includes many other types of reusable objects. The focus of this chapter is discussing and demonstrating how you can use the .NET Framework to create two different types of reusable visual components for an ASP.NET application: user controls and server controls. ➤

A user control encapsulates existing ASP.NET controls into a single container control, which you can then easily reuse throughout your Web project.



A server control encapsulates the raw HTML and client and server logic into a single object. You can program against the server control, and its contents are ultimately rendered to the client to allow the user to interact with on the Web page.

Because the topics of user controls and server controls are so large, and because discussing the intricacies of each could easily fi ll an entire book by itself, this chapter can’t possibly investigate every option available to you. Instead, it attempts to give you a brief overview of building and using user controls and server controls and demonstrates some common scenarios for each type of control.

www.it-ebooks.info

980  ❘  Chapter 25   User and Server Controls

By the end of this chapter, you should have learned enough that you can get started building basic controls of each type and be able to continue to learn on your own.

User Controls User controls represent the most basic form of ASP.NET visual encapsulation. Because they are the most basic, they are also the easiest to create and use. Essentially a user control is the grouping of existing server controls into a single-container control. This enables you to create powerful objects that you can easily use throughout an entire Web project.

Creating User Controls Creating user controls is very simple in Visual Studio. To create a new user control, you first add a new User Control file to your Web site. From the Website menu, select the Add New Item option. After the Add New File dialog appears, select the Web User Control File template from the list and click Add. Notice that after the file is added to the project, the file has an .ascx extension. This extension signals to ASP.NET that this file is a user control. If you attempt to load the user control directly into your browser, ASP.NET returns an error telling you that this type of file cannot be served to the client. If you look at the HTML source shown in Listing 25-1, you see several interesting differences from a standard ASP.NET Web page. Listing 25-1:  A Web user control file template <%@ Control Language="VB" ClassName="WebUserControl" %>

Notice that the source uses the @Control directive rather than the @Page directive, which a standard Web page would use. Also notice that unlike a standard ASP.NET Web page, no other HTML tags besides the

After you add the controls to the user control, you put the user control onto a standard ASP.NET Web page. To do this, drag the file from the Solution Explorer onto your Web page. Figure 25-1 shows the user control after it has been dropped onto a host Web page.

www.it-ebooks.info

User Controls  ❘ 

981

Figure 25-1

After you have placed the user control onto a Web page, open the page in a browser to see the fully rendered Web page. User controls fully participate in the page-rendering life cycle, and controls contained within a user control behave just as they would if placed onto a standard ASP.NET Web page. This means that the user control has its own page events (such as Init, Load, and Prerender) that execute as the page is processed and that controls within the user control will also fire events as they normally would. Listing 25-3 shows how to use the user controls Page_Load event to populate the label and to handle a button’s Click event. Listing 25-3:  Creating control events in a user control <%@ Control Language="VB" ClassName="WebUserControl1" %>

VB



continues

www.it-ebooks.info

982  ❘  Chapter 25   User and Server Controls

Listing 25-3  (continued)

C#

<%@ Control Language="C#" ClassName="WebUserControl1" %>

When you render the Web page, you see that the text of the label changes as the user control loads, and again when you click the bottom of the page. In fact, if you put a breakpoint on either of these two events, you can see that ASP.NET does indeed break inside the user control code when the page is executed.

Interacting with User Controls So far, you have learned how you can create user controls and add them to a Web page. You have also learned how user controls can execute their own code and fire events. Most user controls, however, are not isolated islands within their parent page. Often the host Web page will need to interact with user controls that have been placed on it. For instance, you may decide that the text you want to load in the label must be given to the user control by the host page. To do this, you simply add a public property to the user control, and then assign text using the property. Listing 25-4 shows the modified user control. Listing 25-4:  Exposing user control properties <%@ Control Language="VB" ClassName="WebUserControl" %>

VB



C#

<%@ Control Language="C#" ClassName="WebUserControl" %>

After you modify the user control, you simply populate the property from the host Web page. Listing 25-5 shows how to set the Text property in code. Listing 25-5:  Populating user control properties from the host Web page

VB

C#

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Me.WebUserControl1.Text = "The quick brown fox jumped over the lazy dog" End Sub protected void Page_Load(object sender, EventArgs e) { this. WebUserControl1.Text = "The quick brown fox jumped over the lazy dog"; }

Note that public properties exposed by user controls will also be exposed by the Property Browser, so you can also set user control’s properties using it. User controls are simple ways of creating powerful, reusable components in ASP.NET. They are easy to create using the built-in templates. Because they participate fully in the page life cycle, you can create controls that can interact with their host page and even other controls on the host page.

Loading User Controls Dynamically User controls can also be created and added to the Web form dynamically at runtime. The ASP.NET Page object includes the LoadControl method, which allows you to load user controls at runtime by providing the method with a virtual path to the user control you want to load. The method returns a generic Control object that you can then add to the page’s Controls collection. Listing 25-6 demonstrates how you can use the LoadControl method to dynamically add a user control to a Web page. Listing 25-6:  Dynamically adding a user control <%@ Page Language="VB" %>

VB

Untitled Page


C#

void Page_Load(object sender, EventArgs e) { Control myForm = Page.FindControl("Form1"); Control c1 = LoadControl("WebUserControl.ascx"); myForm.Controls.Add(c1); }

The fi rst step in adding a user control to the page is to locate the page’s Form control using the FindControl method. Should the user control contain ASP.NET controls that render form elements such as a button or text box, the user control must be added to the form element’s Controls collection. Adding user controls containing certain ASP.NET elements such as a Label, HyperLink, or Image directly to the Page object’s Controls collection is possible; however, it is generally safer to be consistent and add them to the Form. Adding a control that must be contained within the Form, such as a Button control, to the Pages Controls collection results in a runtime parser error. After the form has been found, the sample uses the Page’s LoadControl() method to load an instance of the user control. The method accepts a virtual path to the user control you want to load and returns the loaded user control as a generic Control object. Finally, you add the control to the Form object’s Controls collection. You can also add the user control to other container controls that may be present on the Web page, such as a Panel or Placeholder control. Remember that you need to re-add your control to the ASP.NET page each time the page performs a postback. After you have the user control loaded, you can also work with its object model, just as you can with any other control. To access properties and methods that the user control exposes, you need to cast the control from the generic Control type to its actual type. To do that, you also need to add the @Reference directive to the page. This tells ASP.NET to compile the user control and link it to the ASP.NET page so that the page knows where to fi nd the user control type. Listing 25-7 demonstrates how you can access a custom property of your user control by casting the control after loading it. The sample loads a modified user control that hosts an ASP.NET TextBox control and exposes a public property that allows you to access the TextBox control’s Text property.

www.it-ebooks.info

User Controls  ❘ 

985

Listing 25-7:  Casting a user control to its native type

VB

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Dim myForm As Control = Page.FindControl("Form1") Dim c1 As WebUserControl = CType(LoadControl("WebUserControl.ascx"), WebUserControl) myForm.Controls.Add(c1) c1.ID = "WebUserControl1" c1.Text = "My users controls text" End Sub

C#

void Page_Load(object sender, EventArgs e) { Control myForm = Page.FindControl("Form1"); WebUserControl c1 = (WebUserControl)LoadControl("WebUserControl.ascx"); myForm.Controls.Add(c1); c1.ID = "WebUserControl1"; c1.Text = "My users controls text"; }

Notice that the sample adds the control to the Form’s Controls collection and then sets the Text property. The ordering of this is important as after a page postback occurs the control’s ViewState is not calculated until the control is added to the Controls collection. Therefore, if you set the Text value (or any other property of the user control) before the control’s ViewState is calculated, the value is not persisted in the ViewState. One additional twist to dynamically adding user controls occurs when you are using output caching to cache the user controls. In this case, after the control has been cached, the LoadControl method does not return a new instance of the control. Instead, it returns the cached copy of the control. This presents problems when you try to cast the control to its native type because, after the control is cached, the LoadControl method returns it as a PartialCachingControl object rather than as its native type. Therefore, the cast in the previous sample results in an exception being thrown. To solve this problem, you simply test the object type before attempting the cast. This is shown in Listing 25-8. Listing 25-8:  Detecting cached user controls

VB

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Dim myForm As Control = Page.FindControl("Form1") Dim c1 As Control = LoadControl("WebUserControl.ascx") myForm.Controls.Add(c1) If c1.GetType() Is GetType(WebUserControl) Then 'This control is not participating in OutputCache CType(c1, WebUserControl).ID = "myListing258" CType(c1, WebUserControl).Text = "My users controls text" ElseIf c1.GetType() Is GetType(PartialCachingControl) And Not (IsNothing(CType(c1, PartialCachingControl).CachedControl)) Then 'The control is participating in output cache, but has expired Dim webUserControl1 As WebUserControl = CType(CType(c1, PartialCachingControl).CachedControl, WebUserControl) webUserControl1.ID = "WebUserControl1"

continues

www.it-ebooks.info

986  ❘  Chapter 25   User and Server Controls

Listing 25-8  (continued) webUserControl1.Text = "My users controls text" End If End Sub

C#

void Page_Load(object sender, EventArgs e) { Control myForm = Page.FindControl("Form1"); Control c1 = LoadControl("WebUserControl.ascx"); myForm.Controls.Add(c1); if (c1 is WebUserControl) { //This control is not participating in OutputCache ((WebUserControl)c1).ID = "WebUserControl1"; ((WebUserControl)c1).Text = "My users controls text"; } else if ((c1 is PartialCachingControl) && ((PartialCachingControl)c1).CachedControl != null) { //The control is participating in output cache, but has expired WebUserControl webUserControl1 = ((WebUserControl)((PartialCachingControl)c1).CachedControl); webUserControl1.ID = "WebUserControl1"; webUserControl1.Text = "My users controls text"; } }

The sample demonstrates how you can test to see what type the LoadControl returns and set properties based on the type. For more information on caching, check out Chapter 22. Finally, in the previous samples user controls have been added dynamically during the Page_Load event. But there may be times when you want to add the control in a different event, such as a Button’s Click event or the SelectedIndexChanged event of a DropDownList control. Using these events to add user controls dynamically presents new challenges. Because these events may not be raised each time a page postback occurs, you need to create a way to track when a user control has been added so that it can be re-added to the Web page as additional postbacks occur. A simple way to do this is to use the ASP.NET session to track when the user control is added to the Web page. Listing 25-9 demonstrates this. Listing 25-9:  Tracking added user controls across postbacks <%@ Page Language="VB" %>

VB

Untitled Page


C#

<%@ Page Language="C#" %> protected void Button1_Click(object sender, EventArgs e) { //Make sure the control has not already been added to the page if ((Session["WebUserControlAdded"] == null) || (!(bool)Session["WebUserControlAdded"])) { Control myForm = Page.FindControl("Form1"); Control c1 = LoadControl("WebUserControl.ascx"); myForm.Controls.Add(c1); Session["WebUserControlAdded"] = true; } } protected void Page_Load(object sender, EventArgs e) { //Check to see if the control should be added to the page if ((Session["WebUserControlAdded"] != null) && ((bool)Session["WebUserControlAdded"])) { Control myForm = Page.FindControl("Form1"); Control c1 = LoadControl("WebUserControl.ascx"); myForm.Controls.Add(c1); } }

This sample used a simple Session variable to track whether the user control has been added to the page. When the Button1 Click event fires, the session variable is set to True, indicating that the user control has been added. Then, each time the page performs a postback, the Page_Load event checks to see whether the session variable is set to True, and if so, it re-adds the control to the page.

www.it-ebooks.info

988  ❘  Chapter 25   User and Server Controls

Server Controls The power to create server controls in ASP.NET is one of the greatest tools you can have as an ASP.NET developer. Creating your own custom server controls and extending existing controls are actually both quite easy. In ASP.NET, all controls are derived from two basic classes: System.Web.UI.WebControls .WebControl or System.Web.UI.ScriptControl. Classes derived from the WebControl class have the basic functionality required to participate in the Page framework. These classes include most of the common functionality needed to create controls that render a visual HTML representation and provide support for many of the basic styling elements such as Font, Height, and Width. Because the WebControl class derives from the Control class, the controls derived from it have the basic functionality to be a designable control, meaning they can be added to the Visual Studio Toolbox, dragged onto the page designer, and have their properties and events displayed in the Property Browser. Controls derived from the ScriptControl class build on the functionality that the WebControl class provides by including additional features designed to make working with client-side script libraries easier. The class tests to ensure that a ScriptManager control is present in the hosting page during the control’s PreRender stage, and also ensures that derived controls call the proper ScriptManager methods during the Render event.

Server Control Projects To make creating a custom server control easy, Visual Studio provides two different project templates that set up a basic project structure including the files needed to get started creating a server control. Figure 25-2 shows the ASP.NET Server Control and ASP.NET AJAX Server Control projects in Visual Studios New Project dialog.

Figure 25-2

The ASP.NET Server Control project creates a basic class library project with a single server control class deriving from WebControl included by default. The ASP.NET AJAX Server Control project also

www.it-ebooks.info

Server Controls  ❘ 

creates a basic class library project, but includes a single server control class derived from ScriptControl and a Resource file and a JavaScript file. Creating either of these project types results in a runnable, though essentially functionless, server control. You can add additional server control classes to the project by selecting the ASP.NET Server Control file template from the Add New Item dialog. Note that this template differs slightly from the default template included in the server control projects. It uses a different filename scheme and includes slightly different code in the default control template. After you’ve created a new project, you can test the control by adding a new Web Project to the existing solution, rebuilding the entire solution, and opening the default Web page. Visual Studio automatically adds the server control to the toolbox as shown in Figure 25-3.

Figure 25-3

Visual Studio will do this for any controls contained in projects in the currently open solution. Now simply drag the control onto the Web Form. A reference to the control is added to the project and the control is added to the Web page. Listing 25-10 shows you what the Web page source code looks like after you have added the control. Listing 25-10:  Adding a Web Control Library to a Web page <%@ Register Assembly="ServerControl1" Namespace="ServerControl1" TagPrefix="cc1" %> Adding a Custom Web Control


After you drag the control onto the Web form, take a look at its properties in the Properties window. Figure 25-4 shows the properties of your custom control. Notice that the control has all the basic properties of a visual control, including various styling and behavior properties. The properties are exposed because the control was derived from the WebControl class. The control also inherits the base events exposed by WebControl.

Figure 25-4

www.it-ebooks.info

989

990  ❘  Chapter 25   User and Server Controls

Make sure the control is working by entering a value for the Text property and viewing the page in a browser. Figure 25-5 shows what the page looks like if you set the Text property to “Hello World!”. As expected, the control has rendered the value of the Text property to the Web page. Now that you have a basic server control project up and running, you can go back and take a look at the template class created for you by the ASP.NET Server Control project. The default template is shown in Listing 25-11.

Figure 25-5

Listing 25-11:  The Visual Studio ASP.NET Server Control class template

VB

Imports Imports Imports Imports Imports Imports Imports

System System.Collections.Generic System.ComponentModel System.Text System.Web System.Web.UI System.Web.UI.WebControls

")> Public Class ServerControl1 Inherits WebControl Property Text() As String Get Dim s As String = CStr(ViewState("Text")) If s Is Nothing Then Return "[" & Me.ID & "]" Else Return s End If End Get Set(ByVal Value As String) ViewState("Text") = Value End Set End Property Protected Overrides Sub RenderContents( ByVal output As HtmlTextWriter) output.Write(Text) End Sub End Class

C#

using using using using using using

System; System.Collections.Generic; System.ComponentModel; System.Linq; System.Text; System.Web;

www.it-ebooks.info

Server Controls  ❘ 

991

using System.Web.UI; using System.Web.UI.WebControls; namespace ServerControl1 { [DefaultProperty("Text")] [ToolboxData("<{0}:ServerControl1 runat=server>" + "")] public class ServerControl1 : WebControl { [Bindable(true)] [Category("Appearance")] [DefaultValue("")] [Localizable(true)] public string Text { get { String s = (String)ViewState["Text"]; return ((s == null) ? "[" + this.ID + "]" : s); } set { ViewState["Text"] = value; } } protected override void RenderContents(HtmlTextWriter output) { output.Write(Text); } } }

There are a number of interesting things to note about the default server control template generated by the project. First, notice that both the class declaration and the Text property are decorated by attributes. ASP.NET server controls make heavy use of attributes to indicate different types of runtime and design-time behaviors. You will learn more about the attributes you can apply to server control classes and properties later in this chapter. Second, by default the template includes a single property called Text and a simple overridden method RenderContents that renders the value of that property to the screen. The RenderContents method of the control is the primary method used to output content from the server control. If you view the HTML source of the previous sample, you will see that not only has ASP.NET added the value of the Text property to the HTML markup, but it has surrounded the text with a block. If you look at the code for the WebControl class’s render method, you can see that in addition to calling the RenderContents method, calls to render a begin and end tag are included, inserting the Span tag around the control’s content. protected internal override void Render(HtmlTextWriter writer) { this.RenderBeginTag(writer); this.RenderContents(writer); this.RenderEndTag(writer); }

If you have provided an ID value for your control, then the Span tag will also, by default, render an ID attribute. Having the Span tags can sometimes be problematic, so if you want to prevent ASP.NET from

www.it-ebooks.info

992  ❘  Chapter 25   User and Server Controls

automatically adding the Span tags you can override the Render method in your control and call the RenderContents method directly. VB

Protected Overrides Sub Render(ByVal writer As System.Web.UI.HtmlTextWriter) Me.RenderContents(writer) End Sub

C#

protected override void Render(HtmlTextWriter writer) { this.RenderContents(writer); }

The default server control template does a good job demonstrating how easy creating a simple server control is, but of course, this control does not have much functionality and lacks many of the features you might find in a typical server control. The rest of this chapter focuses on how you can use different features of the .NET Framework to add additional runtime and design time features to a server control.

Control Attributes Much of the design-time experience a server control offers developers is configured by adding attributes to the server control class and properties. For example, when you look at the default control template from the previous section (Listing 25-11), notice that attributes have been applied to both the class and to the Text property. This section describes the attributes that can be applied to server controls and how they affect the behavior of the control.

Class Attributes Class attributes for server controls can be divided into three general categories: global control runtime behaviors, how the control looks in the Visual Studio Toolbox, and how it behaves when placed on the design surface. Table 25-1 describes some of these attributes. Table 25-1 Attribute

Description

Designer

Indicates the designer class this control should use to render a design-time view of the control on the Visual Studio design surface

TypeConverter

Specifies what type to use as a converter for the object

DefaultEvent

Indicates the default event created when the user double-clicks the control on the Visual Studio design surface

DefaultProperty

Indicates the default property for the control

ControlBuilder

Specifies a ControlBuilder class for building a custom control in the ASP.NET control parser

ParseChildren

Indicates whether XML elements nested within the server controls tags will be treated as properties or as child controls

TagPrefix

Indicates the text the control is prefixed within the Web page HTML

Property/Event Attributes Property attributes are used to control a number of different aspects of server controls including how your properties and events behave in the Visual Studio Property Browser and how properties and events are serialized at design time. Table 25-2 describes some of the property and event attributes you can use.

www.it-ebooks.info

Server Controls  ❘ 

993

Table 25-2 Attribute

Description

Bindable

Indicates that the property can be bound to a data source

Browsable

Indicates whether the property should be displayed at design time in the Property Browser

Category

Indicates the category this property should be displayed under in the Property Browser

Description

Displays a text string at the bottom of the Property Browser that describes the purpose of the property

EditorBrowsable

Indicates whether the property should be editable when shown in the Property Browser

DefaultValue

Indicates the default value of the property shown in the Property Browser

DesignerSerializationVisibility

Specifies the visibility a property has to the design-time serializer

NotifyParentProperty

Indicates that the parent property is notified when the value of the property is modified

PersistChildren

Indicates whether, at design time, the child controls of a server control should be persisted as nested inner controls

PersistanceMode

Specifies how a property or an event is persisted to the ASP.NET page

TemplateContainer

Specifies the type of INamingContainer that will contain the template after it is created

Editor

Indicates the UI Type Editor class this control should use to edit its value

Localizable

Indicates that the property contains text that can be localized

Themable

Indicates whether this property can have a theme applied to it

Obviously, the class and property/event attribute tables present a lot of information upfront. You already saw a demonstration of some of these attributes in Listing 25-11 and as you go through the rest of this chapter, samples will leverage other attributes listed in the tables.

Control Rendering So far in this chapter you have seen how easy getting up and running with a very basic server control using the Visual Studio project templates is and how you can apply attributes in the server control to influence some basic control behaviors. The rest of the chapter focuses on how you can use features of ASP.NET to add more advanced control capabilities to your server controls.

The Page Event Life Cycle Before digging deeper into server controls, spending a moment to look at the general ASP.NET page life cycle that server controls operate within is helpful. As the control developer, you are responsible for overriding methods that execute during the life cycle and implementing your own custom rendering logic. Remember that when a Web browser makes a request to the server, it is using HTTP, a stateless protocol. ASP.NET provides a page-execution framework that helps create the illusion of state in a Web application. This framework is basically a series of methods and events that execute every time an ASP.NET page is processed. Figure 25-6 shows the events and methods called during the control’s life cycle.

www.it-ebooks.info

994  ❘  Chapter 25   User and Server Controls

FrameworkInitialize AddParsedSubObject CreateControlCollection AddedControl

Loop to add all controls resident on the page

AddParsedSubObject

DeterminePostBackMode PreInit Init TrackViewState InitComplete LoadPageFromPersistanceMedium PreLoad Load RaisePostbackEvent LoadComplete PreRenderComplete PreRender SaveViewState SavePageToPersistanceMedium SaveStateComplete RenderControl VerifyRenderingServerForm Unload

Figure 25-6

As you read through the rest of this chapter, you will see that a server control uses many of these events and methods. Understanding them and the order in which they execute is helpful so that as you are adding features to your server control, you can structure your logic to follow the page life cycle.

Rendering HTML Tags The main job of a server control is to render some type of markup language to the HTTP output stream, which is returned to and displayed by the client. If your client is a standard browser, the control should emit HTML; if the client is something like a mobile device, the control may need to emit a different type of markup, such as WAP, or WML. As I stated earlier, your responsibility as the control developer is to tell the server control what markup to render. The overridden RenderContents method, called during the control’s life cycle, is the primary location where you tell the control what you want to emit to the client. In

www.it-ebooks.info

Server Controls  ❘ 

995

Listing 25-12, notice that the RenderContents method is used to tell the control to print the value of the Text property. Listing 25-12:  Overriding the Render method

VB C#

Protected Overrides Sub RenderContents(ByVal output As HtmlTextWriter) output.Write(Text) End Sub protected override void RenderContents(HtmlTextWriter output) { output.Write(Text); }

Also notice that the RenderContents method has one method parameter called output. This parameter is an HtmlTextWriter class, which is what the control uses to render HTML to the client. This special writer class is specifically designed to emit HTML 4.0–compliant HTML to the browser. The HtmlTextwriter class has a number of methods you can use to emit your HTML, including RenderBeginTag and WriteBeginTag. Listing 25-13 shows how you can modify the control’s Render method to emit an HTML tag. Listing 25-13:  Using the HtmlTextWriter to render an HTML tag

VB C#

Protected Overrides Sub RenderContents(ByVal output As HtmlTextWriter) output.RenderBeginTag(HtmlTextWriterTag.Input) output.RenderEndTag() End Sub protected override void RenderContents(HtmlTextWriter output) { output.RenderBeginTag(HtmlTextWriterTag.Input); output.RenderEndTag(); }

First, notice that the RenderBeginTag method is used to emit the HTML. The advantage of using this method to emit HTML is that it requires you to select a tag from the HtmlTextWriterTag enumeration. Using the RenderBeginTag method and the HtmlTextWriterTag enumeration enables you to have your control automatically support down-level browsers that cannot understand HTML 4.0 syntax. If a downlevel browser is detected by ASP.NET, the control automatically emits HTML 3.2 syntax instead of HTML 4.0. Second, notice that the RenderEndTag method is also used. As the name suggests, this method renders the closing tag. Notice, however, that you do not have to specify in this method which tag you want to close. The RenderEndTag automatically closes the last begin tag rendered by the RenderBeginTag method, which in this case is the tag. If you want to emit multiple HTML tags, make sure you order your Begin and End render methods properly. In Listing 25-14, for example, you add a
tag to the control. The
tag surrounds the tag when rendered to the page. Listing 25-14:  Using the HtmlTextWriter to render multiple HTML tags

VB

C#

Protected Overrides Sub RenderContents(ByVal output As HtmlTextWriter) output.RenderBeginTag(HtmlTextWriterTag.Div) output.RenderBeginTag(HtmlTextWriterTag.Input) output.RenderEndTag() output.RenderEndTag() End Sub protected override void RenderContents(HtmlTextWriter output) {

continues

www.it-ebooks.info

996  ❘  Chapter 25   User and Server Controls

Listing 25-14  (continued) output.RenderBeginTag(HtmlTextWriterTag.Div); output.RenderBeginTag(HtmlTextWriterTag.Input); output.RenderEndTag(); output.RenderEndTag(); }

Now that you have a basic understanding of how to emit simple HTML, look at the output of your control. Figure 25-7 shows the source for the page.

Figure 25-7

You can see that the control emitted some simple HTML markup. Also notice that the control was smart enough to realize that the input control did not contain any child controls and, therefore, the control did not need to render a full closing tag. Instead, it automatically rendered the shorthand />, rather than .

Adding Tag Attributes Emitting HTML tags is a good start to building the control, but perhaps this is a bit simplistic. Normally, when rendering HTML you would emit some tag attributes (such as ID or Name) to the client in addition to the tag. Listing 25-15 shows how you can easily add tag attributes. Listing 25-15:  Rendering HTML tag attributes

VB

Protected Overrides Sub RenderContents(ByVal output As HtmlTextWriter) output.RenderBeginTag(HtmlTextWriterTag.Div) output.AddAttribute(HtmlTextWriterAttribute.Type, "text") output.AddAttribute(HtmlTextWriterAttribute.Id, Me.ClientID & "_i") output.AddAttribute(HtmlTextWriterAttribute.Name, Me.ClientID & "_i") output.AddAttribute(HtmlTextWriterAttribute.Value, Me.Text) output.RenderBeginTag(HtmlTextWriterTag.Input)

www.it-ebooks.info

Server Controls  ❘ 

output.RenderEndTag() output.RenderEndTag() End Sub

C#

protected override void RenderContents(HtmlTextWriter output) { output.RenderBeginTag(HtmlTextWriterTag.Div); output.AddAttribute(HtmlTextWriterAttribute.Type, "text"); output.AddAttribute(HtmlTextWriterAttribute.Id, this.ClientID + "_i"); output.AddAttribute(HtmlTextWriterAttribute.Name, this.ClientID + "_i"); output.AddAttribute(HtmlTextWriterAttribute.Value, this.Text); output.RenderBeginTag(HtmlTextWriterTag.Input); output.RenderEndTag(); output.RenderEndTag(); }

You can see that by using the AddAttribute method, you have added three attributes to the tag. Also notice that, once again, you are using an enumeration, HtmlTextWriterAttribute, to select the attribute you want to add to the tag. This serves the same purpose as using the HtmlTextWriterTag enumeration, allowing the control to degrade its output to down-level browsers. As with the Render methods, the order in which you place the AddAttributes methods is important. You place the AddAttributes methods directly before the RenderBeginTag method in the code. The AddAttributes method associates the attributes with the next HTML tag that is rendered by the RenderBeginTag method — in this case the tag. Now browse to the test page and check out the HTML source with the added tag attributes. Figure 25-8 shows the HTML source rendered by the control.

Figure 25-8

You can see that the tag attributes you added in the server control are now included as part of the HTML tag rendered by the control.

www.it-ebooks.info

997

998  ❘  Chapter 25   User and Server Controls

A Word about Control IDs Notice that in Listing 25-15, using the control’s ClientID property as the value of both the Id and Name attributes is important. Controls that derive from the WebControl class automatically expose three different types of ID properties: ID, UniqueID, and ClientID. Each of these properties exposes a slightly altered version of the control’s ID for use in a specific scenario. The ID property is the most obvious. Developers use it to get and set the control’s ID. It must be unique to the page at design time. The UniqueID property is a read-only property generated at runtime that returns an ID that has been prepended with the containing control’s ID. This is essential so that ASP.NET can uniquely identify each control in the page’s control tree, even if the control is used multiple times by a container control such as a Repeater or GridView. For example, if you add this custom control to a Repeater, the UniqueID for each custom control rendered by the Repeater is modified to include the Repeater’s ID when the page executed: MyRepeater:Ctrl0:MyCustomControl

Beginning with ASP.NET 4.0, the ClientID property can be generated differently depending on the value of the ClientIDMode property. The ClientIDMode property allows you to select one of four mechanisms that ASP.NET will use to generate the ClientID: ➤➤

AutoID: Equivalent to the behavior used in earlier versions of ASP.NET.

➤➤

Static: Specifies that the ClientID value will be the same as the ID, without concatenating the IDs of parent containers.

➤➤

Predictable: Primarily for use in data controls, it concatenates the IDs of a control’s naming containers, but generated client ID values do not contain strings like ‘ctlxxx’. Instead you can set the ClientIDRowSuffix property to provide a unique value for each control generated.

➤➤

Inherit: Specifies that the control’s ID generation is the same as its parent.

Additionally, to ensure that controls can generate a unique ID, they should implement the INamingContainer interface. This is a marker interface only, meaning that it does not require any additional methods to be implemented; it does, however, ensure that the ASP.NET runtime guarantees the control always has a unique name within the page’s tree hierarchy, regardless of its container.

Styling HTML So far, you have seen how easy building a simple server control and emitting the proper HTML, including attributes, are, but modern Web development techniques generally restrict the use of HTML to a basic content description mechanism, relying instead on CSS for the positioning and styling of HTML elements in a Web page. In this section, you will learn how you can have your control render style information. As mentioned at the very beginning of this section, you are creating controls that inherit from the WebControl class. Because of this, these controls already have the basic infrastructure for emitting most of the standard CSS-style attributes. In the Property Browser for this control, you should see a number of style properties already listed, such as background color, border width, and font. You can also launch the style builder to create complex CSS styles. These basic properties are provided by the WebControl class, but it is up to you to tell your control to render the values set at design time. To do this, you simply execute the AddAttributesToRender method. Listing 25-16 shows you how to do this. Listing 25-16:  Rendering style properties Protected Overrides Sub RenderContents(ByVal output As HtmlTextWriter) output.RenderBeginTag(HtmlTextWriterTag.Div)

VB

output.AddAttribute(HtmlTextWriterAttribute.Type, "text")

www.it-ebooks.info

Server Controls  ❘ 

999

output.AddAttribute(HtmlTextWriterAttribute.Id, Me.ClientID & "_i") output.AddAttribute(HtmlTextWriterAttribute.Name, Me.ClientID & "_i") output.AddAttribute(HtmlTextWriterAttribute.Value, Me.Text) Me.AddAttributesToRender(output) output.RenderBeginTag(HtmlTextWriterTag.Input) output.RenderEndTag() output.RenderEndTag() End Sub

C#

protected override void RenderContents(HtmlTextWriter output) { output.RenderBeginTag(HtmlTextWriterTag.Div); output.AddAttribute(HtmlTextWriterAttribute.Type, "text"); output.AddAttribute(HtmlTextWriterAttribute.Id, this.ClientID + "_i"); output.AddAttribute(HtmlTextWriterAttribute.Name, this.ClientID + "_i"); output.AddAttribute(HtmlTextWriterAttribute.Value, this.Text); this.AddAttributesToRender(output); output.RenderBeginTag(HtmlTextWriterTag.Input); output.RenderEndTag(); output.RenderEndTag(); }

Executing this method tells the control to render any style information that has been set. Note that executing this method not only causes the style-related properties to be rendered, but also several other attributes, including ID, tabindex, and tooltip. If you are manually rendering these attributes earlier in your control, then you may end up with duplicate attributes being rendered. Additionally, being careful about where you execute the AddAttributesToRender method is important. In Listing 25-16, it is executed immediately before the Input tag is rendered, which means that the attributes will be rendered both on the Input element and on the Span element surrounding the Input element. Placing the method call before the beginning Div tag is rendered will ensure that the attributes are now applied to the Div and its surrounding span. Placing the method call after the end Div means only the span has the attribute applied. Using the Property Browser, you can set the background color of the control to Red and the font to Bold. When you set these properties, they are automatically added to the control tag in the ASP.NET page. After you have added the styles, the control tag looks like this:

The style changes have been persisted to the control as attributes. When you execute this page in the browser, the style information should be rendered to the HTML, making the background of the text box red and its font bold. Figure 25-9 shows the page in the browser.

Figure 25-9

Once again, look at the source for this page. The style information has been rendered to the HTML as a style tag. Figure 25-10 shows the HTML emitted by the control.

www.it-ebooks.info

1000  ❘  Chapter 25   User and Server Controls

Figure 25-10

If you want more control over the rendering of styles in your control you can use the HtmlTextWriters AddStyleAttribute method. Similar to the AddAttribute method, the AddStyleAttribute method enables you to specify CSS attributes to add to a control using the HtmlTextWriterStyle enumeration. However, unlike the AddAttribute method, attributes added using AddStyleAttribute are defined inside of a style attribute on the control. Listing 25-17 demonstrates the use of the AddStyleAttribute method. Listing 25-17:  Adding control styles using AddStyleAttribute Protected Overrides Sub RenderContents(ByVal output As HtmlTextWriter) output.RenderBeginTag(HtmlTextWriterTag.Div)

VB

output.AddAttribute(HtmlTextWriterAttribute.Type, "text") output.AddAttribute(HtmlTextWriterAttribute.Id, Me.ClientID & "_i") output.AddAttribute(HtmlTextWriterAttribute.Name, Me.ClientID & "_i") output.AddAttribute(HtmlTextWriterAttribute.Value, Me.Text) output.AddStyleAttribute( HtmlTextWriterStyle.BackgroundColor, "Red") output.RenderBeginTag(HtmlTextWriterTag.Input) output.RenderEndTag() output.RenderEndTag() End Sub

C#

protected override void RenderContents(HtmlTextWriter output) { output.RenderBeginTag(HtmlTextWriterTag.Div); output.AddAttribute(HtmlTextWriterAttribute.Type, "text"); output.AddAttribute(HtmlTextWriterAttribute.Id, this.ClientID + "_i"); output.AddAttribute(HtmlTextWriterAttribute.Name, this.ClientID + "_i");

www.it-ebooks.info

Server Controls  ❘ 

1001

output.AddAttribute(HtmlTextWriterAttribute.Value, this.Text); output.AddStyleAttribute( HtmlTextWriterStyle.BackgroundColor, "Red"); output.RenderBeginTag(HtmlTextWriterTag.Input); output.RenderEndTag(); output.RenderEndTag(); }

Running this sample results in the same red background color being applied to the control.

Themes and Skins A great feature of ASP.NET that allows you to create visual styles for your Web applications and was introduced to you in Chapter 6 is themes and skins. This section describes how you can allow your custom server controls to leverage themes and skins. As you saw in Chapter 6, skins are essentially a way to set default values for the UI elements of controls in your Web application. You simply define the control and its properties in a .skin file and the values are applied to the control at runtime. Listing 25-18 shows a sample skin. Listing 25-18:  Sample ASP.NET skin <%@ Register Assembly="WebControlLibrary1" Namespace="WebControlLibrary1" TagPrefix="cc1" %>

By default, ASP.NET allows all control properties to be defined in the skin file, but obviously this is not always appropriate. Most exposed properties are non-UI related; therefore, you do not apply a theme to them. By setting the Themeable attribute to False on each of these properties, you prevent the application of a theme. Listing 25-19 shows how to do this in your control by disabling themes on the Text property. Listing 25-19:  Disabling theme support on a control property

VB

Property Text() As String Get Dim s As String = CStr(ViewState("Text")) If s Is Nothing Then Return "[" + Me.ID + "]" Else Return s End If End Get Set(ByVal Value As String) ViewState("Text") = Value End Set End Property

C#

[Bindable(true)] [Category("Appearance")] [DefaultValue("")] [Localizable(true)] [Themeable(false)] public string Text

continues

www.it-ebooks.info

1002  ❘  Chapter 25   User and Server Controls

Listing 25-19  (continued) { get { String s = (String)ViewState["Text"]; return ((s == null) ? "["+ this.ID + "]": s); } set { ViewState["Text"] = value; } }

Now, if a developer attempts to define this property in his skin file, he receives a compiler error when the page is executed.

Adding Client-Side Features Although the capability to render and style HTML is quite powerful by itself, other resources can be sent to the client, such as client-side scripts, images, and resource strings. ASP.NET provides you with some powerful tools for using client-side scripts in your server controls and retrieving other resources to the client along with the HTML your control emits. Additionally, ASP.NET includes an entire model that allows you to make asynchronous callbacks from your Web page to the server.

Emitting Client-Side Script Having your control emit client-side script like JavaScript enables you to add powerful client-side functionality to your control. Client-side scripting languages take advantage of the client’s browser to create more flexible and easy-to-use controls. ASP.NET provides a wide variety of methods for emitting client-side script that you can use to control where and how your script is rendered. Most of the properties and methods needed to render client script are available from the ClientScriptManager class, which you can access using Page.ClientScript. Listing 25-20 demonstrates how you can use the RegisterStartupScript method to render JavaScript to the client. This listing adds the code into the OnPreRender method, rather than into the Render method used in previous samples. This method allows every control to inform the page about the client-side script it needs to render. After the Render method is called, the page is able to render all the client-side script it collected during the OnPreRender method. If you call the client-side script registration methods in the Render method, the page has already completed a portion of its rendering before your client-side script can render itself. Listing 25-20:  Rendering a client-side script to the browser

VB

C#

Protected Overrides Sub OnPreRender(ByVal e As System.EventArgs) Page.ClientScript.RegisterStartupScript(GetType(Page), "ControlFocus", "document.getElementById('" & Me.ClientID & "_i" & "').focus();", True) End Sub protected override void OnPreRender(EventArgs e) { Page.ClientScript.RegisterStartupScript(typeof(Page), "ControlFocus", "document.getElementById('" + this.ClientID + "_i" + "').focus();", true); }

www.it-ebooks.info

Server Controls  ❘ 

1003

In this listing, the code emits client-side script to automatically move the control focus to the TextBox control when the Web page loads. When you use the RegisterStartupScript method, notice that it now includes an overload that lets you specify whether the method should render surrounding script tags. This can be handy if you are rendering more than one script to the page. Also notice that the method requires a key parameter. This parameter is used to uniquely identify the script block; if you are registering more than one script block in the Web page, make sure that each block is supplied a unique key. You can use the IsStartupScriptRegistered method and the key to determine whether a particular script block has been previously registered on the client using the RegisterStatupScript method. When you execute the page in the browser, notice that the focus is automatically placed into a text box. If you look at the source code for the Web page, you should see that the JavaScript was written to the bottom of the page, as shown in Figure 25-11.

Figure 25-11

If you want the script to be rendered to the top of the page, you use the RegisterClientScriptBlock method that emits the script block immediately after the opening
element. Keep in mind that the browser parses the Web page from top to bottom, so if you emit client-side script at the top of the page that is not contained in a function, any references in that code to HTML elements further down the page will fail. The browser has not parsed that portion of the page yet. Being able to render script that automatically executes when the page loads is nice, but it is more likely that you will want the code to execute based on an event fired from an HTML element on your page, such as the Click, Focus, or Blur events. To do this, you add an attribute to the HTML element you want the event to fire from. Listing 25-21 shows you how you can modify your control’s Render and PreRender methods to add this attribute.

www.it-ebooks.info

1004  ❘  Chapter 25   User and Server Controls

Listing 25-21:  Using client-side script and event attributes to validate data Protected Overrides Sub RenderContents(ByVal output As HtmlTextWriter) output.RenderBeginTag(HtmlTextWriterTag.Div)

VB

output.AddAttribute(HtmlTextWriterAttribute.Type, "text") output.AddAttribute(HtmlTextWriterAttribute.Id, Me.ClientID & "_i") output.AddAttribute(HtmlTextWriterAttribute.Name, Me.ClientID & "_i") output.AddAttribute(HtmlTextWriterAttribute.Value, Me.Text) output.AddAttribute("OnBlur", "ValidateText(this)") output.RenderBeginTag(HtmlTextWriterTag.Input) output.RenderEndTag() output.RenderEndTag() End Sub Protected Overrides Sub OnPreRender(ByVal e As System.EventArgs) Page.ClientScript.RegisterStartupScript(GetType(Page), "ControlFocus", "document.getElementById('" & Me.ClientID & "_i" & "').focus();", True) Page.ClientScript.RegisterClientScriptBlock( GetType(Page), "ValidateControl", "function ValidateText() {" & "if (ctl.value=='') {" & "alert('Please enter a value.');ctl.focus(); }" & "}", True) End Sub

C#

protected override void RenderContents(HtmlTextWriter output) { output.RenderBeginTag(HtmlTextWriterTag.Div); output.AddAttribute(HtmlTextWriterAttribute.Type, "text"); output.AddAttribute(HtmlTextWriterAttribute.Id, this.ClientID + "_i"); output.AddAttribute(HtmlTextWriterAttribute.Name, this.ClientID + "_i"); output.AddAttribute(HtmlTextWriterAttribute.Value, this.Text); output.AddAttribute("OnBlur", "ValidateText(this)"); output.RenderBeginTag(HtmlTextWriterTag.Input); output.RenderEndTag(); output.RenderEndTag(); } protected override void OnPreRender(EventArgs e) { Page.ClientScript.RegisterStartupScript( typeof(Page), "ControlFocus", "document.getElementById('" + this.ClientID + "_i" + "').focus();",

www.it-ebooks.info

Server Controls  ❘ 

1005

true); Page.ClientScript.RegisterClientScriptBlock( typeof(Page), "ValidateControl", "function ValidateText(ctl) {" + "if (ctl.value=='') {" + "alert('Please enter a value.'); ctl.focus(); }" + "}", true); }

As you can see, the TextBox control is modified to check for an empty string. An attribute that adds the JavaScript OnBlur event to the text box is also included. The OnBlur event fires when the control loses focus. When this happens, the client-side ValidateText method is executed, which is rendered to the client using RegisterClientScriptBlock. The rendered HTML is shown in Figure 25-12.

Figure 25-12

Embedding JavaScript in the page is powerful, but if you are writing large amounts of client-side code, you might want to consider storing the JavaScript in an external file. You can include this file in your HTML by using the RegisterClientScriptInclude method. This method renders a script tag using the URL you provide to it as the value of its src element.

Listing 25-22 shows how you can modify the validation added to the input element in Listing 25-20 to store the JavaScript validation function in an external file.

www.it-ebooks.info

1006  ❘  Chapter 25   User and Server Controls

Listing 25-22:  Adding client-side script include files to a Web page

VB

Protected Overrides Sub OnPreRender(ByVal e As System.EventArgs) Page.ClientScript.RegisterClientScriptInclude( "UtilityFunctions", "Listing25-23.js") Page.ClientScript.RegisterStartupScript(GetType(Page), "ControlFocus", "document.getElementById('" & Me.ClientID & "_i" & "').focus();", True) End Sub

C#

protected override void OnPreRender(EventArgs e) { Page.ClientScript.RegisterClientScriptInclude( "UtilityFunctions", "Listing25-23.js"); Page.ClientScript.RegisterStartupScript( typeof(Page), "ControlFocus", "document.getElementById('" + this.ClientID + "_i" + "').focus();", true); }

You have modified the OnPreRender event to register a client-side script file include, which contains the ValidateText function. You need to add a JScript file to the project and create the ValidateText function, as shown in Listing 25-23. Listing 25-23:  The validation JavaScript contained in the Jscript file // JScript File function ValidateText(ctl) { if (ctl.value==") { alert('Please enter a value.'); ctl.focus(); } }

The ClientScriptManager also provides methods for registering hidden HTML fields and adding script functions to the OnSubmit event.

Accessing Embedded Resources A great way to distribute application resources like JavaScript files, images, or resource files is to embed them directly into the compiled assembly. ASP.NET makes this easy by using the RegisterClientScriptResource method which is part of the ClientScriptManager. This method makes it possible for your Web pages to retrieve stored resources — like JavaScript files — from the compiled assembly at runtime. It works by using an HttpHandler to retrieve the requested resource from the assembly and return it to the client. The RegisterClientScriptResource method emits a

As you can see, the WebResource.axd handler is used to return the resource — in this case, the JavaScript file. You can use this method to retrieve any resource stored in the assembly, such as images or localized content strings from resource files.

www.it-ebooks.info

Server Controls  ❘ 

1007

Asynchronous Callbacks Finally, ASP.NET also includes a convenient mechanism for enabling basic AJAX behavior, or clientside callbacks, in a server control. Client-side callbacks enable you to take advantage of the XmlHttp components found in most modern browsers to communicate with the server without actually performing a complete postback. Figure 25-13 shows how client-side callbacks work in the ASP.NET Framework.

Using JavaScript, the browser creates an instance of the MSXML ActiveX control

The MSXML ActiveX control makes a request to the server

JavaScript handles the Callback method

JavaScript handles the ErrorCallback method

The Internet

The request raises the ICallbackEventHandler method RaiseCallbackEvent on the server

Method returns a string, or throws an exception

Figure 25-13

To enable callbacks in your server control, you implement the System.Web.UI.ICallBackEventHander interface. This interface requires you to implement two methods, the RaiseCallbackEvent method and the GetCallbackResult method. These server-side events fire when the client executes the callback. After you implement the interface, you want to tie your client-side events back to the server. You do this by using the Page.ClientScript.GetCallbackEventReference method. This method allows you to specify the two client-side functions: one to serve as the callback handler and one to serve as an error handler. Listing 25-24 demonstrates how you can modify the TextBox control’s Render methods and add the RaiseCallBackEvent method to use callbacks to perform validation. Listing 25-24:  Adding an asynchronous callback to validate data

VB

Protected Overrides Sub RenderContents(ByVal output As HtmlTextWriter) output.RenderBeginTag(HtmlTextWriterTag.Div) output.AddAttribute(HtmlTextWriterAttribute.Type, "text") output.AddAttribute(HtmlTextWriterAttribute.Id, Me.ClientID & "_i") output.AddAttribute(HtmlTextWriterAttribute.Name, Me.ClientID & "_i") output.AddAttribute(HtmlTextWriterAttribute.Value, Me.Text) output.AddAttribute("OnBlur", "ClientCallback();") output.RenderBeginTag(HtmlTextWriterTag.Input) output.RenderEndTag() output.RenderEndTag() End Sub Protected Overrides Sub OnPreRender(ByVal e As System.EventArgs) Page.ClientScript.RegisterClientScriptInclude(

continues

www.it-ebooks.info

1008  ❘  Chapter 25   User and Server Controls

Listing 25-24  (continued) "UtilityFunctions", "ServerControl1.js") Page.ClientScript.RegisterStartupScript(GetType(Page), "ControlFocus", "document.getElementById('" & Me.ClientID & "_i" & "').focus();", True) Page.ClientScript.RegisterStartupScript( GetType(Page), "ClientCallback", "function ClientCallback() {" & "args=document.getElementById('" & Me.ClientID & "_i" & "').value;" & Page.ClientScript.GetCallbackEventReference(Me, "args", "CallbackHandler", Nothing, "ErrorHandler", True) & "}", True) End Sub Public Sub RaiseCallbackEvent(ByVal eventArgument As String) _ Implements System.Web.UI.ICallbackEventHandler.RaiseCallbackEvent Dim result As Int32 If (Not Int32.TryParse(eventArgument, result)) Then Throw New Exception( "The method or operation is not implemented.") End If End Sub Public Function GetCallbackResult() As String _ Implements System.Web.UI.ICallbackEventHandler.GetCallbackResult Return "Valid Data" End Function

C#

protected override void RenderContents(HtmlTextWriter output) { output.RenderBeginTag(HtmlTextWriterTag.Div); output.AddAttribute(HtmlTextWriterAttribute.Type, "text"); output.AddAttribute(HtmlTextWriterAttribute.Id, this.ClientID + "_i"); output.AddAttribute(HtmlTextWriterAttribute.Name, this.ClientID + "_i"); output.AddAttribute(HtmlTextWriterAttribute.Value, this.Text); output.AddAttribute("OnBlur", "ClientCallback();"); output.RenderBeginTag(HtmlTextWriterTag.Input); output.RenderEndTag(); output.RenderEndTag(); } protected override void OnPreRender(EventArgs e) { Page.ClientScript.RegisterClientScriptInclude( "UtilityFunctions", "Listing25-25.js"); Page.ClientScript.RegisterStartupScript( typeof(Page), "ControlFocus", "document.getElementById('" + this.ClientID + "_i" + "').focus();", true); Page.ClientScript.RegisterStartupScript(

www.it-ebooks.info

Server Controls  ❘ 

1009

typeof(Page), "ClientCallback", "function ClientCallback() {" + "args=document.getElementById('" + this.ClientID + "_i" + "').value;" + Page.ClientScript.GetCallbackEventReference(this, "args", "CallbackHandler", null, "ErrorHandler", true) + "}", true); } #region ICallbackEventHandler Members public void RaiseCallbackEvent(string eventArgument) { int result; if (!Int32.TryParse(eventArgument, out result)) throw new Exception("The method or operation is not implemented."); } public string GetCallbackResult() { return "Valid Data"; } #endregion

As you can see, the OnBlur attribute has again been modified, this time by simply calling the ClientCallback method. This method is created and rendered during the PreRender event. The main purpose of this event is to populate the client-side args variable and call the client-side callback method. You are using the GetCallbackEventReference method to generate the client-side script that actually initiates the callback. The parameters passed to the method indicate which control is initiating the callback, the names of the client-side callback method, and the name of the callback method parameters. Table 25-3 provides more details on the GetCallbackEventReference arguments. Table 25-3 Parameter

Description

Control

Server control that initiates the callback.

Argument

Client-side variable used to pass arguments to the server-side event handler.

ClientCallback

Client-side function serving as the Callback method. This method fires when the server-side processing has completed successfully.

Context

Client-side variable that gets passed directly to the receiving client-side function. The context does not get passed to the server.

ClientErrorCallback

Client-side function serving as the Callback error-handler method. This method fires when the server-side processing encounters an error.

In the code, two client-side methods are called: CallbackHandler and ErrorHandler, respectively. The two method parameters are args and ctx. In addition to the server control code changes, the two client-side callback methods have been added to the JavaScript file. Listing 25-25 shows these new functions. Listing 25-25:  The client-side callback JavaScript functions // JScript File var args; var ctx; function ValidateText(ctl)

continues

www.it-ebooks.info

1010  ❘  Chapter 25   User and Server Controls

Listing 25-25  (continued) { if (ctl.value==") { alert('Please enter a value.'); ctl.focus(); } } function CallbackHandler(args,ctx) { alert("The data is valid"); } function ErrorHandler(args,ctx) { alert("Please enter a number"); }

Now, when you view your Web page in the browser, as soon as the text box loses focus, you perform a client-side callback to validate the data. The callback raises the RaiseCallbackEvent method on the server, which validates the value of the text box that was passed to it in the eventArguments. If the value is valid, you return a string and the client-side CallbackHandler function fires. If the value is invalid, you throw an exception, which causes the client-side ErrorHandler function to execute.

Browser Capabilities So far this chapter has described many powerful features, such as styling and emitting client-side scripts that you can utilize when writing your own custom control. But if you are taking advantage of these features, you must also consider how you can handle certain browsers, often called down-level browsers, that might not understand these advanced features or might not have them enabled. Being able to detect and react to down-level browsers is an important consideration when creating your control. ASP.NET includes some powerful tools you can use to detect the type and version of the browser making the page request, as well as what capabilities the browser supports.

.browser Files ASP.NET uses a highly flexible method for configuring, storing, and discovering browser capabilities. All browser identification and capability information is stored in .browser files. ASP.NET stores these files in the C:\Windows\Microsoft.NET\Framework\v4.0.xxxxx\CONFIG\Browsers directory. If you open this folder, you see that ASP.NET provides you with a variety of .browser files that describe the capabilities of most of today’s common desktop browsers, as well as information on browsers in devices such as PDAs and cellular phones. Open one of the browser files, and you see that the file contains all the identification and capability information for the browser. Listing 25-26 shows you the contents of the iPhone capabilities file. Listing 25-26:  A sample browser capabilities file
name="mobileDeviceModel" name="mobileDeviceManufacturer" name="isMobileDevice" name="canInitiateVoiceCall"

www.it-ebooks.info

value="IPhone" /> value="Apple" /> value="true" /> value="true" />

Server Controls  ❘ 

1011



value="IPod" /> value="Apple" /> value="true" />

The advantage of this method for storing browser capability information is that as new browsers are created or new versions are released, developers simply create or update a .browser file to describe the capabilities of that browser.

Accessing Browser Capability Information Now that you have seen how ASP.NET stores browser capability information, you need to know how you can access this information at runtime and program your control to change what it renders based on the browser. To access capability information about the requesting browser, you can use the Page.Request .Browser property. This property gives you access to the System.Web.HttpBrowserCapabilities class, which provides information about the capabilities of the browser making the current request. The class provides you with a myriad of attributes and properties that describe what the browser can support and render and what it requires. Lists use this information to add capabilities to the TextBox control. Listing 25-27 shows how you can detect browser capabilities to make sure a browser supports JavaScript. Listing 25-27:  Detecting browser capabilities in server-side code Protected Overrides Sub OnPreRender(ByVal e As System.EventArgs) If (Page.Request.Browser.EcmaScriptVersion.Major > 0) Then

VB

Page.ClientScript.RegisterClientScriptInclude( "UtilityFunctions", "Listing25-25.js") Page.ClientScript.RegisterStartupScript( GetType(Page), "ClientCallback", "function ClientCallback() {" & "args=document.getElementById('" & Me.ClientID & "_i" & "').value;" & Page.ClientScript.GetCallbackEventReference(Me, "args", "CallbackHandler", Nothing, "ErrorHandler", True) + "}", True) Page.ClientScript.RegisterStartupScript(GetType(Page), "ControlFocus", "document.getElementById('" & Me.ClientID & "_i" & "').focus();", True) End If End Sub

C#

protected override void OnPreRender(EventArgs e) { if (Page.Request.Browser.EcmaScriptVersion.Major > 0)

continues

www.it-ebooks.info

1012  ❘  Chapter 25   User and Server Controls

Listing 25-27  (continued) { Page.ClientScript.RegisterClientScriptInclude( "UtilityFunctions", "Listing25-25.js"); Page.ClientScript.RegisterStartupScript( typeof(Page), "ControlFocus", "document.getElementById('" + this.ClientID + "_i" + "').focus();", true); Page.ClientScript.RegisterStartupScript( typeof(Page), "ClientCallback", "function ClientCallback() {" + "args=document.getElementById('" + this.ClientID + "_i" + "').value;" + Page.ClientScript.GetCallbackEventReference(this, "args", "CallbackHandler", null, "ErrorHandler", true) + "}", true); } }

This is a very simple sample, but it gives you an idea of what is possible using the HttpBrowserCapabilities class.

Using ViewState When developing Web applications, remember that they are built on the stateless HTTP protocol. ASP.NET gives you a number of ways to give users the illusion that they are using a stateful application, but perhaps the most ubiquitous is called ViewState. ViewState enables you to maintain the state of the objects and controls that are part of the Web page through the page’s life cycle by storing the state of the controls in a hidden form field that is rendered as part of the HTML. The state contained in the form field can then be used by the application to reconstitute the page’s state when a postback occurs. Figure 25-14 shows how ASP.NET stores ViewState information in a hidden form field.

Figure 25-14

www.it-ebooks.info

server Controls

❘ 1013

Notice that the page contains a hidden form field named _ViewState. The value of this form field is the ViewState for your Web page. By default, ViewState is enabled in all in-box server controls shipped with ASP.NET. If you write customer server controls, however, you are responsible for ensuring that a control is participating in the use of ViewState by the page. The ASP.NET ViewState is basically a storage format that enables you to save and retrieve objects as key/value pairs. As you see in Figure 25-14, these objects are then serialized by ASP.NET and persisted as an encrypted string, which is pushed to the client as a hidden HTML form field. When the page posts back to the server, ASP.NET can use this hidden form field to reconstitute the StateBag, which you can then access as the page is processed on the server. Because the ViewState can sometimes grow to be very large and can therefore affect the overall page size, you might consider an alternate method of storing the ViewState information. You can create your own persistence mechanism by deriving a class from the System.Web.UI.PageStatePersister class and overriding its Load and Save methods. As shown in Listing 25-28, by default, the Text property included with the ASP.NET Server Control template is set up to store its value in ViewState. lisTing 25 -28: The Text property’s use of Viewstate

VB

Property Text() As String Get Dim s As String = CStr(ViewState("Text")) If s Is Nothing Then Return "[" + Me.ID + "]" Else Return s End If End Get Set(ByVal Value As String) ViewState("Text") = Value End Set End Property

C#

public string Text { get { String s = (String)ViewState["Text"]; return ((s == null) ? "[" + this.ID + "]": s); } set { ViewState["Text"] = value; } }

When creating new properties in an ASP.NET server control, you should remember to use this same technique to ensure that the values set by the end user in your control will be persisted across page postbacks.

www.it-ebooks.info

1014



chaPTer 25 user And server controls

Note that the loading of ViewState happens after the OnInit event has been raised by the page. If your control makes changes to itself or another server control before the event has been raised, the changes are not saved to the ViewState.

Types and Viewstate As mentioned in the preceding section, the ViewState is basically a generic collection of objects, but not all objects can be added to the ViewState. Only types that can be safely persisted can be used in the ViewState, so objects such as database connections or fi le handles should not be added to the ViewState. Additionally, certain data types are optimized for use in the ViewState. When adding data to the ViewState, try to package the data into these types: ➤

Primitive Types (Int32, Boolean, and so on).



Arrays of Primitive Types.



ArrayList, HashTable.



Pair, Triplet.



Color, DataTime.



String, IndexedString.



HybridDictionary of these types.



Objects that have a TypeConverter available. Be aware, however, that there is a reduction in performance if you use these types.



Objects that are serializable (marked with the Serializable attribute).

Control state At times, your control must store small amounts of critical, usually private, information across postbacks. To allow for the storage of this type of information, even if a developer disables ViewState, ASP.NET includes a separate type of ViewState called ControlState. ControlState is essentially a private ViewState for your control only, and it is not affected when ViewState is turned off. Two methods, SaveViewState and LoadViewState, provide access to ControlState; however, the implementation of these methods is left up to you. Listing 25-29 shows how you can use the LoadControlState and SaveViewState methods. lisTing 25 -29: Using Controlstate in a server control

VB

")> Public Class ServerControl1 Inherits WebControl Dim state As String Protected Overrides Sub OnInit(ByVal e As System.EventArgs) Page.RegisterRequiresControlState(Me) MyBase.OnInit(e) End Sub Protected Overrides Sub LoadControlState(ByVal savedState As Object) state = CStr(savedState) End Sub Protected Overrides Function SaveControlState() As Object

www.it-ebooks.info

Server Controls  ❘ 

1015

Return CType("ControlSpecificData", Object) End Function Protected Overrides Sub Render(ByVal output As System.Web.UI.HtmlTextWriter) output.Write("Control State: " & state) End Sub End Class

C#

[DefaultProperty("Text")] [ToolboxData("<{0}:ServerControl1 runat=server>")] public class ServerControl1 : WebControl { string state; protected override void OnInit(EventArgs e) { Page.RegisterRequiresControlState(this); base.OnInit(e); } protected override void LoadControlState(object savedState) { state = (string)savedState; } protected override object SaveControlState() { return (object)"ControlSpecificData"; } protected override void RenderContents(HtmlTextWriter output) { output.Write("Control State: " + state); } }

Controls intending to use ControlState must call the Page.RegisterRequiresControlState method before attempting to save control state data. Additionally, the RegisterRequiresControlState method must be called for each page load because the value is not retained through page postbacks.

Raising Postback Events As you have seen in this chapter, ASP.NET provides a very powerful set of tools you can use to develop server controls and emit them to a client browser. But this is still one-way communication because the server only pushes data to the client. It would be useful if the server control could send data back to the server. The process of sending data back to the server is generally known as a page postback. You experience a page postback any time you click a form button or link that causes the page to make a new request to the Web server. ASP.NET provides a rich framework for handling postbacks from ASP.NET Web pages. A development model that mimics the standard Windows Forms event model is provided that enables you to use controls that, even though they are rendered in the client browser, can raise events in server-side code. It also provides an easy mechanism for plugging a server control into that framework, allowing you to create controls that can initiate a page postback. Figure 25-15 shows the ASP.NET postback framework.

www.it-ebooks.info

1016  ❘  Chapter 25   User and Server Controls

Init Load State Process Postback Data IPostbackDataHandler IPostbackEventHandler

Load Postback Events Save State PreRender Render Unload

Figure 25-15

To initiate a page postback, by default ASP.NET uses client-side scripting. If you want your control to be able to initiate a postback, you must attach the postback initiation script to a HTML element event using the GetPostBackEventReference method during the controls render method. Listing 25-30 shows how you can attach the postback client script to the onClick event of a standard HTML Button element. Listing 25-30:  Adding postback capabilities to a server control Protected Overrides Sub RenderContents(ByVal output As HtmlTextWriter) Dim p As New PostBackOptions(Me)

VB

output.AddAttribute(HtmlTextWriterAttribute.Onclick, Page.ClientScript.GetPostBackEventReference(p)) output.AddAttribute(HtmlTextWriterAttribute.Value, "My Button") output.AddAttribute(HtmlTextWriterAttribute.Id, Me.ClientID & "_i") output.AddAttribute(HtmlTextWriterAttribute.Name, Me.ClientID & "_i") output.RenderBeginTag(HtmlTextWriterTag.Button) output.Write("My Button") output.RenderEndTag()

www.it-ebooks.info

Server Controls  ❘ 

1017

End Sub

C#

protected override void RenderContents(HtmlTextWriter output) { PostBackOptions p = new PostBackOptions(this); output.AddAttribute(HtmlTextWriterAttribute.Onclick, Page.ClientScript.GetPostBackEventReference(p)); output.AddAttribute(HtmlTextWriterAttribute.Id, this.ClientID + "_i"); output.AddAttribute(HtmlTextWriterAttribute.Name, this.ClientID + "_i"); output.RenderBeginTag(HtmlTextWriterTag.Button); output.Write("My Button"); output.RenderEndTag(); }

When the GetPostBackEventReference method is called, it requires a PostBackOptions object be passed to it. The PostBackOptions object allows you to specify a number of configuration options that influence how ASP.NET will initiate the postback. You can add the postback JavaScript to any client-side event, or even add the code to a client-side function if you want to include some additional pre-postback logic for your control. Now that the control can initiate a postback, you may want to add events to your control that execute during the pages postback. To raise server-side events from a client-side object, you implement the System.Web.IPostBackEventHandler interface. Listing 25-31 shows how to do this for the Button in the previous listing. Listing 25-31:  Handling postback events in a server control

VB

")> Public Class ServerControl1 Inherits WebControl Implements IPostBackEventHandler Protected Overrides Sub RenderContents( ByVal output As HtmlTextWriter) Dim p As New PostBackOptions(Me) output.AddAttribute(HtmlTextWriterAttribute.Onclick, Page.ClientScript.GetPostBackEventReference(p)) output.AddAttribute(HtmlTextWriterAttribute.Value, "My Button") output.AddAttribute(HtmlTextWriterAttribute.Id, Me.ClientID & "_i") output.AddAttribute(HtmlTextWriterAttribute.Name, Me.ClientID & "_i") output.RenderBeginTag(HtmlTextWriterTag.Button) output.Write("My Button") output.RenderEndTag() End Sub Public Event Click() Public Sub OnClick(ByVal args As EventArgs) RaiseEvent Click() End Sub Public Sub RaisePostBackEvent(ByVal eventArgument As String) _ Implements System.Web.UI.IPostBackEventHandler.RaisePostBackEvent OnClick(EventArgs.Empty)

continues

www.it-ebooks.info

1018  ❘  Chapter 25   User and Server Controls

Listing 25-31  (continued) End Sub End Class

C#

[DefaultProperty("Text")] [ToolboxData("<{0}:ServerControl1 runat=server>")] public class ServerControl1 : WebControl, IPostBackEventHandler { protected override void RenderContents(HtmlTextWriter output) { PostBackOptions p = new PostBackOptions(this); output.AddAttribute(HtmlTextWriterAttribute.Onclick, Page.ClientScript.GetPostBackEventReference(p)); output.AddAttribute(HtmlTextWriterAttribute.Id, this.ClientID); output.AddAttribute(HtmlTextWriterAttribute.Name, this.ClientID); output.RenderBeginTag(HtmlTextWriterTag.Button); output.Write("My Button"); output.RenderEndTag(); } #region IPostBackEventHandler Members public event EventHandler Click; public virtual void OnClick(EventArgs e) { if (Click != null) { Click(this, e); } } public void RaisePostBackEvent(string eventArgument) { OnClick(EventArgs.Empty); } #endregion }

When the user clicks the Button, a page postback occurs, and ASP.NET calls the RaisePostBackEvent method in the control, which lets you raise a server-side event. If several different client events in your control can initiate a postback, you can change the behavior of your control by using the RaisePostBackEvent method’s eventArgument parameter to determine which element caused the postback. The eventArgument parameter is set using the PostBackOptions object mentioned previously.

Handling Postback Data Now that you have learned how to store control data in ViewState and add postback capabilities to a control you can enable the control to handle data a user has entered into form fields on the page. When an ASP.NET control initiates a postback, all the form data from the page is posted to the server. A server control can access and interact with that data, storing the information in ViewState and completing the illusion of a stateful application. To access postback data, your control must implement the System.Web.IPostBackDataHandler interface. This interface allows ASP.NET to hand to your control the form data that is passed back to the server during the postback.

www.it-ebooks.info

Server Controls  ❘ 

1019

The IPostBackDataHandler interface requires two methods to be implemented, the LoadPostData and RaisePostBackDataChangedEvent methods. Listing 25-32 shows how you implement the IPostBackDataHandler interface method in a simple text input control. Listing 25-32:  Accessing postback data in a server control

VB

")> Public Class ServerControl1 Inherits WebControl Implements IPostBackEventHandler, IPostBackDataHandler Property Text() As String Get Dim s As String = CStr(ViewState("Text")) If s Is Nothing Then Return String.Empty Else Return s End If End Get Set(ByVal Value As String) ViewState("Text") = Value End Set End Property Protected Overrides Sub RenderContents( ByVal output As HtmlTextWriter) Dim p As New PostBackOptions(Me) output.AddAttribute(HtmlTextWriterAttribute.Id, Me.ClientID) output.AddAttribute(HtmlTextWriterAttribute.Name, Me.ClientID) output.AddAttribute(HtmlTextWriterAttribute.Value, Me.Text) output.RenderBeginTag(HtmlTextWriterTag.Input) output.RenderEndTag() End Sub Public Function LoadPostData(ByVal postDataKey As String, ByVal postCollection As _ System.Collections.Specialized.NameValueCollection) _ As Boolean Implements _ System.Web.UI.IPostBackDataHandler.LoadPostData Me.Text = postCollection(postDataKey) Return False End Function Public Sub RaisePostDataChangedEvent() _ Implements _ System.Web.UI.IPostBackDataHandler.RaisePostDataChangedEvent End Sub Public Event Click() Public Sub OnClick(ByVal args As EventArgs) RaiseEvent Click()

continues

www.it-ebooks.info

1020  ❘  Chapter 25   User and Server Controls

Listing 25-32  (continued) End Sub Public Sub RaisePostBackEvent(ByVal eventArgument As String) _ Implements System.Web.UI.IPostBackEventHandler.RaisePostBackEvent OnClick(EventArgs.Empty) End Sub End Class

C#

[DefaultProperty("Text")] [ToolboxData("<{0}:ServerControl1 runat=server>")] public class ServerControl1 : WebControl, IPostBackEventHandler, IPostBackDataHandler { [Bindable(true)] [Category("Appearance")] [DefaultValue("")] [Localizable(true)] public string Text { get { String s = (String)ViewState["Text"]; return ((s == null) ? "[" + this.ID + "]" : s); } set { ViewState["Text"] = value; } } protected override void RenderContents(HtmlTextWriter output) { PostBackOptions p = new PostBackOptions(this); output.AddAttribute(HtmlTextWriterAttribute.Id, this.ClientID); output.AddAttribute(HtmlTextWriterAttribute.Name, this.ClientID); output.AddAttribute(HtmlTextWriterAttribute.Value, this.Text); output.RenderBeginTag(HtmlTextWriterTag.Input); output.RenderEndTag(); } #region IPostBackDataHandler Members public bool LoadPostData(string postDataKey, System.Collections.Specialized.NameValueCollection postCollection) { this.Text = postCollection[postDataKey]; return false; } public void RaisePostDataChangedEvent() { } #endregion #region IPostBackEventHandler Members public event EventHandler Click; public virtual void OnClick(EventArgs e)

www.it-ebooks.info

Server Controls  ❘ 

1021

{ if (Click != null) { Click(this, e); } } public void RaisePostBackEvent(string eventArgument) { OnClick(EventArgs.Empty); } #endregion }

During a postback, ASP.NET will call the LoadPostData method for this control, passing to it as a NameValueCollection any data submitted with the form. The postDataKey method parameter allows the control to access the postback data specific to it from the NameValueCollection. Using the method parameters you can save the input element’s text to the server control’s Text property. If you remember the earlier ViewState example, the Text property will save the value to ViewState, allowing the control to automatically repopulate the input element’s value when another page postback occurs. The LoadPostData method requires you to return a Boolean value from the method. This value indicates whether ASP.NET should call the RaisePostBackDataChangedEvent method after the LoadPostData method returns. For example, if you created a TextChanged event in the control to notify you the controls text has changed, you would want to return True from this method so that you could subsequently raise that event in the RaisePostDataChangedEvent method.

Composite Controls So far, in looking at Server controls, you have concentrated on emitting a single HTML element; but this can be fairly limiting. Creating extremely powerful controls often requires that you combine several HTML elements together. Although you can always use the RenderContents method to emit multiple HTML elements, ASP.NET also allows you to emit existing ASP.NET controls from within a custom server control. These types of controls are called composite controls. To demonstrate how easy creating a composite control can be, try changing the control shown in Listing 25-32 into a composite control. Listing 25-33 shows how you can do this. Listing 25-33:  Creating a composite control

VB

")> Public Class ServerControl1 Inherits System.Web.UI.WebControls.CompositeControl Protected textbox As TextBox = New TextBox() Protected Overrides Sub CreateChildControls() Me.Controls.Add(textbox) End Sub End Class

C#

using System; [DefaultProperty("Text")] [ToolboxData("<{0}:ServerControl1 runat=server>")] public class ServerControl1 : CompositeControl

continues

www.it-ebooks.info

1022  ❘  Chapter 25   User and Server Controls

Listing 25-33  (continued) { protected TextBox textbox = new TextBox(); protected override void CreateChildControls() { this.Controls.Add(textbox); } }

A number of things in this listing are important. First, notice that the control class is now inheriting from CompositeControl, rather than WebControl. Deriving from CompositeControl gives you a few extra features specific to this type of control. Second, notice that no Render method appears in this code. Instead, you simply create an instance of another type of server control and add that to the Controls collection in the CreateChildControls method. When you run this sample, you see that it renders a text box just like the last control did. In fact, the HTML that it renders is almost identical. When you drop a composite control (such as the control from the previous sample) onto the design surface, notice that even though you are leveraging a powerful ASP.NET TextBox control within the control, none of that control’s properties are exposed to you in the Properties Explorer. To expose child control properties through the parent container, you must create corresponding properties in the parent control. For example, if you want to expose the ASP.NET text box Text property through the parent control, you create a Text property. Listing 25-34 shows how to do this. Listing 25-34:  Exposing control properties in a composite control

VB

")> Public Class ServerControl1 Inherits System.Web.UI.WebControls.CompositeControl Protected textbox As TextBox = New TextBox() Public Property Text() As String Get EnsureChildControls() Return textbox.Text End Get Set(ByVal value As String) EnsureChildControls() textbox.Text = value End Set End Property Protected Overrides Sub CreateChildControls() Me.Controls.Add(textbox) Me.ChildControlsCreated = True End Sub End Class

C#

[DefaultProperty("Text")] [ToolboxData("<{0}:ServerControl1 runat=server>")] public class ServerControl1 : CompositeControl { protected TextBox textbox = new TextBox(); public string Text

www.it-ebooks.info

Server Controls  ❘ 

1023

{ get { EnsureChildControls(); return textbox.Text; } set { EnsureChildControls(); textbox.Text = value; } } protected override void CreateChildControls() { this.Controls.Add(textbox); this.ChildControlsCreated = true; } }

Notice that you use this property simply to populate the underlying control’s properties. Also notice that before you access the underlying control’s properties, you always call the EnsureChildControls method. This method ensures that children of the container control have actually been initialized before you attempt to access them.

Templated Controls In addition to composite controls, you can also create templated controls. Templated controls allow the developer to specify a portion of the HTML that is used to render the control, and to nest other controls inside of a container control. You might be familiar with the Repeater or DataList control. These are both templated controls that let you specify how you want the bound data to be displayed when the page renders. To demonstrate a templated control, the following code gives you a simple example of displaying a simple text message on a Web page. Because the control is a templated control, the developer has complete control over how the message is displayed. To get started, create the Message server control that will be used as the template inside of a container control. Listing 25-35 shows the class that simply extends the existing Panel control by adding two additional properties, Name and Text, and a new constructor. Listing 25-35:  Creating the templated control’s inner control class

VB

Public Class Message Inherits System.Web.UI.WebControls.Panel Implements System.Web.UI.INamingContainer Public Property Name() As String Public Property Text() As String End Class

C#

public class Message : Panel, INamingContainer { public string Name { get; internal set; } public string Text { get; internal set; } }

As you will see in a moment, you can access the public properties exposed by the Message class to insert dynamic content into the template. You will also see how you can display the values of the Name and Text properties as part of the rendered template control.

www.it-ebooks.info

1024  ❘  Chapter 25   User and Server Controls

Next, as shown in Listing 25-36, create a new server control that will be the container for the Message control. This server control is responsible for rendering any template controls nested in it. Listing 25-36:  Creating the template control container class

VB

" & "")> Public Class TemplatedControl Inherits System.Web.UI.WebControls.WebControl Public Property TemplateMessage() As Message Public Property MessageTemplate() As ITemplate Public Property Name() As String Public Property Text() As String Public Overrides Sub DataBind() EnsureChildControls() ChildControlsCreated = True MyBase.DataBind() End Sub Protected Overrides Sub CreateChildControls() Me.Controls.Clear() Me.TemplateMessage = New Message() With {.Name = Name, .Text = Text} If Me.MessageTemplate Is Nothing Then Me.MessageTemplate = New DefaultMessageTemplate() End If Me.MessageTemplate.InstantiateIn(Me.TemplateMessage) Controls.Add(Me.TemplateMessage) End Sub Protected Overrides Sub RenderContents( ByVal writer As System.Web.UI.HtmlTextWriter) EnsureChildControls() ChildControlsCreated = True MyBase.RenderContents(writer) End Sub End Class

C#

[DefaultProperty("Text")] [ToolboxData("<{0}:TemplatedControl runat=server>" + "")] public class TemplatedControl : WebControl { [Browsable(false)] public Message TemplateMessage {get;internal set;} [PersistenceMode(PersistenceMode.InnerProperty)]

www.it-ebooks.info

Server Controls  ❘ 

1025

[TemplateContainer(typeof(Message))] public virtual ITemplate MessageTemplate {get;set;} [Bindable(true)] [DefaultValue("")] public string Name {get;set;} [Bindable(true)] [DefaultValue("")] public string Text { get; set; } public override void DataBind() { EnsureChildControls(); ChildControlsCreated = true; base.DataBind(); } protected override void CreateChildControls() { this.Controls.Clear(); this.TemplateMessage = new Message() { Name=Name, Text=Text}; if (this.MessageTemplate == null) { this.MessageTemplate = new DefaultMessageTemplate(); } this.MessageTemplate.InstantiateIn(this.TemplateMessage); Controls.Add(this.TemplateMessage); } protected override void RenderContents(HtmlTextWriter writer) { EnsureChildControls(); ChildControlsCreated = true; base.RenderContents(writer); } }

To start to dissect this sample, first notice the MessageTemplate property. This property allows Visual Studio to understand that the control will contain a template and allows it to display the IntelliSense for that template. The property has been marked with the PersistanceMode attribute indicating that the template control should be persisted as an inner property within the control’s tag in the ASPX page. Additionally, the property is marked with the TemplateContainer attribute, which helps ASP.NET figure out what type of template control this property represents. In this case, it’s the Message template control you created earlier. The container control exposes two public properties, Name and Text. These properties are used to populate the Name and Text properties of the Message control because that class does not allow developers to set the properties directly. Finally, the CreateChildControls method, called by the DataBind method, does most of the heavy lifting in this control. It creates a new Message object, passing the values of Name and Text as constructor values. After the CreateChildControls method completes, the base DataBind operation continues to execute. This is important because that is where the evaluation of the Name and Text properties occurs, which allows you to insert these properties values into the template control. After the control and template are created, you can drop them onto a test Web page. Listing 25-37 shows how the control can be used to customize the display of the data.

www.it-ebooks.info

1026  ❘  Chapter 25   User and Server Controls

Listing 25-37:  Adding a templated control to a Web page <%@ Page Language="VB" %>

VB

<%@ Register Assembly="ServerControl1" Namespace="ServerControl1" TagPrefix="cc1" %>

Templated Web Controls
The user '<%# Container.Name %>' has a message for you:
"<%#Container.Text%>"


C#



As you can see in the listing, the control contains a MessageTemplate within it, which has been customized to display the Name and Text values. Figure 25-16 shows this page after it has been rendered in the browser. One additional thing to consider when creating templated controls is what should happen if the developer does not specify a template for the control. In the previous example, if you removed the MessageTemplate from the TemplateContainer, a NullReferenceException would occur when you tried to run your Web page because the container control’s MessageTemplate property would return a null value. To prevent this, you can include a default template class as part of the container control. An example of a default template is shown in Listing 25-38.

Figure 25-16

www.it-ebooks.info

Server Controls  ❘ 

1027

Listing 25-38:  Creating the templated control’s default template class Friend Class DefaultMessageTemplate Implements ITemplate

VB

Public Sub InstantiateIn(ByVal container As System.Web.UI.Control) _ Implements System.Web.UI.ITemplate.InstantiateIn Dim l As New Literal() l.Text = "No MessageTemplate was included." container.Controls.Add(l) End Sub End Class

C#

internal sealed class DefaultMessageTemplate : ITemplate { public void InstantiateIn(Control container) { Literal l = new Literal(); l.Text = "No MessageTemplate was included."; container.Controls.Add(l); } }

Notice that the DefaultMessageTemplate implements the ITemplate interface. This interface requires that the InstantiateIn method be implemented, which you use to provide the default template content. To include the default template, simply add the class to the TemplatedControl class. You also need to modify the CreateChildControls method to detect the null MessageTemplate and instead create an instance of and use the default template. VB

If template = Nothing Then template = New DefaultMessageTemplate() End If

C#

if (template == null) { template = new DefaultMessageTemplate(); }

Design-Time Experiences So far in this chapter, you concentrated primarily on what gets rendered to the client’s browser, but the browser is not the only consumer of server controls. Visual Studio and the developer using a server control are also consumers, and you need to consider their experiences when using your control. ASP.NET offers numerous ways to give developers using your control a great design-time experience. Some of these require no additional coding, such as the WYSIWYG rendering of user controls and basic server controls. For more complex scenarios, ASP.NET includes a variety of options that allow you to give the developer an outstanding design-time experience when using your control. When you write server controls, a priority should be to give the developer a design-time experience that closely replicates the runtime experience. This means altering the appearance of the control on the design surface in response to changes in control properties and the introduction of other server controls onto the design surface. Three main components are involved in creating the design-time behaviors of a server control: ➤➤

Type converters

➤➤

Designers

➤➤

UI type editors

Because a chapter can be written for each one of these topics, this section gives you just an overview of each, how they tie into a control’s design-time behavior, and some simple examples of their use.

www.it-ebooks.info

1028  ❘  Chapter 25   User and Server Controls

Type Converters TypeConverter is a class that allows you to perform conversions between one type and another. Visual

Studio uses type converters at design time to convert object property values to String types so that they can be displayed on the Property Browser, and it returns them to their original types when the developer changes the property. ASP.NET includes a wide variety of type converters you can use when creating your control’s design-time behavior. These range from converters that allow you to convert most numeric types, to converters that let you convert Fonts, Colors, DataTimes, and Guids. The easiest way to see what type converters are available to you in the .NET Framework is to search for types in the framework that derive from the TypeConverter class using the MSDN Library help. After you have found a type converter that you want to use on a control property, mark the property with a TypeConverter attribute, as shown in Listing 25-39. Listing 25-39:  Applying the TypeConverter attribute to a property

VB

_ _ _ _ Property BookId() As System.Guid Get Return _bookid End Get Set(ByVal Value As System.Guid) _bookid = Value End Set End Property

C#

[Bindable(true)] [Category("Appearance")] [DefaultValue("")] [TypeConverter(typeof(GuidConverter))] public Guid BookId { get { return _bookid; } set { _bookid = value; } }

In this example, a property is exposed that accepts and returns an object of type Guid. The Property Browser cannot natively display a Guid object, so you convert the value to a string so that it can be displayed properly in the property browser. Marking the property with the TypeConverter attribute and, in this case, specifying the GuidConverter as the type converter you want to use, allows complex objects like a Guid to display properly in the Property Browser.

Custom Type Converters Creating your own custom type converters if none of the in-box converters fit into your scenario is also possible. Type converters derive from the System.ComponentModel.TypeConverter class. Listing 25-40 shows a custom type converter that converts a custom object called Name to and from a string.

www.it-ebooks.info

Server Controls  ❘ 

1029

Listing 25-40:  Creating a custom type converter

VB

Imports System Imports System.ComponentModel Imports System.Globalization Public Class Name Private _first As String Private _last As String Public Sub New(ByVal first As String, ByVal last As String) _first = first _last = last End Sub Public Property First() As String Get Return _first End Get Set(ByVal value As String) _first = value End Set End Property Public Property Last() As String Get Return _last End Get Set(ByVal value As String) _last = value End Set End Property End Class Public Class NameConverter Inherits TypeConverter Public Overrides Function CanConvertFrom(ByVal context As _ ITypeDescriptorContext, ByVal sourceType As Type) As Boolean If (sourceType Is GetType(String)) Then Return True End If Return MyBase.CanConvertFrom(context, sourceType) End Function Public Overrides Function ConvertFrom( _ ByVal context As ITypeDescriptorContext, _ ByVal culture As CultureInfo, ByVal value As Object) As Object If (value Is GetType(String)) Then Dim v As String() = (CStr(value).Split(New [Char]() {" "c})) Return New Name(v(0), v(1)) End If Return MyBase.ConvertFrom(context, culture, value) End Function Public Overrides Function ConvertTo( _ ByVal context As ITypeDescriptorContext, _ ByVal culture As CultureInfo, ByVal value As Object, _ ByVal destinationType As Type) As Object

continues

www.it-ebooks.info

1030  ❘  Chapter 25   User and Server Controls

Listing 25-40  (continued) If (destinationType Is GetType(String)) Then Return (CType(value, Name).First + " " + (CType(value, Name).Last)) End If Return MyBase.ConvertTo(context, culture, value, destinationType) End Function End Class

C#

using System; using System.ComponentModel; using System.Globalization; public class Name { private string _first; private string _last; public Name(string first, string last) { _first=first; _last=last; } public string First { get{ return _first;} set { _first = value;} } public string Last { get { return _last;} set { _last = value;} } } public class NameConverter : TypeConverter { public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) { if (sourceType == typeof(string)) { return true; } return base.CanConvertFrom(context, sourceType); } public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) { if (value is string) { string[] v = ((string)value).Split(new char[] {' '}); return new Name(v[0],v[1]); } return base.ConvertFrom(context, culture, value); } public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) { if (destinationType == typeof(string)) { return ((Name)value).First + " " + ((Name)value).Last;

www.it-ebooks.info

Server Controls  ❘ 

1031

} return base.ConvertTo(context, culture, value, destinationType); } }

The NameConverter class overrides three methods, CanConvertFrom, ConvertFrom, and ConvertTo. The CanConvertFrom method allows you to control what types the converter can convert from. The ConvertFrom method converts the string representation back into a Name object, and ConvertTo converts the Name object into a string representation. After you have built your type converter, you can use it to mark properties in your control with the TypeConverter attribute, as you saw in Listing 25-39.

Control Designers Controls that live on the Visual Studio design surface depend on control designers to create the design-time experience for the end user. Control designers, for both WinForms and ASP.NET, are classes that derive from the System.ComponentModel.Design.ComponentDesigner class. .NET provides an abstracted base class specifically for creating ASP.NET control designers called the System.Web.UI.Design .ControlDesigner. To access these classes you need to add a reference to the System.Design.dll assembly to your project. .NET includes a number of in-box control designer classes that you can use when creating a custom control; but as you develop server controls, you see that .NET automatically applies a default designer. The designer it applies is based on the type of control you are creating. For instance, when you created your first TextBox control, Visual Studio used the ControlDesigner class to achieve the WYSIWYG design-time rendering of the text box. If you develop a server control derived from the ControlContainer class, .NET automatically use the ControlContainerDesigner class as the designer. You can also explicitly specify the designer you want to use to render your control at design time using the Designer attribute on your control’s class, as shown in Listing 25-41. Listing 25-41:  Adding a Designer attribute to a control class

VB

C#

_ ")> _ _ Public Class WebCustomControl1 Inherits System.Web.UI.WebControls.WebControl [DefaultProperty("Text")] [ToolboxData("<{0}:WebCustomControl1 runat=server>")] [Designer(typeof(System.Web.UI.Design.ControlDesigner))] public class WebCustomControl1 : WebControl

Notice that the Designer attribute has been added to the WebCustomControl1 class. You have specified that the control should use the ControlDesigner class as its designer. Other in-box designers you could have specified are: ➤➤

CompositeControlDesigner

➤➤

TemplatedControlDesigner

➤➤

DataSourceDesigner

Each designer provides a specific design-time behavior for the control, and you can select one that is appropriate for the type of control you are creating.

Design-Time Regions As you saw earlier, ASP.NET allows you to create server controls that consist of other server controls and text. ASP.NET allows you to create server controls that have design-time editable portions using a technique

www.it-ebooks.info

1032  ❘  Chapter 25   User and Server Controls

called designer regions. Designer regions enable you to create multiple, independent regions defined within a single control and respond to events raised by a design region. This might be the designer drawing a control on the design surface or the user clicking an area of the control or entering or exiting a template edit mode. To show how you can use designer regions, create a container control to which you can apply a custom control designer, as shown in Listing 25-42. Listing 25-42:  Creating a composite control with designer regions

VB

_ " & _ "")> _ Public Class MultiRegionControl Inherits CompositeControl ' Define the templates that represent 2 views on the control Private _view1 As ITemplate Private _view2 As ITemplate ' These properties are inner properties _ Public Overridable Property View1() As ITemplate Get Return _view1 End Get Set(ByVal value As ITemplate) _view1 = value End Set End Property _ Public Overridable Property View2() As ITemplate Get Return _view2 End Get Set(ByVal value As ITemplate) _view2 = value End Set End Property ' The current view on the control; 0= view1, 1=view2, 2=all views Private _currentView As Int32 = 0 Public Property CurrentView() As Int32 Get Return _currentView End Get Set(ByVal value As Int32) _currentView = value End Set End Property Protected Overrides Sub CreateChildControls() MyBase.CreateChildControls() Controls.Clear() Dim template As ITemplate = View1 If (_currentView = 1) Then template = View2 End If Dim p As New Panel()

www.it-ebooks.info

Server Controls  ❘ 

1033

Controls.Add(p) If (Not template Is Nothing) Then template.InstantiateIn(p) End If End Sub End Class

C#

[Designer(typeof(MultiRegionControlDesigner))] [ToolboxData("<{0}:MultiRegionControl runat=\"server\" width=\"100%\">" + "")] public class MultiRegionControl : CompositeControl { // Define the templates that represent 2 views on the control private ITemplate _view1; private ITemplate _view2; // These properties are inner properties [PersistenceMode(PersistenceMode.InnerProperty), DefaultValue(null)] public virtual ITemplate View1 { get { return _view1;} set { _view1 = value;} } [PersistenceMode(PersistenceMode.InnerProperty), DefaultValue(null)] public virtual ITemplate View2 { get { return _view2;} set { _view2 = value;} } // The current view on the control; 0= view1, 1=view2, 2=all views private int _currentView = 0; public int CurrentView { get { return _currentView;} set { _currentView = value;} } protected override void CreateChildControls() { Controls.Clear(); ITemplate template = View1; if (_currentView == 1) template = View2; Panel p = new Panel(); Controls.Add(p); if (template != null) template.InstantiateIn(p); } }

The container control creates two ITemplate objects, which serve as the controls to display. The ITemplate objects are the control containers for this server control, allowing you to drop other server controls or text into this control. The control also uses the Designer attribute to indicate to Visual Studio that it should use the MultiRegionControlDesigner class when displaying this control on the designer surface. Now you create the control designer that defines the regions for the control. Listing 25-43 shows the designer class.

www.it-ebooks.info

1034  ❘  Chapter 25   User and Server Controls

Listing 25-43:  A custom designer class used to define designer regions Public Class MultiRegionControlDesigner Inherits System.Web.UI.Design.WebControls.CompositeControlDesigner

VB

Protected _currentView As Int32 = 0 Private myControl As MultiRegionControl Public Overrides Sub Initialize(ByVal component As IComponent) MyBase.Initialize(component) myControl = CType(component, MultiRegionControl) End Sub Public Overrides ReadOnly Property AllowResize() As Boolean Get Return True End Get End Property Protected Overrides Sub OnClick(ByVal e As DesignerRegionMouseEventArgs) If (e.Region Is Nothing) Then Return End If If ((e.Region.Name = "Header0") And (Not _currentView = 0)) Then _currentView = 0 UpdateDesignTimeHtml() End If If ((e.Region.Name = "Header1") And (Not _currentView = 1)) Then _currentView = 1 UpdateDesignTimeHtml() End If End Sub Public Overrides Function GetDesignTimeHtml( _ ByVal regions As DesignerRegionCollection) As String BuildRegions(regions) Return BuildDesignTimeHtml() End Function Protected Overridable Sub BuildRegions( _ ByVal regions As DesignerRegionCollection) regions.Add(New DesignerRegion(Me, "Header0")) regions.Add(New DesignerRegion(Me, "Header1")) ' If the current view is for all, we need another editable region Dim edr0 As New EditableDesignerRegion(Me, "Content" & _currentView, False) edr0.Description = "Add stuff in here if you dare:" regions.Add(edr0) ' Set the highlight, depending upon the selected region If ((_currentView = 0) Or (_currentView = 1)) Then regions(_currentView).Highlight = True End If End Sub Protected Overridable Function BuildDesignTimeHtml() As String Dim sb As New StringBuilder()

www.it-ebooks.info

Server Controls  ❘ 

1035

sb.Append(BuildBeginDesignTimeHtml()) sb.Append(BuildContentDesignTimeHtml()) sb.Append(BuildEndDesignTimeHtml()) Return sb.ToString() End Function Protected Overridable Function BuildBeginDesignTimeHtml() As String ' Create the table layout Dim sb As New StringBuilder() sb.Append("") ' Generate the title or caption bar sb.Append("" & _ "") sb.Append("") Return sb.ToString() End Function Protected Overridable Function BuildEndDesignTimeHtml() As String Return ("
") sb.Append("Page-View 1") sb.Append("Page-View 2
") End Function Protected Overridable Function BuildContentDesignTimeHtml() As String Dim sb As New StringBuilder() sb.Append("") Return sb.ToString() End Function Public Overrides Function GetEditableDesignerRegionContent( _ ByVal region As EditableDesignerRegion) As String Dim host As IDesignerHost = _ CType(Component.Site.GetService(GetType(IDesignerHost)), IDesignerHost) If (Not host Is Nothing) Then Dim template As ITemplate = myControl.View1 If (region.Name = "Content1") Then template = myControl.View2 End If If (Not template Is Nothing) Then Return ControlPersister.PersistTemplate(template, host)

continues

www.it-ebooks.info

1036  ❘  Chapter 25   User and Server Controls

Listing 25-43  (continued) End If End If Return String.Empty End Function Public Overrides Sub SetEditableDesignerRegionContent( _ ByVal region As EditableDesignerRegion, ByVal content As String) Dim regionIndex As Int32 = Int32.Parse(region.Name.Substring(7)) If (content Is Nothing) Then If (regionIndex = 0) Then myControl.View1 = Nothing ElseIf (regionIndex = 1) Then myControl.View2 = Nothing Return End If Dim host As IDesignerHost = _ CType(Component.Site.GetService(GetType(IDesignerHost)), IDesignerHost) If (Not host Is Nothing) Then Dim template = ControlParser.ParseTemplate(host, content) If (Not template Is Nothing) Then If (regionIndex = 0) Then myControl.View1 = template ElseIf (regionIndex = 1) Then myControl.View2 = template End If End If End If End If End Sub End Class

C#

public class MultiRegionControlDesigner : System.Web.UI.Design.WebControls.CompositeControlDesigner { protected int _currentView = 0; private MultiRegionControl myControl; public override void Initialize(IComponent component) { base.Initialize(component); myControl = (MultiRegionControl)component; } public override bool AllowResize { get { return true;}} protected override void OnClick(DesignerRegionMouseEventArgs e) { if (e.Region == null) return; if (e.Region.Name == "Header0" && _currentView != 0) { _currentView = 0;

www.it-ebooks.info

Server Controls  ❘ 

1037

UpdateDesignTimeHtml(); } if (e.Region.Name == "Header1" && _currentView != 1) { _currentView = 1; UpdateDesignTimeHtml(); } } public override String GetDesignTimeHtml(DesignerRegionCollection regions) { BuildRegions(regions); return BuildDesignTimeHtml(); } protected virtual void BuildRegions(DesignerRegionCollection regions) { regions.Add(new DesignerRegion(this, "Header0")); regions.Add(new DesignerRegion(this, "Header1")); // If the current view is for all, we need another editable region EditableDesignerRegion edr0 = new EditableDesignerRegion(this, "Content" + _currentView, false); edr0.Description = "Add stuff in here if you dare:"; regions.Add(edr0); // Set the highlight, depending upon the selected region if (_currentView ==0 || _currentView==1) regions[_currentView].Highlight = true; } protected virtual string BuildDesignTimeHtml() { StringBuilder sb = new StringBuilder(); sb.Append(BuildBeginDesignTimeHtml()); sb.Append(BuildContentDesignTimeHtml()); sb.Append(BuildEndDesignTimeHtml()); return sb.ToString(); } protected virtual String BuildBeginDesignTimeHtml() { // Create the table layout StringBuilder sb = new StringBuilder(); sb.Append(""); // Generate the title or caption bar sb.Append("" + ""); sb.Append(""); return sb.ToString();

continues

www.it-ebooks.info

1038  ❘  Chapter 25   User and Server Controls

Listing 25-43  (continued) } protected virtual String BuildEndDesignTimeHtml() { return ("
"); sb.Append("Page-View 1"); sb.Append("Page-View 2
"); } protected virtual String BuildContentDesignTimeHtml() { StringBuilder sb = new StringBuilder(); sb.Append(""); return sb.ToString(); } public override string GetEditableDesignerRegionContent (EditableDesignerRegion region) { IDesignerHost host = (IDesignerHost)Component.Site.GetService(typeof(IDesignerHost)); if (host != null) { ITemplate template = myControl.View1; if (region.Name == "Content1") template = myControl.View2; if (template != null) return ControlPersister.PersistTemplate(template, host); } return String.Empty; } public override void SetEditableDesignerRegionContent (EditableDesignerRegion region, string content) { int regionIndex = Int32.Parse(region.Name.Substring(7)); if (content == null) { if (regionIndex == 0) myControl.View1 = null; else if (regionIndex == 1) myControl.View2 = null; return; } IDesignerHost host = (IDesignerHost)Component.Site.GetService(typeof(IDesignerHost)); if (host != null) { ITemplate template = ControlParser.ParseTemplate(host, content); if (template != null) {

www.it-ebooks.info

Server Controls  ❘ 

1039

if (regionIndex == 0) myControl.View1 = template; else if (regionIndex == 1) myControl.View2 = template; } } } }

The designer overrides the GetDesignTimeHtml method, calling the BuildRegions and BuildDesignTimeHtml methods to alter the HTML that the control renders to the Visual Studio design surface. The BuildRegions method creates three design regions in the control, two header regions and an editable content region. The regions are added to the DesignerRegionCollection. The BuildDesignTimeHtml method calls three methods to generate the actual HTML that will be generated by the control at design time. The designer class also contains two overridden methods for getting and setting the editable designer region content: GetEditableDesignerRegionContent and SetEditableDesignerRegionContent. These methods get or set the appropriate content HTML, based on the designer region template that is currently active. Finally, the class contains an OnClick method that it uses to respond to click events fired by the control at design time. This control uses the OnClick event to switch the current region being displayed by the control at design time. When you add the control to a Web form, you see that you can toggle between the two editable regions, and each region maintains its own content. Figure 25-17 shows what the control looks like on the Visual Studio design surface.

Figure 25-17

As you can see in Figure 25-17, the control contains three separate design regions. When you click design regions 1 or 2, the OnClick method in the designer fires and redraws the control on the design surface, changing the template area located in design region 3.

www.it-ebooks.info

1040  ❘  Chapter 25   User and Server Controls

Designer Actions Another great feature of ASP.NET design-time support is control smart tags. Smart tags give developers using a control quick access to common control properties. To add menu items to a server control’s smart tag, you create a new class that inherits from the DesignerActionList class. The DesignerActionList contains the list of designer action items that are displayed by a server control. Classes that derive from the DesignerActionList class can override the GetSortedActionItems method, creating their own DesignerActionItemsCollection object to which you can add designer action items. You can add several different types of DesignerActionItems types to the collection: ➤➤

DesignerActionTextItem

➤➤

DesignerActionHeaderItem

➤➤

DesignerActionMethodItem

➤➤

DesignerActionPropertyItem

Listing 25-44 shows a control designer class that contains a private class deriving from DesignerActionList. Listing 25-44:  Adding designer actions to a control designer Public Class ServerControl44Designer Inherits ControlDesigner

VB

Private _actionLists As DesignerActionListCollection Public Overrides ReadOnly Property ActionLists() _ As DesignerActionListCollection Get If IsNothing(_actionLists) Then _actionLists = New DesignerActionListCollection() _actionLists.AddRange(MyBase.ActionLists) _actionLists.Add(New ServerControl44ControlList(Me)) End If Return _actionLists End Get End Property Private NotInheritable Class ServerControl44ControlList Inherits DesignerActionList Public Sub New(ByVal c As ServerControl44Designer) MyBase.New(c.Component) End Sub Public Overrides Function GetSortedActionItems() _ As DesignerActionItemCollection Dim c As New DesignerActionItemCollection() c.Add(New DesignerActionTextItem("Text Action Item", "Custom Category")) Return c End Function End Class End Class

C#

public class ServerControl44Designer : ControlDesigner { private DesignerActionListCollection _actionLists = null; public override DesignerActionListCollection ActionLists

www.it-ebooks.info

Server Controls  ❘ 

{ get { if (_actionLists == null) { _actionLists = new DesignerActionListCollection(); _actionLists.AddRange(base.ActionLists); _actionLists.Add(new ServerControl44ControlList(this)); } return _actionLists; } } private sealed class ServerControl44ControlList : DesignerActionList { public ServerControl44ControlList(ControlDesigner c) : base(c.Component) { } public override DesignerActionItemCollection GetSortedActionItems() { DesignerActionItemCollection c = new DesignerActionItemCollection(); c.Add(new DesignerActionTextItem("Text Action Item", "Custom Category")); return c; } } }

The control designer class overrides the ActionsLists property. The property creates an instance of the TextControlList class, which derives from DesignerActionList and overrides the GetSortedActionItems method. The method creates a new DesignerActionListCollection, and a DesignerActionTextItem is added to the collection (see Figure 25-18). The DesignerActionTextItem class allows you to add text menu items to the smart tag.

Figure 25-18

www.it-ebooks.info

1041

1042  ❘  Chapter 25   User and Server Controls

As shown in Figure 25-18, when you add the control to a Web page, the control now has a smart tag with the DesignerActionTextItem class as content.

UI Type Editors A UI type editor is a way to provide users of your controls with a custom interface for editing properties directly from the Property Browser. One type of UI type editor you might already be familiar with is the Color Picker you see when you want to change the ForeColor attribute that exists on most ASP.NET controls. ASP.NET provides a wide variety of in-box UI type editors that make editing more complex property types easy. The easiest way to find what UI type editors are available in the .NET Framework is to search for types derived from the UITypeEditor class in the MSDN Library help. After you find the type editor you want to use on your control property, you simply apply the UI type editor to the property using the Editor attribute. Listing 25-45 shows how to do this. Listing 25-45:  Adding a UI type editor to a control property

VB

C#

Public Property Url() As String [Bindable(true)] [Category("Appearance")] [DefaultValue("")] [Editor(typeof(System.Web.UI.Design.UrlEditor), typeof(System.Drawing.Design.UITypeEditor))] public string Url { get; set; }

In this sample, you have created a Url property for a control. Because you know this property will be a URL, you want to give the control user a positive design-time experience. You can use the UrlEditor type editor to make it easier for users to select a URL. Figure 25-19 shows the URL Editor that appears when the user edits the control property.

Figure 25-19

www.it-ebooks.info

Summary  ❘ 

1043

Summary In this chapter, you learned a number of ways you can create reusable, encapsulated chunks of code. You first looked at user controls, the simplest form of control creation. You learned how to create user controls and how you can make them interact with their host Web pages. Creating user controls is quite easy, but they lack the portability of other control-creation options. Then, you saw how you can create your own custom server controls. You looked at many of the tools you can create by writing custom server controls. These range from tools for emitting HTML and creating CSS styles and JavaScript to those applying themes. The chapter also discussed the type of server controls you can create, ranging from server controls that simply inherit from the WebControl class to templated controls that give users of the control the power to define the display of the server control. Finally, you looked at ways you can give the users of your server control a great design-time experience by providing them with type convertors, design surface interactions, and custom property editors in your server control.

www.it-ebooks.info

www.it-ebooks.info

26

Modules and Handlers whaT’s in This chaPTer? ➤

Interacting with the ASP�NET request processing pipeline



Working with HttpModules and HttpHandlers

Sometimes, just creating dynamic Web pages with the latest languages and databases does not give you, the developer, enough control over an application. At times, you need to be able to dig deeper and create applications that can interact with the Web server itself. You want to be able to interact with the low-level processes, such as how the Web server processes incoming and outgoing HTTP requests. Before ASP.NET, to get this level of control using IIS, you were forced to create ISAPI extensions or fi lters. This task was quite daunting and painful for many developers because creating ISAPI extensions and fi lters required knowledge of C/C++ and knowledge of how to create native Win32 DLLs. Thankfully, in the .NET world, creating these types of low-level applications is really no more difficult than most other applications you might build. This chapter looks at two methods of manipulating how ASP.NET processes HTTP requests, the HttpModule and the HttpHandler. Each method provides a unique level of access to the underlying processing of ASP.NET and can be a powerful tool for creating Web applications.

Processing hTTP requesTs Before starting to write handlers or modules, knowing how IIS and ASP.NET normally process incoming HTTP requests and what options you have for plugging custom logic into those requests is helpful. IIS is the basic endpoint for incoming HTTP requests. At a very high level, its job is to listen for and validate incoming HTTP requests. Then it routes them to the appropriate module for processing and returns any results to the original requestor. ASP.NET is one of the modules that IIS may pass requests to for processing. However, exactly how that processing happens and how you can integrate your own logic into the pipeline differs based on the version of IIS you are using.

www.it-ebooks.info

1046  ❘  Chapter 26   Modules and Handlers

IIS 6 and ASP.NET If you are using IIS 6, the HTTP request processing pipeline is fairly black box to a managed code developer. IIS basically treats ASP.NET as one of the modules that it can pass requests to for processing rather than as an integrated part of the IIS request processing pipeline. Figure 26-1 shows the basic request processing pipeline of IIS 6 and ASP.NET. Request

Request

IIS 6 Request Processing Pipeline

ASP.NET Request Processing Pipeline

Preprocessing

Preprocessing

Authentication

Authentication

Request

Request

Handler Mapper

Handler Mapper

Postprocessing

Postprocessing

Response

Response

Figure 26-1

As you can see, the IIS and ASP.NET request pipelines are very similar and several tasks, such as authentication, are even duplicated between the two pipelines. Furthermore, although you can write handlers and modules using managed code, they are still processed in the isolated context of the ASP.NET process. If you wanted to integrate deeper into the IIS pipeline you are forced to create modules using native code.

IIS 7 and ASP.NET Starting with IIS 7, the request processing pipeline in IIS was completely re-architected using an open and highly extensible module-based system. Instead of IIS seeing ASP.NET as a separate entity, ASP.NET was deeply integrated into the IIS request processing pipeline. As shown in Figure 26-2, the request processing pipeline was streamlined to eliminate duplicate processes and to allow you to integrate managed modules in the pipeline.

www.it-ebooks.info

Processing HTTP Requests  ❘ 

1047

Request

Integrated Request Processing Pipeline Authentication Module Preprocessing Modules Authorization Module

Request

PageHandlerFactory Handler Mapper Static File

Compression Module Postprocessing Modules Logging Module Response

Figure 26-2

Because ASP.NET modules are first-class citizens, you can place them at any point in the pipeline, or even completely replace existing modules with your own custom functionality. Features that previously required you to write custom ISAPI modules in unmanaged code can simply be replaced by managed code modules containing your logic. If you are interested in learning more about the integration of ASP.NET and IIS 7, check out the Wrox title Professional IIS 7 and ASP.NET Integrated Programming by Shahram Khosravi (Wiley Publishing, Inc., 2007).

ASP.NET Request Processing Regardless of the IIS version, the basic HTTP request pipeline model has two core mechanisms for handling requests: HttpModules and HttpHandlers. ASP.NET uses those two mechanisms to process incoming ASP. NET requests, generate a response, and return that response to the client. In fact, you are probably already familiar with HttpModules and HttpHandlers — although you might not know it. If you have ever used the Inbox caching or the authentication features of ASP.NET, you have used several different HttpModules. Additionally, if you have ever served up an ASP.NET application, even something as simple as a Hello World Web page and viewed it in a browser, you have used an HttpHandler. ASP.NET uses handlers to process and render ASPX pages and other file extensions. Modules and handlers allow you to plug into the requestprocessing pipeline at different points and interact with the actual requests being processed by IIS. As you can see in both Figures 26-1 and 26-2, ASP.NET passes each incoming request through a layer of preprocessing HttpModules in the pipeline. ASP.NET allows multiple modules to exist in the pipeline for each request. After the incoming request has passed through each module, it is passed to the HttpHandler, which serves the request. Notice that although a single request may pass through many different modules, it can be processed by one handler only. The handler is generally responsible for creating a response to the incoming HTTP request. After the handler has completed execution and generated a response, the response is passed back through a series of post-processing modules, before it is returned to the client.

www.it-ebooks.info

1048  ❘  Chapter 26   Modules and Handlers

You should now have a basic understanding of the IIS and ASP.NET request pipeline — and how you can use HttpModules and HttpHandlers to interact with the pipeline. The following sections take an in-depth look at each of these.

HttpModules HttpModules are simple classes that can plug themselves into the request-processing pipeline. They do this by hooking into a handful of events thrown by the application as it processes the HTTP request. To create an HttpModule, you simply create a class that derives from the System.Web.IHttpModule interface. This interface requires you to implement two methods: Init and Dispose. Listing 26-1 shows the class stub created after you implement the IHttpModule interface. Listing 26-1:  Implementing the IHttpModule interface Imports Microsoft.VisualBasic

VB

Namespace Chapter26.VB Public Class SimpleModule Implements IHttpModule Public Overridable Sub Init(ByVal context As HttpApplication) _ Implements IHttpModule.Init End Sub Public Overridable Sub Dispose() Implements IHttpModule.Dispose End Sub End Class End Namespace

C#

using using using using

System; System.Collections.Generic; System.Text; System.Web;

namespace Chapter26.CS { class SimpleModule : IHttpModule { #region IHttpModule Members public void Dispose() { throw new Exception( "The method or operation is not implemented."); } public void Init(HttpApplication context) { throw new Exception( "The method or operation is not implemented."); } #endregion } }

www.it-ebooks.info

HttpModules  ❘ 

1049

The Init method is the primary method you use to implement HttpModule functionality. Notice that it has a single method parameter — an HttpApplication object named context. This parameter gives you access to the current HttpApplication context, and it is what you use to wire up the different events that fire during the request processing. Table 26-1 shows the events that you can register in the Init method. Table 26-1 Event Name

Description

AcquireRequestState

Raised when ASP.NET runtime is ready to acquire the session state of the current HTTP request.

AuthenticateRequest

Raised when ASP.NET runtime is ready to authenticate the identity of the user.

AuthorizeRequest

Raised when ASP.NET runtime is ready to authorize the user for the resources user is trying to access.

BeginRequest

Raised when ASP.NET runtime receives a new HTTP request.

Disposed

Raised when ASP.NET completes the processing of an HTTP request.

EndRequest

Raised just before sending the response content to the client.

Error

Raised when an unhandled exception occurs during the processing of the HTTP request.

PostRequestHandlerExecute

Raised just after the HTTP handler finishes execution.

PreRequestHandlerExecute

Raised just before ASP.NET begins executing a handler for the HTTP request. After this event, ASP.NET forwards the request to the appropriate HTTP handler.

PreSendRequestContent

Raised just before ASP.NET sends the response contents to the client. This event allows you to change the contents before it gets delivered to the client. You can use this event to add the contents, which are common in all pages, to the page output — for example, a common menu, header, or footer.

PreSendRequestHeaders

Raised just before ASP.NET sends the HTTP response headers to the client. This event allows you to change the headers before they get delivered to the client. You can use this event to add cookies and custom data into headers.

To see how you can create and use an HttpModule, you can use a simple example involving modifying the HTTP output stream before it is sent to the client. This can be a simple and useful tool if you want to add text to each page served from your Web site, such as a date/time stamp or the server that processed the request, but do not want to modify each individual page in your application. To get started creating this HttpModule, create a Web project in Visual Studio and add a class file to the App_Code directory. Listing 26-2 shows the code for the HttpModule. Listing 26-2:  Altering the output of an ASP.NET web page Imports Microsoft.VisualBasic

VB

Namespace Chapter26.VB Public Class AppendMessage Implements IHttpModule Dim WithEvents _application As HttpApplication = Nothing Public Overridable Sub Init(ByVal context As HttpApplication) _ Implements IHttpModule.Init _application = context End Sub

continues

www.it-ebooks.info

1050  ❘  Chapter 26   Modules and Handlers

Listing 26-2  (continued) Public Overridable Sub Dispose() Implements IHttpModule.Dispose End Sub Public Sub context_EndRequest( ByVal sender As Object, ByVal e As EventArgs) _ Handles _application.EndRequest Dim message As String = String.Format("processed on {0}", System.DateTime.Now.ToString()) _application.Context.Response.Output.Write(message) End Sub End Class End Namespace

C#

using using using using

System; System.Collections.Generic; System.Text; System.Web;

namespace Chapter26.CS { public class AppendMessage : IHttpModule { private HttpApplication _application = null; #region IHttpModule Members public void Dispose() { } public void Init(System.Web.HttpApplication context) { _application = context; context.EndRequest += new EventHandler(context_EndRequest); } void context_EndRequest(object sender, EventArgs e) { string message = string.Format("processed on {0}", System.DateTime.Now.ToString()); _application.Context.Response.Write(message); } #endregion } }

www.it-ebooks.info

HttpModules  ❘ 

1051

You can see that the class stub from Listing 26-1 has been expanded by registering the PreSendRequestContent event in the Init method. This event fires right before the content created by the HttpHandler is sent to the client, and gives you one final opportunity to modify it. To modify the content of the request, in the PreSendRequestContent handler method you simply write your modification to the HttpResponse object’s output stream, which appends the content to the end of the existing content. This sample gets the current date and time and writes it to the output stream. The HTTP request is then sent back to the client. To use this module, you must let ASP.NET know that you want to include the module in the requestprocessing pipeline. You do this by modifying the web.config file to contain a reference to the module. Listing 26-3 shows how you can add an section to your web.config file. Listing 26-3:  Adding the httpModules configuration to web.config

The generic format of the section is

If you are deploying your application to an IIS 7 server, you must also add the module configuration to the configuration section.

If you have created your HttpModule in the App_Code directory of an ASP.NET Web site, you might wonder how you know what the assemblyname value should be, considering ASP.NET now dynamically compiles this code at runtime. The solution is to use the text “App_Code” as the assembly name, which tells ASP.NET that your module is located in the dynamically created assembly. You can also create HttpModules as a separate class library, in which case you simply use the assembly name of the library. After you have added this section to your web.config file, simply view one of the Web pages from your project in the browser. When you view the page in the browser you should not notice any difference, but, if you view the source of the page notice the timestamp you added at the bottom of the HTML. Figure 26-3 shows what you should see when you view the page source.

CONFER PROGRAMMER TO PROGRAMMER ABOUT THIS TOPIC.

Visit p2p.wrox.com

www.it-ebooks.info

1052  ❘  Chapter 26   Modules and Handlers

Figure 26-3

HttpHandlers HttpHandlers differ from HttpModules, not only because of their positions in the request-processing pipeline (refer to Figures 26-1 and 26-2), but also because they must be mapped to a specific file extension. Handlers are the last stop for incoming HTTP requests and are ultimately the point in the requestprocessing pipeline that is responsible for serving up the requested content, be it an ASPX page, HTML, plain text, or an image. Using HttpHandlers to serve up content you might normally serve using a standard ASP.NET page (such as a dynamic file download request) can be a good idea in your application because it allows you to write a specialized handler that eliminates some of the overhead of a standard ASP.NET handler. This section demonstrates two different ways to create a simple HttpHandler that you can use to serve up images based on dynamic querystring data: ➤➤

First, you look at creating an HttpHandler using an .ashx file extension that allows you to get started quickly and requires no server configuration.

➤➤

Next, you learn how to create even more customized handlers by mapping your HttpHandler to a custom file extension using IIS.

Generic Handlers In early versions of Visual Studio, HttpHandlers were somewhat hard to understand and create because little documentation was included to help developers understand handlers. In addition, Visual Studio did not provide any friendly methods for creating them. Since Visual Studio 2005, however, this has changed, with Visual Studio providing a standard template for HttpHandlers to help you get started. To add an HttpHandler to your project, you simply select the Generic Handler file type from the Add New Item dialog. Figure 26-4 shows this dialog box with the file type selected.

www.it-ebooks.info

HttpHandlers  ❘ 

1053

Figure 26-4

You can see that when you add the Generic Handler file to your project, it adds a file with an .ashx extension. The .ashx file extension is the default HttpHandler file extension set up by ASP.NET. Remember that HttpHandlers must be mapped to a unique file extension, so by default ASP.NET uses the .ashx extension. This feature is convenient because, otherwise, you would be responsible for adding the file extension yourself. This task is obviously not always possible, nor is it practical. Using the Custom Handler file type helps you avoid any extra configuration. Notice the class stub that the file type automatically creates for you. Listing 26-4 shows the class. Listing 26-4:  The HttpHandler page template <%@ WebHandler Language="VB" Class="Handler4" %>

VB

Imports System Imports System.Web Public Class Handler4 Implements IHttpHandler Public Sub ProcessRequest(ByVal context As HttpContext) _ Implements IHttpHandler.ProcessRequest context.Response.ContentType = "text/plain" context.Response.Write("Hello World") End Sub Public ReadOnly Property IsReusable() As Boolean _ Implements IHttpHandler.IsReusable Get Return False End Get

continues

www.it-ebooks.info

1054  ❘  Chapter 26   Modules and Handlers

Listing 26-4  (continued) End Property End Class

C#

<%@ WebHandler Language="C#" Class="Handler4" %> using System; using System.Web; public class Handler4 : IHttpHandler { public void ProcessRequest (HttpContext context) { context.Response.ContentType = "text/plain"; context.Response.Write("Hello World"); } public bool IsReusable { get { return false; } } }

Notice that the stub implements the IHttpHandler interface, which requires you to implement the ProcessRequest method and IsReusable property. ➤➤

The ProcessRequest method is the method you use to actually process the incoming HTTP request. By default, the class stub changes the content type to plain and then writes the “Hello World” string to the output stream.

➤➤

The IsReusable property simply lets ASP.NET know whether incoming HTTP requests can reuse the sample instance of this HttpHandler.

The handler generated in the template is ready to run right away. Try executing the handler in your browser and see what happens. The interesting thing to note about this handler is that because it changes the content to text/plain, browsers handle the responses from this handler in potentially very different ways depending on a number of factors: ➤➤

Browser type and version

➤➤

Applications loaded on the system that may map to the MIME type

➤➤

Operating system and service pack level

Based on these factors, you might see the text returned in the browser, you might see Notepad open and display the text, or you might receive the Open/Save/Cancel prompt from IE. Make sure you understand the potential consequences of changing the ContentType header. You can continue the example by modifying it to return an actual file. In this case, you use the handler to return an image. To do this, you simply modify the code in the ProcessRequest method, as shown in Listing 26-5. Listing 26-5:  Outputting an image from an HttpHandler <%@ WebHandler Language="VB" Class="Handler5" %>

VB

Imports System Imports System.Web Public Class Handler5 Implements IHttpHandler Public Sub ProcessRequest(ByVal context As HttpContext) _

www.it-ebooks.info

HttpHandlers  ❘ 

1055

Implements IHttpHandler.ProcessRequest 'Logic to retrieve the image file context.Response.ContentType = "image/jpeg" context.Response.WriteFile("Garden.jpg") End Sub Public ReadOnly Property IsReusable() As Boolean _ Implements IHttpHandler.IsReusable Get Return False End Get End Property End Class

C#

<%@ WebHandler Language="C#" Class="Handler5" %> using System; using System.Web; public class Handler5 : IHttpHandler { public void ProcessRequest(HttpContext context) { //Logic to retrieve the image file context.Response.ContentType = "image/jpeg"; context.Response.WriteFile("Garden.jpg"); } public bool IsReusable { get { return false; } } }

As you can see, you simply change the ContentType to image/jpeg to indicate that you are returning a JPEG image; then you use the WriteFile() method to write an image file to the output stream. Load the handler into a browser, and you see that the handler displays the image. Figure 26-5 shows the resulting Web page.

Figure 26-5

www.it-ebooks.info

1056  ❘  Chapter 26   Modules and Handlers

Now, you create a simple Web page to display the image handler. Listing 26-6 shows code for the Web page. Listing 26-6:  A sample Web page using the HttpHandler for the image source HttpHandler Serving an Image
Dynamic Image


Although this sample is simple, you can enhance it by passing querystring parameters to your handler and using them to perform additional logic in the handler:

Using the querystring data you could, for example, dynamically retrieve an image from a SQL database and return it to the client or perform some type of authentication to ensure the requestor is allowed to access this image.

Mapping a File Extension in IIS Although using the .ashx file extension is convenient, you might want to create an HTTP handler for a custom file extension or even for a commonly used extension. You can use the code from the image handler to demonstrate this method. Create a new class in the App_Code directory of your Web project. You can simply copy the code from the existing image handler control into this class, as shown in Listing 26-7. Notice that you remove the WebHandler directive because this is only a class and not a generic handler control. Other than that, the code is the same. Listing 26-7:  The class-based image HttpHandler Imports Microsoft.VisualBasic

VB

Namespace Chapter26.VB Public Class MappedHandler : Implements IHttpHandler Public Sub ProcessRequest(ByVal context As HttpContext) _ Implements IHttpHandler.ProcessRequest context.Response.ContentType = "image/jpeg" context.Response.WriteFile("Garden.jpg") End Sub Public ReadOnly Property IsReusable() As Boolean _ Implements IHttpHandler.IsReusable Get Return False End Get End Property End Class End Namespace

www.it-ebooks.info

HttpHandlers  ❘ 

C#

1057

using System.Web; namespace Chapter26.CS { public class MappedHandler : IHttpHandler { public void ProcessRequest(HttpContext context) { context.Response.ContentType = "image/jpeg"; context.Response.WriteFile("Garden.jpg"); } public bool IsReusable { get { return false; } } } }

After adding your class, configure the application to show which file extension this handler serves. You do this by adding an section to the web.config file. Listing 26-8 shows the section to add for the image handler. Listing 26-8:  Adding the httpHandlers configuration information to web.config file

In the configuration section, you direct the application to use the MappedHandler class to process incoming requests for ImageHandler.img. You can also specify wildcards for the path. Specifying *.img for the path indicates that you want the application to use the MappedHandler class to process any request with the .img file extension. Specifying * indicates that you want all requests to the application to be processed using the handler. As with HttpModules, if you are running your Web application using IIS 7, then you also must add the configuration section to the configuration section of your application’s config file. When adding the handler configuration in this section, you must also include the name attribute.

Load the ImageHandler.img file into a browser and, again, you should see that it serves up the image. Figure 26-6 shows the results. Notice the path in the browser’s address bar leads directly to the ImageHandler.img file.

www.it-ebooks.info

1058  ❘  Chapter 26   Modules and Handlers

Figure 26-6

Summary This chapter presented a number of ways you can create modules that allow you to interact with the ASP. NET request processing pipeline. You worked with HttpModules, which give you the power to plug yourself directly into the ASP.NET page-processing pipeline. The events provided to an HttpModule give you great power and flexibility to customize your applications. You also looked at HttpHandlers. Handlers allow you to skip the ASP.NET page-processing pipeline completely and have 100 percent control over how the framework serves up requested data. You learned how to create your own image handler and then map the handler to any file or file extension you want. Using these features of ASP.NET can help you create features in your application that exercise great control over the standard page processing that ASP.NET uses.

www.it-ebooks.info

27

asP.neT MVC whaT’s in This chaPTer? ➤

Understanding MVC and ASP�NET



Working with routes and URLs



Understanding Controllers and Views

Model-View- Controller (MVC) has been an important architectural pattern in computer science for many years. Originally named Thing-Model-View-Editor in 1979, it was later simplified to Model-View- Controller. It is a powerful and elegant means of separating concerns within an application and applies itself extremely well to Web applications. Its explicit separation of concerns does add a small amount of extra complexity to an application’s design, but the extraordinary benefits outweigh the extra effort. It’s been used in dozens of frameworks since its introduction. You can fi nd MVC in Java and C++, on Mac and on Windows, and inside literally dozens of frameworks. Understanding the core concepts of MVC is critical to using it effectively. This chapter discusses the history of the MVC pattern, as well as how it is used in Web programming today. ASP.NET MVC 1.0 shipped as a downloadable add- on to Visual Studio 2008. Now in Visual Studio 2010, ASP.NET MVC 2 ships built-in. This chapter also covers some of the limitations of ASP.NET Web forms and how ASP.NET MVC attempts to release the developer from those limitations. The content of this chapter is adapted from Professional ASP.NET MVC 1.0 by Rob Conery, Scott Hanselman, Phil Haack, and Scott Guthrie (Wiley, 2009). For more in - depth coverage of this topic, we recommend you check out that book.

defining model-view- conTroller Model-View- Controller (MVC) is an architectural pattern used to separate an application into three main aspects: ➤

The Model: A set of classes that describes the data you’re working with as well as the business rules for how the data can be changed and manipulated



The View: The application’s user interface (UI)

www.it-ebooks.info

1060  ❘  Chapter 27   ASP.NET MVC

➤➤

The Controller: A set of classes that handles communication from the user, overall application flow, and application-specific logic

This pattern is used frequently in Web programming. With ASP.NET MVC, it’s translated roughly to the following: ➤➤

The Models are the classes that represent the domain in which you are interested. These domain objects often encapsulate data stored in a database as well as code used to manipulate the data and enforce domain-specific business logic. With ASP.NET MVC, this is most likely a data access layer of some kind using a tool like LINQ to SQL, Entity Framework, or NHibernate, combined with custom code containing domain-specific logic.

➤➤

The View is a dynamically generated page. In ASP.NET MVC, you implement it via the System.Web .Mvc.ViewPage class, which inherits from System.Web.UI.Page.

➤➤

The Controller is a special class that manages the relationship between the View and Model. It talks to the Model, and it decides which View to render (if any). In ASP.NET MVC, this class is conventionally denoted by the suffix “Controller.”

MVC on the Web Today For many, the Web didn’t really become prominent until the first graphical browsers began to flood the market, starting with Mosaic in 1993. Shortly after, dynamic Web pages began showing up using languages such as Perl and enabled by technologies like the Common Gateway Interface (CGI). The technology available in the early stages of the Web was focused more around the concept of scripting HTML to do light content-driven work, as opposed to deep application logic, which just wasn’t needed back then. As the Web grew and HTML standards began to allow for richer interactions, the notion of the Web as an application platform began to take off. In the Microsoft realm, the focus was on quick and simple (in line with the simplicity of VB), and Active Server Pages (ASP) was born in 1996. ASP used VBScript, a very simple, lightweight language that gave developers a lot of “un-prescribed freedom” in terms of the applications they could create. A request for an ASP page would be handled by a file with the .asp extension, which consisted of a server-side script intermixed with HTML markup. Written in a procedural language, many ASP pages often devolved into “spaghetti code” in which the markup was intertwined with code in difficult-to-manage ways. Although writing clean ASP code was possible, it took a lot of work, and the language and tools were not sufficiently helpful. Even so, ASP did provide full control over the markup produced, it just took a lot of work. In January of 2002, Microsoft released version 1.0 of the .NET platform, which included the original version of ASP.NET, and thus Web forms was born. Its birth provided access to advanced tools and object-oriented languages for building a Web site. ASP.NET has grown tremendously over the last 8 years (almost a decade!) and has made developing Web pages very productive and simple by abstracting the repetitive tasks of Web development into simple drag-and-drop controls. This abstraction can be a tremendous help, but some developers have found that they want more control over the generated HTML and browser scripting, and they also want to be able to easily test their Web page logic. As languages matured and Web server software grew in capability, MVC soon found its way into Web application architectures. But MVC didn’t hit its mainstream stride until July of 2004, when a 24-year old developer at 37Signals in Chicago, Illinois, named David Heinemeier Hansson, introduced the world to his fresh way of thinking about MVC. David, or DHH as he’s known in the community, created Ruby on Rails, a Web development framework that used the Ruby language and the MVC pattern to create something special. Now let’s further delve into the new kid on the block, ASP.NET MVC, and answer the question, “Why not Web forms?”

www.it-ebooks.info

Model-View- Controller and asP.neT

❘ 1061

In February of 2007, Scott Guthrie of Microsoft sketched out the core of ASP.NET MVC while flying on a plane to a conference on the east coast of the United States. It was a simple application, containing a few hundred lines of code, but the promise and potential it offered for parts of the Microsoft Web developer audience was huge.

ProducT Team aside ScottGu, or “The Gu,” is legendary for prototyping cool stuff, and if he sees you in the hallway and he has his laptop, you won’t be able to escape as he says, “Dude! Check this out!” His enthusiasm is infectious. As the legend goes, at the Austin ALT.NET conference in October of 2007 in Redmond, Washington, ScottGu showed a group of developers “this cool thing he wrote on a plane” and asked whether they saw the need and what they thought of it. It was a hit. In fact, a number of people were involved with the original prototype, codenamed “Scalene.” Eilon Lipton e-mailed the fi rst prototype to the team in September of 2007, and he and ScottGu bounced prototypes, code, and ideas back and forth via e-mail, and still do!

model-view- conTroller and asP.neT ASP.NET MVC relies on many of the same core strategies that the other MVC platforms use, plus it offers the benefits of compiled and managed code and exploits new language features in .NET 3.5 and above within VB9 and C#3 such as lambdas and anonymous types. Each of the MVC frameworks used on the web usually share in some fundamental tenets: ➤

Convention over Configuration



Don’t repeat yourself (also known as the DRY principle)



Plugability whenever possible



Try to be helpful, but if necessary, get out of the developer’s way

serving methods, not files Web servers initially served up HTML stored in static fi les on disk. As dynamic Web pages gained prominence, Web servers served HTML generated on the fly from dynamic scripts that were also located on disk. With MVC, serving up HTML is a little different. The URL tells the routing mechanism which Controller to instantiate and which action method to call and supplies the required arguments to that method. The Controller’s method then decides which View to use, and that View then does the rendering. Rather than having a direct relationship between the URL and a fi le living on the Web server’s hard drive, a relationship exists between the URL and a method on a controller object. ASP.NET MVC implements the “front controller” variant of the MVC pattern, and the Controller sits in front of everything except the routing subsystem. A good way to conceive of the way that MVC works in a Web scenario is that MVC serves up the results of method calls, not dynamically generated (also known as scripted) pages. In fact, we heard a speaker once call this “RPC for the Web,” which is particularly apt, although quite a bit narrower in scope.

is This web forms 4.0? One of the major concerns that we’ve heard when talking to people about ASP.NET MVC is that its release means the death of Web forms. This just isn’t true. ASP.NET MVC is not ASP.NET Web forms 4.0. It’s an alternative to Web forms, and it’s a fully supported part of the framework. While Web forms continues

www.it-ebooks.info

1062  ❘  Chapter 27   ASP.NET MVC

to march on with new innovations and new developments, ASP.NET MVC will continue as a parallel alternative that’s totally supported by Microsoft. One interesting way to look at this is to refer to the namespaces these technologies live in. If you could point to a namespace and say, “That’s where ASP.NET lives,” it would be the System.Web namespace. ASP.NET MVC lives in the System.Web.Mvc namespace. It’s not System.Mvc, and it’s not System.Web2. Although ASP.NET MVC is a separately downloadable Web component today for users of Visual Studio 2008 (often referred to by the ASP.NET team as an out-of-band [OOB] release), it has been folded into .NET Framework 4 and it’s built into Visual Studio 2010 out of the box. This cements ASP.NET MVC’s place as a fundamental part of ASP.NET itself.

Why Not Web Forms? In ASP.NET Web forms, you create an instance of System.Web.UI.Page and put “server controls” on it (for example, a calendar and some buttons) so that the user can enter or view information. You then wire these controls to events on the System.Web.UI.Page to allow for interactivity. This page is then compiled, and when it’s called by the ASP.NET runtime, a server-side control tree is created, each control in the tree goes through an event lifecycle, it renders itself, and the result is served back as HTML. As a result, a new Web aesthetic started to emerge — Web forms layers eventing and state management on top of HTTP — a truly stateless protocol. Why was this abstraction necessary? Remember that Web forms was introduced to a Microsoft development community that was very accustomed to Visual Basic 6. Developers using VB6 would drag buttons onto the design surface, double-click the button, and a Button_Click event handler method was instantly created for them. This was an incredibly powerful way to create business applications and had everyone excited about Rapid Application Development (RAD) tools. When developers started using classic ASP, it was quite a step backward from the rich environment they were used to in Visual Basic. For better or worse, Web forms brought that Rapid Application Development experience to the Web. However, as the Web matured and more and more people came to terms with their own understanding of HTML as well as the introduction of CSS (Cascading Style Sheets) and XHTML, a new Web aesthetic started to emerge. Web forms is still incredibly productive for developers, enabling them to create a Webbased line of business applications very quickly. However, the HTML it generates looks, well, generated and can sometimes offend the sensibilities of those who handcraft their XHTML and CSS sites. Web forms concepts like ViewState and the Postback event model have their place, but many developers want a lowerlevel alternative that embraces not only HTML but also HTTP itself. Additionally, the architecture of Web forms also makes testing via the current unit testing tools such as NUnit, MbUnit, and xUnit.NET difficult. ASP.NET Web forms wasn’t designed with unit testing in mind, and although a number of hacks can be found on the Web, it’s fair to say that Web forms does not lend itself well to test-driven development. ASP.NET MVC offers absolute control over HTML, doesn’t deny the existence of HTTP, and was designed from the ground up with an eye towards testability.

ASP.NET MVC Is Totally Different! Yes, ASP.NET MVC is totally different. That’s the whole point. It’s built on top of a system of values and architectural principles that is very different from those in Web forms. ASP.NET MVC values extensibility, testability, and flexibility. It’s very lightweight and doesn’t make a lot of assumptions on how you will use it — aside from the fact that it assumes you appreciate the Model-View-Controller pattern. Different developers and different projects have different needs. If ASP.NET MVC meets your needs, use it. If it doesn’t, don’t use it. Either way, don’t be afraid.

Why “(ASP.NET > ASP.NET MVC) == True” Creating your first MVC application is fairly straightforward. You can use any version of Visual Studio 2010 to create the basic application, including Express, Standard, Professional, or Team Edition.

www.it-ebooks.info

Model-View-Controller and ASP.NET  ❘ 

1063

If you’re NOT using Visual Studio 2010, the first order of business is to install the MVC Framework on your 2008 development box. Start at www.asp.net/mvc by downloading the latest release. If you like living on the edge, you can often get ASP.NET MVC future releases at www.codeplex.com/aspnet. What you’re downloading is a set of Visual Studio project templates that will create your ASP.NET MVC Web application for you. You’ve used these before — every new ASP.NET Web site and ASP.NET Web application is based on a set of templates. The templates will be installed in Visual Studio, and the reference assemblies will be installed in C:\Program Files\Microsoft ASP.NET. After you’ve installed ASP.NET MVC, you’re ready to create an ASP.NET MVC application using Visual Studio 2008. Though, if you are using Visual Studio 10, then you are going to want to follow these steps:

1. Open Visual Studio 2010 by selecting File ➪ New Project. 2. From the New Project dialog box, shown in Figure 27-1, select ASP.NET MVC 2 Web Application.

Figure 27-1



3. Pick your project name and where it’s going

to live on disk, and click OK. The Create Unit Test Project dialog appears, as shown in Figure 27-2.



4. By default the Test Framework drop-down list includes Visual Studio Unit Test as an option. Select Yes to create a solution that includes both a basic ASP.NET MVC project but also an additional MSTest Unit Test project. If you’ve installed a third-party unit-testing framework like MbUnit or NUnit, additional options appear in this dialog.

Figure 27-2

www.it-ebooks.info

1064  ❘  Chapter 27   ASP.NET MVC



5. Click OK, and a solution with projects that look like that shown in

Figure 27-3 appears. Note that, although this is an ASP.NET application, along with a standard class library, it has some additional folders you haven’t seen before.

In fact, the application has quite a few more directories than you might be used to; this is by design. ASP.NET MVC, like other MVC frameworks, relies heavily on the idea that you can reduce effort and code by relying on some basic structural rules in your application. Ruby on Rails expresses this powerful idea very succinctly: Convention over Configuration.

Convention over Configuration The concept of “Convention over Configuration” was made popular by Ruby on Rails a few years back, and essentially means the following: We know, by now, how to build a Web application. Let’s roll that experience into the framework so we don’t have to configure absolutely everything again.

Figure 27-3

You can see this concept at work in ASP.NET MVC by taking a look at the three core directories that make the application work: ➤➤

Controllers

➤➤

Models

➤➤

Views

You don’t have to set these folder names in the web.config file — they are just expected to be there by convention. This saves you the work of having to edit an XML file such as your web.config file, for example, to explicitly tell the MVC engine “you can find my controllers in the Controllers directory.” It already knows. It’s convention. This feature isn’t meant to be magical. Well actually it is; it’s just not meant to be “black magic,” as Phil Haack, the Program Manager for ASP.NET MVC, calls it — the kind of magic where you may not get the outcome you expected (and moreover can actually harm your application). ASP.NET MVC’s conventions are straightforward. This is what is expected of your application’s structure: ➤➤

It has a single Controllers directory that holds your Controller classes.

➤➤

Each Controller’s class name ends with “Controller” — ProductController, HomeController, and so on — and exists in the Controllers directory.

➤➤

It has a single Views directory for all the Views of your application.

➤➤

Views that Controllers use are located in a subdirectory of the Views main directory, and are named according to the Controller name (minus “Controller”). For example, the views for the ProductController discussed earlier would be in /Views/Product.

➤➤

All reusable UI elements live in a similar structure above, but in a “shared” directory off the root.

If you take a deeper, expanded look at the initial structure of the sample application, shown in Figure 27-4, you can see these conventions at work. Two controllers, HomeController and AccountController, are in the Controllers directory, and a number of Views are in the Views directory. The following discussion focuses on the Views under /Views/Home named About and Index.

www.it-ebooks.info

Figure 27-4

Model-View-Controller and ASP.NET  ❘ 

1065

Although no convention is expected of you with respect to what you name your Views, you can lean on the ASP.NET MVC convention that you give your View the same name as your action. Using this convention also makes reviewing and understanding your application easier for other developers. You can see this convention in action in the way that the template creates the Index and About views. These are also the names of the Controller actions that are called, and the code to render these views is simply: return View();

That can be a little confusing. You can see a clear example by changing the application a little and then digging in:

1. Open HomeController.cs, copy and paste the About method, and create a duplication called Foo, as shown here:

public ActionResult Foo() { ViewData["Title"] = "Foo Page"; return View(); }



2. Having made this one small addition, start your application. You will be prompted to modify your

web.config file to enable debugging. Click OK to have Visual Studio automatically make the change for you. The ASP.NET Development Web Server automatically selects a high port number and your browser launches. Your browser ends up navigating to an address like http://localhost:1074, as shown in Figure 27-5.

Figure 27-5



3. See how there’s no .aspx extension? ASP.NET MVC puts you in full control. Now, change the relative

URL in your browser’s address bar from / to /Home/Foo. Things get interesting, as shown in Figure 27-6. Remember that you’re just returning the result of the call to View in your Foo method. As you’re in the Foo method of HomeController, the system is looking for a View called Foo in a number of locations. ASP.NET MVC is smart enough to give you an error message that you can actually do something useful with. It’s really quite refreshing! System.InvalidOperationException: The view ‘Foo’ could not be located at these paths: ~/Views/Home/Foo.aspx, ~/Views/Home/Foo.ascx, ~/Views/Shared/Foo.aspx, ~/Views/Shared/Foo.ascx

www.it-ebooks.info

1066  ❘  Chapter 27   ASP.NET MVC

The error message lists (see Figure 27-6) the locations where the system looked for Views, in the order searched. It gives you enough information to infer the naming convention for Views. First, it looks in a directory under /Views with the name of the current Controller, in this case Home, then it looks in /Views/Shared. The WebFormsViewEngine that ASP.NET MVC uses by default looks for .aspx pages, then .ascx files.

Figure 27-6



4. Go back into HomeController.cs and change the call to View in the Foo method to include the name of a View as a parameter.

public ActionResult Foo() { ViewData["Title"] = "Foo Page"; return View("Index"); }



5. Start your application again, and visit /Home/Foo again. The Index View is rendered, and the Title



6. Switch back over to Visual Studio and set a breakpoint on the line that returns the result of View.

string appears in the browser’s title.

Refresh your browser, confirming that you’re still at /Home/Foo, and get ready to dig in.

The Third Request Is the Charm Take a moment and think about what’s happening here. What’s the state of affairs within your application? Your instance of Visual Studio should look more or less like Figure 27-7.

www.it-ebooks.info

Model-View-Controller and ASP.NET  ❘ 

1067

Figure 27-7

Spend a moment looking at Visual Studio (or the figure, if you like) and try to determine what it is telling you. How did you get here? Where are you? You visited /Home/Foo in the browser, and now you’re magically sitting on a breakpoint inside of the Foo action method. The Call Stack tool window confirms this, but doesn’t tell you enough. How did you get here? Right-click the whitespace of the call stack, and select Show External Code. You might also drag the Call Stack tool window and “tear it off” Visual Studio to better analyze the crush of information that’s going to be revealed. If you have multiple monitors, remember that Visual Studio 2010 supports that, so you can fill one whole monitor with the Call Stack tool window if you like. The Call Stack tool window contains so much information, in fact, that the authors have taken it upon themselves to highlight some important bits of it, as shown in Figure 27-8. Remember that call stacks are read from bottom to top, where the bottom is where you started and the top is the line you are currently debugging. In this call stack, some parts are more significant than others.

Figure 27-8

www.it-ebooks.info

1068



chaPTer 27 Asp.net mvc

Starting at the bottom, you can see that the execution thread is chosen and the HttpWorkerRequest is being dispatched and handled by ASP.NET — specifically by System.Web.HttpRuntime. This is the “beginning” of ASP.NET. Note that this is System.Web, and you’re inside System.Web.dll — nothing MVC specific has happened yet. If you’re already familiar with Web forms, you might fi nd it useful to remember what ASP.NET proper is, where it ends, and where ASP.NET MVC starts. The fi rst significant thing happens (remember, you’re reading from bottom to top) in the bottom callout shown in Figure 27-8. What can you learn from this single stack frame? ASP.NET MVC is built on ASP.NET with HttpHandlers and HttpModules. That’s where MVC gets its hooks in. The fact that ASP.NET MVC is implemented as an HttpHandler is comforting because you know that the team “played by the rules” when writing it. No internal knowledge or secrets exist in the design of ASP.NET MVC. It’s written using the same public constructs and APIs that are available to all developers. We fi nd great comfort in the discovery that ASP.NET MVC has no secrets in its design. Less magic in ASP.NET MVC means we can understand it more easily. If ASP.NET MVC is an HttpHandler, and you’ve written lots of those, then it ’s less magical than you thought! It ’s also nice to see that ASP.NET itself was fl exible and extensible enough to allow something like ASP.NET MVC to be created. Another thing you can glean from these discoveries is that because ASP.NET MVC uses HttpHandlers (and HttpModules) to do its work, MVC is built on ASP.NET. This might seem like an obvious statement to some, but a very common question is “Is ASP.NET MVC a whole new ASP.NET?” You can see from Figure 27-8 that it’s not. It’s built on the same infrastructure, the same “core” ASP.NET, that you’ve used for years. Glance back at Figure 27-8 and look at that call stack. Remember that you’re currently sitting on a breakpoint in the Foo method inside HomeController. Who created HomeController? Someone had to “new it up.” Who called Foo for you? Look to the call stack, my friends. Inside the MvcHandler’s ProcessRequest method an instance of a Controller is created by the DefaultControllerFactory. ASP.NET MVC creates an instance of the HomeController and then calls the Execute method on the Controller. This method in turn relies on the Controller’s action invoker (by default a ControllerActionInvoker) to actually call the method. Remember that you opened a browser and requested /Home/Foo. The ASP.NET MVC application routed the request to an MvcHandler. That Handler created an instance of the HomeController and called the Foo method. ASP.NET MVC handled both object activation and method invocation for you. The /Home/Foo URL was intercepted by the UrlRoutingModule. That module is responsible for making sure the right URLs go to the right Controllers by parsing the URLs and creating some routing data. The MVC pipeline uses a ControllerFactory and a ControllerActionInvoker to create your controller and call its method, respectively. Controllers exist to “do stuff.” What that stuff is, is up to you. They talk to a Model, do calculations, whatever. However, they don’t render HTML, and they don’t talk to databases. That’s separation of concerns. Controllers are concerned with controlling. The Controller passes ViewData to a View, which is concerned with rendering HTML (or whatever you want). That HTML contains links to other URLs, and the cycle continues.

undersTanding rouTes and urls Software developers are well known for paying close attention to the little details, especially when it comes to the quality and structure of their source code. They often fight long battles over code indentation styles and where curly braces should go. So it comes as a bit of a surprise when you approach a majority of sites built using ASP.NET and encounter a URL that looks like this:

www.it-ebooks.info

Understanding Routes and URLs  ❘ 

1069

http://example.com/products/list.aspx?id=17313&catid=33723&page=3

For all the attention developers pay to code, why not pay the same amount of attention to the URL? It may not seem all that important, but the URL is a legitimate and widely used Web user interface. Usability expert Jakob Nielsen (www.useit.com) urges developers to pay attention to URLs and provides the following guidelines for high-quality URLs: ➤➤

A domain name that is easy to remember and easy to spell

➤➤

Short URLs

➤➤

Easy-to-type URLs

➤➤

URLs that reflect the site structure

➤➤

URLs that are “hackable” to allow users to move to higher levels of the information architecture by hacking off the end of the URL

➤➤

Persistent URLs, which don’t change

Traditionally, in many Web frameworks such as classic ASP, JSP, PHP, ASP.NET, and the like, the URL represents a physical file on disk. For example, when you see a request for http://example.com/products/list.aspx

you could bet your kid’s tuition that the Web site has a directory structure that contains a products folder and a List.aspx file within that folder. In this case, a direct relationship exists between the URL and what physically exists on disk. When such a request is received by the Web server, the Web framework executes code associated with this file to respond to the request. In many cases, this code contains or is associated with a template that intermixes server-side declarations with HTML markup to generate the resulting markup sent back to the browser via the response. Routing within the ASP.NET MVC Framework serves two main purposes: ➤➤

It matches incoming requests and maps them to a Controller action.

➤➤

It constructs outgoing URLs that correspond to Controller actions.

Now that you understand something of URLs and routing, it’s time to take a closer look at routing and how it’s different from URL rewriting.

Routing Compared to URL Rewriting To better understand routing, many developers compare it to URL rewriting. After all, both approaches are useful in creating a separation between the URL and the code that handles the URL, which can help create “pretty” URLs for search engine optimization (SEO) purposes. One key difference, though, is that URL rewriting represents a “page-centric” view of URLs. Most rewriting schemes with ASP.NET rewrite a URL for one page to be handled by another. For example, you might see /product/bolts.aspx

rewritten as /product/display.aspx?productid=111

Routing, on the other hand, takes a “resource-centric” view of URLs. In this case, the URL represents a resource (not necessarily a page) on the Web. With ASP.NET routing, this resource is a piece of code that executes when the incoming request matches the route. The route determines how the request is dispatched based on the characteristics of the URL — it doesn’t rewrite the URL. Another key difference is that routing also helps generate URLs using the same mapping rules that it uses to match incoming URLs. Another way to look at it is that ASP.NET routing is more like bidirectional URL rewriting. Where this comparison falls short is that ASP.NET routing never actually rewrites your URL. The request URL that the user makes in the browser is the same URL your application sees throughout the entire request lifecycle.

www.it-ebooks.info

1070  ❘  Chapter 27   ASP.NET MVC

Defining Routes Every ASP.NET MVC application needs at least one route to define how the application should handle requests, but usually ends up with at least a handful. Conceivably, a very complex application could have dozens of routes or more. In this section, you look at how to define routes. Route definitions start with the URL, which specifies a pattern that the route will match. Along with the route URL, routes can also specify default values and constraints for the various parts of the URL, providing tight control over how the route matches incoming request URLs. In this next part of the chapter, you start with an extremely simple route and build up from there.

Setting Route URLs After you create a new ASP.NET MVC Web application project, take a quick look at the code in Global .asax.cs. You’ll notice that the Application_Start method contains a call to a method named RegisterRoutes method. This method is where all routes for the application are registered. Clear out the routes in there for now and replace them with this very simple route: routes.MapRoute("simple", "{first}/{second}/{third}");

The simplest form of the MapRoute method takes in a name for the route and the URL pattern for the route. The name is discussed a bit later in this section. For now, focus on the URL pattern. Notice that the route URL consists of several URL segments (a segment is everything between slashes but not including the slashes), each of which contains a placeholder delimited using curly braces. These placeholders are referred to as URL parameters. This structure is a pattern-matching rule used to determine whether this route applies to an incoming request. In this example, this rule will match any URL with three segments because a URL parameter, by default, matches any nonempty value. When it matches a URL with three segments, the text in the first segment of that URL corresponds to the {first} URL parameter, the value in the second segment of that URL corresponds to the {second} URL parameter, and the value in the third segment corresponds to the {third} parameter. You can name these parameters anything you want, as in this case. When a request comes in, routing parses the request URL into a dictionary (specifically a RouteValueDictionary accessible via the RequestContext), using the URL parameter names as the keys and subsections of the URL in the corresponding position as the values. When using routes in the context of an MVC application, certain parameter names carry a special purpose. Table 27-1 displays how the route just defined converts certain URLs into a RouteValue Dictionary. Table 27-1 URL

URL Parameter Values

/products/display/123

{first} = products {second} = display {third} = 123

/foo/bar/baz

{first} = foo {second} = bar {third} = baz

/a.b/c-d/e-f

{first} = “a.b” {second} = “c-d” {third} = “e-f”

www.it-ebooks.info

Understanding Routes and URLs  ❘ 

1071

If you actually make a request to the URLs listed in the preceding table, you may notice that your ASP.NET MVC application appears to be broken. Although you can define a route with any parameter names you want, certain special parameter names are required by ASP.NET MVC for the route to function correctly — {controller} and {action}. The value of the {controller} parameter is used to instantiate a controller class to handle the request. By convention, MVC appends the suffix “Controller” to the {controller} value and attempts to locate a type of that name (case insensitively) that also inherits from the System.Web.Mvc.IController interface. Going back to the simple route example, change it from routes.MapRoute("simple", "{first}/{second}/{third}");

to routes.MapRoute("simple", "{controller}/{action}/{id}");

so that it contains the special URL parameter names. Now looking again at the first example in Table 27-1, you see that the request for /products/display/123 is a request for a {controller} named “Products”. ASP.NET MVC takes that value and appends the “Controller” suffix to get a type name, ProductsController. If a type of that name that implements the IController interface exists, it is instantiated and used to handle the request. The {action} parameter value is used to indicate which method of the controller to call to handle the current request. Note that this method invocation only applies to controller classes that inherit from the System.Web.Mvc.Controller base class. Continuing with the example of /products/display/123, the method of ProductsController that MVC will invoke is Display. Note that the third URL in Table 27-1, although it is a valid route URL, will probably not match any real Controller and action, because it would attempt to instantiate a Controller named a.bController and call the method named c-d, which is not a valid method name. Any route parameters other than {controller} and {action} are passed as parameters to the action method, if they exist. For example, assuming the following Controller: public class ProductsController : Controller { public ActionResult Display(int id) { //Do something return View(); } }

a request for /products/display/123 would cause MVC to instantiate this class and call the Display method, passing in 123 for the id. In the previous example with the route URL {controller}/{action}/{id}, each segment contains a URL parameter that takes up the entire segment. This doesn’t have to be the case. Route URLs do allow for literal values within the segments. For example, if you are integrating MVC into an existing site and want all your MVC requests to be prefaced with the word site, you could do it as follows: site/{controller}/{action}/{id}

This indicates that the first segment of a URL must start with site to match this request. Thus, /site/products/display/123 matches this route, but /products/display/123 does not match. Having URL segments that intermix literals with parameters is even possible. The only restriction is that two consecutive URL parameters are not allowed. Thus {language}-{country}/{controller}/{action} {controller}.{action}.{id}

are valid route URLs, but {controller}{action}/{id}

www.it-ebooks.info

1072



chaPTer 27 Asp.net mvc

is not a valid route. No way exists for the route to know when the controller part of the incoming request URL ends and when the action part should begin. Looking at some other samples (shown in Table 27-2) can help you see how the URL pattern corresponds to matching URLs. TaBle 27-2 rouTe url PaTTern

ex amPles of urls ThaT maTch

{controller}/{action}/{category}

/products/list/beverages /blog/posts/123

service/{action}-{format}

/service/display-xml

{reporttype}/{year}/{month}/{date}

/sales/2008/1/23

Under the Hood: How routes Tie Your Url to an action In the last section, you walked through how routes map to Controller actions within the MVC Framework. In this section, you take a look under the hood to get a better look at how this happens and get a better picture of where the dividing line is between routing and MVC. One common misconception is that routing is just a feature of ASP.NET MVC. During the early stages of ASP.NET MVC implementation, this was true, but after a while, it became apparent that this was a more generally useful feature. The ASP.NET Dynamic Data team in particular was also interested in using routing inside Dynamic Data itself . At that point, routing became a more general-purpose feature that has neither internal knowledge of nor dependency on MVC. One very outward bit of proof that routing is separate is not just that it’s a separate assembly but that it lives in the System.Web.Routing namespace, and not a theoretical System.Web.Mvc.Routing. You can glean a lot of information from reading into namespaces. The discussion here focuses on routing for IIS 7 integrated mode. Some slight differences exist when using routing with IIS 7 classic mode or IIS 6. When you are using the Visual Studio built- in Web server, the behavior is very similar to the IIS 7 Integrated mode.

The High- level request routing Pipeline All this talk about routing might be a lot of information for you to process. However, it’s important to understand as routing really is your most powerful tool to control your application’s URLs. Broken down into its component parts, the routing pipeline consists of the following five high-level steps:

1.

UrlRoutingModule attempts to match the current request with the routes registered in the RouteTable.

2. 3.

If a route matches, then the routing module grabs the IRouteHandler from that route.

4. 5.

The routing module calls GetHandler from the IRouteHandler, which returns an IHttpHandler. Recall that a typical ASP.NET Page (also known as System.Web.UI.Page) is nothing more than an IHttpHandler. ProcessRequest is called on the HttpHandler, thus handing off the request to be handled.

In the case of MVC, the IRouteHandler is by default an instance of MvcRouteHandler, which in turn returns an MvcHandler (implement IHttpHandler). The MvcHandler is responsible for instantiating the correct Controller and calling the action method on that Controller.

www.it-ebooks.info

Controllers  ❘ 

1073

Route Matching At its core, routing is simply matching requests and extracting route data from that request and passing it to an IRouteHandler. The algorithm for route matching is very simple from a high-level perspective. When a request comes in, the UrlRoutingModule iterates through each route in the RouteCollection accessed via RouteTable.Routes in order. It then asks each route, “Can you handle this request?” If the route answers “Yes I can!”, then the route lookup is done and that route gets to handle the request. The question of whether a route can handle a request is asked by calling the method GetRouteData. The method returns null if the current request is not a match for the route (in other words, no real conversation is going on between the module and routes).

RouteData Recall that when you call GetRouteData, it returns an instance of RouteData. What exactly is RouteData? RouteData contains information about the route that matched a particular request, including context information for the specific request that matched. Recall in the previous section that we showed a route with the following URL: {foo}/{bar}/{baz}. When a request for /products/display/123 comes in, the route attempts to match the request. If it does match, it then creates a dictionary that contains information parsed from the URL. Specifically, it adds a key to the dictionary for each url parameter in the route URL. So in the case of {foo}/{bar}/{baz}, you would expect the dictionary to contain at least three keys, “foo”, “bar”, “baz”. In the case of /products/display/123, the URL is used to supply values for these dictionary keys. In this case, foo = products, bar = list, and baz = 123.

Controllers You might want to remember a quick definition: Controllers within the MVC pattern are responsible for responding to user input, often making changes to the Model in response to user input. In this way, Controllers in the MVC pattern are concerned with the flow of the application, working with data coming in, and providing data going out to the relevant View.

Defining the Controller: The IController Interface Among the core focuses of ASP.NET MVC are extensibility and flexibility. When building software with these goals in mind, leveraging abstraction as much as possible by using interfaces is important. For a class to be a Controller in ASP.NET MVC, it must at minimum implement the IController interface, and by convention the name of the class must end with the suffix “Controller.” The naming convention is actually quite important — and you’ll find that many of these small rules are in play with ASP.NET MVC, which will make your life just a little bit easier by not making you define configuration settings and attributes. Ironically, the IController interface is quite simple given the power it is abstracting: public interface IController { void Execute(RequestContext requestContext); }

Creating an IController is a simple process really: When a request comes in, the routing system identifies a Controller, and it calls the Execute method. The ControllerBase class is an abstract base class that layers a bit more API surface on top of the IController interface. It provides the TempData and ViewData properties, and the Execute method of ControllerBase is responsible for creating the ControllerContext, which provides the MVC-specific context for the current request much the same way that an instance of HttpContext provides the context for ASP.NET in general (providing request and response, URL, and server information, among other elements).

www.it-ebooks.info

1074  ❘  Chapter 27   ASP.NET MVC

This base class is still very lightweight and allows developers to provide extremely customized implementations for their own Controllers, while benefiting from the action filter infrastructure in ASP. NET MVC. What it doesn’t provide is the ability to convert actions into method calls. That’s where the Controller class comes in.

The Controller Class and Actions In theory, you could build an entire site with classes that simply implement ControllerBase or IController, and it would work. Routing would look for an IController by name and then call Execute, and you would have yourself a very, very basic Web site. This approach, however, is akin to working with ASP.NET using raw HttpHandlers — it would work, but you’re left to reinvent the wheel and plumb the core framework logic yourself. Interestingly, ASP.NET MVC itself is layered on top of HTTP handlers, and overall there was no need to make internal plumbing changes to ASP.NET to implement MVC. Instead, the ASP.NET MVC team simply layered this new framework on top of existing ASP.NET extensibility points. The standard approach to writing a Controller is to have it inherit from the System.Web.Mvc.Controller abstract base class, which implements the ControllerBase base class. The Controller class is intended to serve as the base class for all Controllers, as it provides a lot of nice behaviors to Controllers that derive from it. Now walk through another simple Controller example, but this time add a public method. To get started with this example, open the previous example and create a new Controller by right-clicking the Controllers folder and selecting Add ➪ Controller, then name it Simple2Controller. Next, add the following code: using System; using System.Web; using System.Web.Mvc; public class Simple2Controller : Controller { public void Hello() { Response.Write("

Hello World Again!

"); } }

Press Ctrl+F5 (or Debug ➪ Run) and navigate to /Simple2/Hello in the browser. As before, this is not exactly breathtaking, but it is a bit more interesting. Notice that the URL in the address bar directly correlates to the action method of your Controller. If you recall from earlier, the default route for MVC breaks URLs into three main components: /{controller}/{action}/{id}. Take a look at how that applies to this example. The Simple2 portion of the URL corresponds to the Controller name. The MVC Framework appends the “Controller” suffix to the Controller name and locates your Controller class, Simple2Controller. /Simple2/Hello

The last portion of the URL corresponds to the action. The framework locates a public method with this name and attempts to call the method.

Working with Parameters You can add any number of public methods (which are called actions from here on out to keep with convention) to a Controller class, which will all be callable via this pattern. Actions may also contain parameters. Going back to the previous example, add a new action method that takes in a parameter: public class Simple2Controller : Controller { public void Goodbye(string name) { Response.Write("Goodbye " + HttpUtility.HtmlEncode(name)); } }

www.it-ebooks.info

Controllers  ❘ 

1075

This method is callable via the URL: /Simple2/Goodbye?name=World

Notice that you can pass in parameters to an action method by name via the query string. You can also pass in parameters via the URL segments, discoverable by position as defined in your routes. For example, the following URL is more aesthetically pleasing to many developers and Internet users: /Simple2/Goodbye/World

Working with parameters passed by URL segment requires you to define how routing will identify these parameters in the URL. Fortunately, the default route (created for you when you click File ➪ New) is already set up for you and contains a common URL pattern: {controller}/{action}/{id}.

Changing the action method signature a little bit (by renaming the parameter “name” to “id”) like so: public class Simple2Controller : Controller { public void Goodbye(string id) { Response.Write("Goodbye " + HttpUtility.HtmlEncode(id)); } }

allows you to call that method using the “cleaner” URL, and routing will pass the parameter by structured URL instead of a query string parameter: /Simple2/Goodbye/World

Working with Multiple Parameters What if you have a method with more than one parameter? This scenario is very common and rest assured that you can still use query strings, but if you want to pass both parameters via the URL segments, you must define a new route specifically for this situation. For example, suppose that you have an action method that calculates the distance between two points: public void Distance(int x1, int y1, int x2, int y2) { double xSquared = Math.Pow(x2 - x1, 2); double ySquared = Math.Pow(y2 - y1, 2); Response.Write(Math.Sqrt(xSquared + ySquared)); }

Using only the default route, the request would need to look like this: /Simple2/Distance?x2=1&y2=2&x1=0&y1=0

You can improve on this situation a bit by defining a route that allows you to specify the parameters in a cleaner format. This code goes inside the RegisterRoutes methods within the Global.asax.cs file, and uses the MapRoute method to define a new route: routes.MapRoute("distance", "Simple2/Distance/{x1},{y1}/{x2},{y2}", new { Controller = "Simple2", action = "Distance" } );

Notice that you are using the comma character to separate x and y coordinates. Now this action method is callable via the URL: /Simple2/Distance/0,0/1,2

The presence of commas in a URL might look strange, but routing is quite powerful! So far you’ve used Response.Write in these little example methods, but this violates the principle of separation of concerns. It’s really not the business of a controller to be managing the “views” of your data. That’s something better handled by the “V” in MVC; that is, Views.

www.it-ebooks.info

1076  ❘  Chapter 27   ASP.NET MVC

Views The View is responsible for providing the user interface (UI) to the user. It is given a reference to the Model, and it transforms that Model into a format ready to be presented to the user. In ASP.NET MVC, this consists of examining the ViewDataDictionary handed off to it by the Controller (accessed via the ViewData property) and transforming that to HTML. In the strongly typed View case, which is covered in more depth in the section on “Strongly Typed Views” later in the chapter, the ViewDataDictionary has a strongly typed Model object that the View renders. This Model might represent the actual domain object, such as a Product instance, or it might be a presentation Model object specific to the View, such as a ProductEditViewData instance. Take a quick look at an example of a View. The following code sample shows the Index View within the default ASP.NET MVC project template reformatted to fit the format of this book: <%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage" %> Home Page

<%= Html.Encode(ViewData["Message"]) %>

To learn more about ASP.NET MVC visit http://asp.net/mvc.



This is an extremely simple example of a View, but it’s useful for pointing out some of the key details of Views in ASP.NET MVC. One of the first things you’ll notice is that on the surface, it looks just like a Web form. ASP.NET MVC allows you to swap out different View engines, but the default View engine is a WebFormViewEngine. Technically, this is not a Web form because it doesn’t include the
tag; it’s really just an ASP.NET page. Views in ASP.NET MVC derive from a common base class, System.Web.Mvc .ViewPage, which itself derives from System.Web.UI.Page. Strongly typed Views derive from the generic ViewPage. In following with the principle of separation of concerns, Views should not contain application and business logic. In fact, they should contain as little code as possible. Although it’s perfectly acceptable for a View to contain View logic, Views are generally the most difficult part of the application to test in an automated fashion, and they therefore benefit from having very little code.

Specifying a View So far, this chapter has discussed what a View does and doesn’t do, but it hasn’t addressed how to specify the View that should render the output for a specific action. It turns out that this task is very easy when you follow the conventions implicit in the framework. When you create a new project template, notice that the project contains a “Views” directory structured in a very specific manner (see Figure 27-9). By convention, the Views directory contains a folder per Controller, with the same name as the Controller, sans the “Controller” suffix. Within each Controller folder, there’s a View file for each action method, named the same as the action method. This provides the basis for how Views are associated to an action method.

www.it-ebooks.info

Figure 27-9

Views  ❘ 

1077

For example, an action method can return a ViewResult via the View method like so: public class HomeController : Controller { public ActionResult Index() { ViewData["Title"] = "Home Page"; ViewData["Message"] = "Welcome to ASP.NET MVC!"; return View(); } }

This method ought to look familiar; it’s the Index action method of HomeController in the default project template. Because the View name was not specified, the ViewResult returned by this method looks for a View named the same as the action name in the /Views/ControllerName directory. The View selected in this case would be /Views/Home/Index.aspx. As with most things in ASP.NET MVC, this convention can be overridden. Suppose that you want the Index action to render a different View. You could supply a different View name like so: public ActionResult Index() { ViewData["Title"] = "Home Page"; ViewData["Message"] = "Welcome to ASP.NET MVC!"; return View("NotIndex"); }

In this case, it will still look in the /Views/Home directory, but choose NotIndex.aspx as the View. In some situations, you might even want to specify a View in a completely different directory structure. You can use the tilde syntax to provide the full path to the View like so: public ActionResult Index() { ViewData["Title"] = "Home Page"; ViewData["Message"] = "Welcome to ASP.NET MVC!"; return View("~/Some/Other/View.aspx"); }

When using the tilde syntax, you must supply the file extension of the View because this bypasses the View engine’s internal lookup mechanism for finding Views.

Strongly Typed Views Suppose that you have a list of Product instances you want to display in a View. One means of doing this is to simply add the products to the View data dictionary and iterate them over the View. For example, the code in your Controller action might look like this: public ActionResult List() { var products = new List(); for(int i = 0; i < 10; i++) { products.Add(new Product {ProductName = "Product " + i}); } ViewData["Products"] = products; return View(); }

In your View, you can then iterate and display the products like so:
    <% foreach(Product p in (ViewData["Products"] as IEnumerable)) {%>
  • <%= Html.Encode(p.ProductName) %>
  • <% } %>


www.it-ebooks.info

1078  ❘  Chapter 27   ASP.NET MVC

Because the ViewData indexer returns an object, casting ViewData[“Products”] to an IEnumerable before enumerating it is necessary. The code would be cleaner if you could provide the View with the type for the Model being sent in. This is where strongly typed Views come in. In the Controller method, you can specify the Model via an overload of the View method whereby you pass in the Model: public ActionResult List() { var products = new List(); for(int i = 0; i < 10; i++) { products.Add(new Product {ProductName = "Product " + i}); } return View(products); }

Behind the scenes, this sets the value of the ViewData.Model property to the value passed into the View method. The next step is to change the type of the View to inherit from ViewPage. The View really has no business having a code-behind file in the MVC Model. In fact, by default, no code-behind files exist for Views in ASP.NET MVC. If you want strongly typed Views, just add the type to derive from in the @Page directive like this: <%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage>" %>

This is the preferred way to have strongly typed Views. Now within the markup for the View, you can access the strongly typed ViewData.Model property, with full IntelliSense support.
    <% foreach(Product p in Model) {%>
  • <%= Html.Encode(p.ProductName) %>
  • <% } %>


Using HTML Helper Methods One of the traits of the ASP.NET MVC Framework often touted is that it puts you in full control of your application, including the HTML markup. Many announce this as a benefit of the framework. After all, full control is good, right? But it’s really a characteristic of the framework that’s only good or bad depending on the circumstance. There are times when you don’t want to have control over the markup. You would rather drop a control and have it figure out the markup because you don’t care how it looks. Other times, you want to have absolute control over the markup. Being in control is great, but it also means more responsibility. You are now responsible for outputting markup that would have otherwise been handled by a server control in the Web forms world. HTML helpers provide a middle ground. These are methods included with the framework that help with rendering markup for very common cases. In most cases, they handle common mistakes such as forgetting to encode attribute values and so on.

HtmlHelper Class and Extension Methods The ViewPage class has an HtmlHelper property named Html. When you look at the methods of HtmlHelper, you’ll notice they are rather sparse. This property is really an anchor point for attaching extension methods. When you import the System.Web.Mvc.Html namespace (imported by default in the default template), the Html property suddenly lights up with a bunch of helper methods.

www.it-ebooks.info

Summary  ❘ 

1079

In the screenshot in Figure 27-10, the extension methods are denoted by the blue down arrow (gray in the dialog shown here). One benefit of the HtmlHelper-style approach is that, as they are just regular extension methods, if you don’t like the helper methods included with the framework, you can remove this namespace and attach your own HTML helper extension methods. Likewise, it provides a convenient conventional place to add your own helper methods by simply writing extension methods of the HtmlHelper class. All helpers share a few common patterns that are worth calling out now:

Figure 27-10

➤➤

All helper’s attributes encode attribute values.

➤➤

All helper’s HTML encode values they display, such as link text.

➤➤

Helpers that accept a RouteValueDictionary have a corresponding overload that allows you to specify an anonymous object as the dictionary.

➤➤

Likewise, helpers that accept an IDictionary used to specify HTML attributes, have a corresponding overload that allows you to specify an anonymous object as the dictionary.

➤➤

Helpers used to render form fields will automatically look up their current value in the ModelState dictionary. The name argument to the helper is used as the key to the dictionary.

➤➤

If the ModelState contains an error, the form helper associated with that error will render a CSS class of “input-validation-error” in addition to any explicitly specified CSS classes. The default stylesheet, style.css, included in the project template contains styling for this class.

Views and their ViewEngines have a very specific, constrained purpose. They exist to take data passed to them from the Controller, and they generate formatted output, usually HTML. Other than those simple responsibilities, or “concerns,” as the developer, you are empowered to achieve the goals of your View in any way that makes you happy.

Summary The ASP.NET Web forms developer will need to get used to many differences when working with ASP.NET MVC versus Web forms. In many ways, working with ASP.NET MVC will feel like “taking a step back 10 years” to classic ASP — especially when working with the UI and Views. For some, this is a welcome change and a breath of fresh air; for others, it just doesn’t work. It does take some getting used to, but in the end, the core ASP.NET functionality and the .NET Framework in general are there to support you. Ultimately, the most important thing to remember is that ASP.NET Web forms and ASP.NET MVC sit on top of ASP.NET proper. Think of it as ASP.NET > Web forms and ASP.NET > ASP.NET MVC. There’s so much underneath both techniques that you can use either or both without fear. Many people find a hybrid model works for them, or they use a hybrid as they move from one model to the other. Pick the model that makes you feel most productive and run with it. Be sure to download the free 200-page PDF tutorial that shows you how to create a complete ASP.NET MVC application at http://tinyurl.com/aspnetmvc. Again, for more in-depth coverage of ASP.NET MVC, check out Professional ASP.NET MVC 1.0 from Wiley (2009). There are hundreds of resources, videos, starter kits and very active forums at http://www.asp.net/mvc to help you get started.

www.it-ebooks.info

www.it-ebooks.info

28

Using Business objects whaT’s in This chaPTer? ➤

�NET interoperability



Working with managed and unmanaged objects



COM and ASP�NET

One of the best practices in programming is to separate your application into workable and separate components — also known as business objects. This makes your applications far easier to manage and enables you to achieve the goal of code reuse because you can share these components among different parts of the same application or between entirely separate applications. Using business components enables you to build your ASP.NET applications using a true three-tier model where the business tier is in the middle between the presentation and data tiers. In addition, using business objects enables you to use multiple languages within your ASP.NET applications. Business objects can be developed in one programming language while the code used for the presentation logic is developed in another. If you are moving any legacy applications or aspects of these applications to an ASP.NET environment, you might fi nd that you need to utilize various COM components. This chapter shows you how to use both .NET and COM components in your ASP.NET pages and code. This chapter also explains how you can mingle old ActiveX (COM) DLLs with new .NET components. So when all is said and done, you should feel somewhat relieved. You will see that you have not wasted all the effort you put into building componentized applications using the “latest” ActiveX technologies.

using Business oBjecTs in asP.neT 4 Chapter 1 of this book provides an introduction to using .NET business objects within your ASP.NET 4 applications. ASP.NET includes a folder, App_Code, which you can place within your ASP.NET applications to hold all your .NET business objects. The nice thing about the App_Code folder is that you can simply place your uncompiled .NET objects (such as Calculator.vb or Calculator.cs) into this folder and ASP.NET takes care of compiling the objects into usable .NET business objects.

www.it-ebooks.info

1082  ❘  Chapter 28   Using Business Objects

Chapter 1 also shows how you can place within the App_Code folder multiple custom folders that enable you to use business objects written in different programming languages. Using this method enables ASP.NET to compile each business object into the appropriate DLLs to be used by your ASP.NET applications.

Creating Precompiled .NET Business Objects Even though the App_Code folder is there for your use, you might choose instead to precompile your business objects into DLLs to be used by your ASP.NET applications. This method was utilized prior to ASP.NET 2.0 and is still a method that is available today. You also might not have a choice if you are receiving your .NET business objects only as DLLs. For now, look at how to create a simple .NET business object using Visual Studio 2010. The first step is not to create an ASP.NET project but to choose File ➪ New ➪ Project from the Visual Studio menu. This launches the New Project dialog. From this dialog, select Class Library as the project type and name the project Calculator (see Figure 28-1).

Figure 28-1

Using the Class1.vb or Class1.cs file that is created in the project for you, modify the class to be a simple calculator with Add, Subtract, Multiply, and Divide functions. This is illustrated using C# in Figure 28-2. One point to pay attention to when you build your .NET components is the assembly’s metadata that is stored along with the assembly. Looking at the project’s properties, click the Application tab (the first tab available). Note that you can get to the project’s properties by right-clicking on the project title in the Solution Explorer. On this tab’s page is a button labeled Assembly Information. Clicking this button gives you a dialog where you can put in the entire business object’s metadata, including the assembly’s versioning information (see Figure 28-3).

www.it-ebooks.info

Using Business Objects in ASP.NET 4  ❘ 

1083

Figure 28-2

Figure 28-3

You are now ready to compile the business object into a usable object by choosing Build ➪ Build Calculator from the Visual Studio menu. This process compiles everything contained in this solution down to a Calculator.dll file. You can find this DLL in your project’s bin\debug folder, which by default, is C:\Users\[user]\Documents\Visual Studio 10\Projects\Calculator\Calculator\bin\Debug\ Calculator.dll, when using Windows 7.

www.it-ebooks.info

1084



chaPTer 28 using business obJects

Besides using Visual Studio 2010 to build and compile your business objects into DLLs, you can do it yourself manually. In Notepad, you simply create the same class fi le as shown in Figure 28-2 and save the fi le as Calculator.vb or Calculator.cs depending on the language you are using. After saving the fi le, you must compile the class into an assembly (a DLL). The .NET framework provides you with a compiler for each of the targeted languages. This book focuses on the Visual Basic 2010 and C# 2010 compilers that come with the framework. To compile this class, open the Visual Studio 2010 Command Prompt found at All Programs ➪ Microsoft Visual Studio 2010 ➪ Visual Studio Tools ➪ Visual Studio 2010 Command Prompt. From the provided DOS prompt, navigate to the directory that is holding your Calculator class (an example navigation command is cd c:\My Files). From the DOS prompt, type the following command if you are using the Visual Basic compiler: vbc /t: library Calculator.vb

If your class is in C#, you use the following command: csc /t:library Calculator.cs

As stated, each language uses its own compiler. Visual Basic uses the vbc.exe compiler found at C:\Windows\Microsoft.NET\Framework\v4.0.xxxxx\. The C# compiler, csc.exe, is in the same folder. In the preceding examples, /t:library states that you are interested in compiling the Calculator.vb (or .cs) class fi le into a DLL and not an executable (.exe), which is the default. Following the t:/library command is the name of the fi le to be compiled. You can give the compiler many different commands — even more than Visual Studio 2010 offers. For example, if you want to make references to specifi c DLLs in your assembly, you must add commands such as /r:system.data.dll. To get a full list of all the compiler options, check out the MSDN documentation. After you have run the commands through the compiler, the DLL is created and ready to go.

using Precompiled Business objects in your asP.neT applications To use any DLLs in your ASP.NET 4 project, you must create a Bin folder in the root directory of your application by right- clicking on the project within the Solution Explorer and selecting Add ASP.NET Folder ➪ Bin. In Visual Studio 2010, the Bin directory’s icon appears as a gray folder with a gear next to it. Add your new DLL to this folder by right- clicking on the folder and selecting the Add Reference option from the menu provided. The Add Reference dialog appears. From this dialog, select the Browse tab and browse until you fi nd the Calculator.dll. When you fi nd it, highlight the DLL and click OK to add it to the Bin folder of your project. Figure 28- 4 shows the Add Reference dialog. figure 28-4

www.it-ebooks.info

COM Interop: Using COM Within .NET  ❘ 

1085

Calculator.dll is added to your project and is now accessible by the entire project. This means that you now have access to all the functions exposed through this interface. Figure 28-5 shows an example of how IntelliSense makes exploring this .NET component easier than ever.

Figure 28-5

As you can see, creating .NET components and using them in your ASP.NET applications is rather simple. Next, it’s time to take a look at using COM components.

COM Interop: Using COM Within .NET Microsoft knows that every one of its legions of developers out there would be quite disappointed if they couldn’t use the thousands of COM controls that it has built, maintained, and improved over the years. Microsoft knows that nobody would get up and walk away from these controls to a purely .NET world. To this end, Microsoft has provided us with COM Interoperability. COM Interop (for short) is a technology that enables .NET to wrap the functionality of a COM object with the interface of a .NET component so that your .NET code can communicate with the COM object without having to use COM techniques and interfaces in your code. Figure 28-6 illustrates the Runtime Callable Wrapper, the middle component that directs traffic between the .NET code and the COM component.

Your New .NET Code

.NET’s Built-In Interoperability Technology

Your existing ActiveX Component Code

Runtime Callable Wrapper (RCW)

ActiveX DLL or OCX (COM Component)

.NET Code (C# or VB.NET)

Managed Code

Unmanaged Code

Figure 28-6

www.it-ebooks.info

1086  ❘  Chapter 28   Using Business Objects

The Runtime Callable Wrapper The Runtime Callable Wrapper, or RCW, is the magic piece of code that enables interaction to occur between .NET and COM. One RCW is created for each COM component in your project. To create an RCW for a COM component, you can use Visual Studio 2010. To add an ActiveX DLL to the References section of your project, choose Website ➪ Add Reference or choose the Add Reference menu item that appears when you right-click the root node of your project in the Solution Explorer. The Add Reference dialog box appears with five tabs: .NET, COM, Projects, Browse, and Recent, as shown in Figure 28-7. For this example, select the COM tab and locate the component that you want to add to your .NET project. After you have located the component, highlight the item and click OK to add a reference to the component to your project. You can then find the newly added component inside a newly created Bin folder in your project. Your Interop library is automatically created for you from the ActiveX DLL that you told Visual Studio 2010 to use. This Interop library is the RCW component customized for your ActiveX control, as shown previously in Figure 28-6. The name of the Interop file is simply Interop .OriginalName.DLL.

Figure 28-7

You can also create the RCW files manually instead of doing it through Visual Studio 2010. In the .NET framework, you will find a method to create RCW Interop files for controls manually through a command-line tool called the Type Library Importer. You invoke the Type Library Importer by using the tlbimp.exe executable. For example, to create the Interop library for the SQLDMO object used earlier, start up a Visual Studio 2010 Command Prompt from the Microsoft Visual Studio 2010 ➪ Visual Studio Tools group within your Start menu. From the comment prompt, type tlbimp sqldmo.dll /out:sqldmoex.dll

In this example, the /out: parameter specifies the name of the RCW Interop library to be created. If you omit this parameter, you get the same name that Visual Studio would generate for you. The Type Library Importer is useful when you are not using Visual Studio 2010 as your development environment, if you want to have more control over the assemblies that get created for you, or if you are automating the process of connecting to COM components. The Type Library Importer is a wrapper application around the TypeLibConvertor class of the System. Runtime.InteropServices namespace.

Using COM Objects in ASP.NET Code To continue working through some additional examples, you next take a look at a simple example of using a COM object written in Visual Basic 6 within an ASP.NET page.

www.it-ebooks.info

COM Interop: Using COM Within .NET  ❘ 

1087

In the first step, you create an ActiveX DLL that you can use for the upcoming examples. Add the Visual Basic 6 code shown in Listing 28-1 to a class called NameFunctionsClass and compile it as an ActiveX DLL called NameComponent.dll. Listing 28-1:  VB6 code for ActiveX DLL, NameComponent.DLL Option Explicit Private m_sFirstName As String Private m_sLastName As String Public Property Let FirstName(Value As String) m_sFirstName = Value End Property Public Property Get FirstName() As String FirstName = m_sFirstName End Property Public Property Let LastName(Value As String) m_sLastName = Value End Property Public Property Get LastName() As String LastName = m_sLastName End Property Public Property Let FullName(Value As String) m_sFirstName = Split(Value, " ")(0) If (InStr(Value, " ") > 0) Then m_sLastName = Split(Value, " ")(1) Else m_sLastName = "" End If End Property Public Property Get FullName() As String FullName = m_sFirstName + " " + m_sLastName End Property Public Property Get FullNameLength() As Long FullNameLength = Len(Me.FullName) End Property

Now that you have created an ActiveX DLL to use in your ASP.NET pages, the next step is to create a new ASP.NET project using Visual Studio 2010. Replace the HTML code in the Default.aspx file with the HTML code shown in Listing 28-2. This code adds a number of text boxes and labels to the HTML page, as well as the Visual Basic or C# code for the functionality. Listing 28-2:  Using NameComponent.dll <%@ Page Language="VB" %>

VB

Using COM Components

First Name:  

Last Name:  

Full Name:  

Full Name Length:   0



C#

<%@ Page Language="C#" %>

Now you must add the reference to the ActiveX DLL that you created in the previous step. To do so, follow these steps:

1. 2. 3. 4. 5.

Right- click your project in the Solution Explorer dialog. Select the Add Reference menu item. In the Add Reference dialog box, select the Browse tab. Locate the NameComponent.dll object by browsing to its location. Click OK to add NameComponent.dll to the list of selected components and close the dialog box.

If you are not using Visual Studio 2010 or code - behind pages, you can still add a reference to your COM control by creating the RCW manually using the Type Library Converter and then placing an Imports statement (VB) or using statement (C#) in the page. After you have selected your component using the Add Reference dialog, an RCW fi le is created for the component and added to your application. That’s all there is to it! Simply run the application to see the COM interoperability layer in action. Figure 28 -8 shows the ASP.NET page that you created. When the Analyze Name button is clicked, the fields in the First Name, Last Name, and Full Name text boxes are sent to the RCW to be passed to the NameComponent.DLL ActiveX component. Data is retrieved in the same manner to repopulate the text boxes and to indicate the length of the full name.

www.it-ebooks.info

1090  ❘  Chapter 28   Using Business Objects

Figure 28-8

Accessing Tricky COM Members in C# Sometimes, some members of COM objects do not expose themselves properly to C#. In the preceding examples, the String properties did not expose themselves, but the Long property (FullNameLength) did. You know when there is a problem because, although you can see the property, you cannot compile the application. For example, instead of the code shown in Listing 28-2 for C#, use the following piece of code to set the FirstName property of the NameComponent.dll ActiveX component: if (FirstName.Text.Length > 0) Name.FirstName = FirstName.Text.ToString();

When you try to compile this code, you get the following error: c:\inetpub\wwwroot\wrox\Default.aspx.cs(67): Property, indexer, or event 'FirstName' is not supported by the language; try directly calling accessor methods 'NameComponent.NameFunctionsClass.get_FirstName()' or 'NameComponent.NameFunctionsClass.set_FirstName(ref string)'

The FirstName property seems to be fine. It shows up in IntelliSense, but you can’t use it. Instead, you must use set_FirstName (and get_FirstName to read). These methods do not show up in IntelliSense, but rest assured, they exist. Furthermore, these methods expect a ref string parameter rather than a String. In the example from Listing 28-2, two steps are used to do this properly. First, String is assigned to a local variable, and then the variable is passed to the method using ref.

Releasing COM Objects Manually One of the great things about .NET is that it has its own garbage collection — it can clean up after itself. This is not always the case when using COM interoperability, however. .NET has no way of knowing when to release a COM object from memory because it does not have the built-in garbage collection mechanism that .NET relies on. Because of this limitation, you should release COM objects from memory as soon as possible using the ReleaseComObject class of the System.Runtime.InteropServices.Marshal class: C#

System.Runtime.InteropServices.Marshal.ReleaseComObject(Object);

Note that if you attempt to use this object again before it goes out of scope, you would raise an exception.

www.it-ebooks.info

COM Interop: Using COM Within .NET  ❘ 

1091

Error Handling Error handling in .NET uses exceptions instead of the HRESULT values used by Visual Basic 6 applications. Luckily, the RCW does most of the work to convert between the two. Take, for example, the code shown in Listing 28-3. In this example, a user-defined error is raised if the numerator or the denominator is greater than 1000. Also notice that we are not capturing a divide-by-zero error. Notice what happens when the ActiveX component raises the error on its own. Begin this example by compiling the code listed in Listing 28-3 into a class named DivideClass within an ActiveX component called DivideComponent.dll. Listing 28-3:  Raising errors in VB6 Public Function DivideNumber(Numerator As Double, _ Denominator As Double) As Double If ((Numerator > 1000) Or (Denominator > 1000)) Then Err.Raise vbObjectError + 1, _ "DivideComponent:Divide.DivideNumber", _ "Numerator and denominator both have to " + _ "be less than or equal to 1000." End If DivideNumber = Numerator / Denominator End Function

Next, create a new ASP.NET project; add a reference to the DivideComponent.dll (invoking Visual Studio 2010 to create its own copy of the RCW). Remember, you can also do this manually by using the tlbimp executable. Now add the code shown in Listing 28-4 to an ASP.NET page. Listing 28-4:  Error handling in .NET <%@ Page Language="VB" %>

VB

Using COM Components

continues

www.it-ebooks.info

1092  ❘  Chapter 28   Using Business Objects

Listing 28-4  (continued)

Numerator:  

Denominator:  

Numerator divided by Denominator:   0



C#

<%@ Page Language="C#" %>

The code in Listing 28-4 passes the user-entered values for the Numerator and Denominator to the DivideComponent.dll ActiveX component for it to divide. Running the application with invalid data gives the result shown in Figure 28-9.

www.it-ebooks.info

COM Interop: Using COM Within .NET  ❘ 

1093

Figure 28-9

Depending on the language that you are using to run the ASP.NET application, you will see different values for different sets of data. For valid inputs, you will always see the correct result, of course, and for any input that is over 1000, you see the Visual Basic 6 appointed error description of Numerator and denominator both have to be less than or equal to 1000. However, for invalid strings, Visual Basic 2010 reports Cast from string “abc” to type ‘Double’ is not valid. whereas C# reports Input string was not in a correct format. For a divide by zero, they both report Divide by Zero because the error is coming directly from the Visual Basic 6 runtime.

Deploying COM Components with .NET Applications Deploying COM components with your .NET applications is very easy, especially when compared to just deploying ActiveX controls. Two scenarios are possible when deploying .NET applications with COM components: ➤➤

Using private assemblies

➤➤

Using shared or public assemblies

Private Assemblies Installing all or parts of the ActiveX component local to the .NET application is considered installing private assemblies. In this scenario, each installation of your .NET application on the same machine has, at least, its own copy of the Interop library for the ActiveX component you are referencing, as shown in Figure 28-10. C:\Program Files\First Application Location\

MyApp.exe

Interop.MyCOM.dll

(Managed Code)

(RCW)

C:\Program Files\Third Party COM Controls\

MyCOM.DLL C:\Program Files\Second Application Location\

MyApp.exe

Interop.MyCOM.dll

(Managed Code)

(RCW)

(ActiveX DLL)

Figure 28-10

www.it-ebooks.info

1094



chaPTer 28 using business obJects

Whether you decide to install the ActiveX component as local to the application or in a shared directory for all calling applications is up to you. It was once considered proper practice to separate ActiveX components into their own directory so that if these components were referenced again by other applications, you did not have to register or install the file for a second time. Using this method meant that when you upgraded a component, you automatically upgraded all the utilizing applications. However, this practice didn’t work out so well. In fact, it became a very big contributor to DLL hell and the main reason why Microsoft began promoting the practice of installing private .NET component assemblies. After you have your components physically in place, the only remaining task is to register the ActiveX component using regsvr32, just as you would when deploying an ActiveX- enabled application.

Public assemblies The opposite of a private assembly is a public assembly. Public assemblies share the RCW Interop DLL for other applications. In order to create a public assembly, you must put the RCW fi le into the Global Assembly Cache (GAC), as shown in Figure 28-11. C:\Program Files\First Application Location\

Global Assembly Cache (GAC)

MyApp.exe

Interop.MyCOM.dll

(Managed Code)

(RCW)

C:\Program Files\Second Application Location\ MyCOM.dll YourApp.exe

(ActiveX DLL)

(Managed Code) C:\Program Files\Third Party COM Controls\ figure 28-11

You can fi nd the GAC at C:\Windows\assembly. Installing items in the GAC can be as simple as dragging-and- dropping an item into this folder through Windows Explorer. Although the GAC is open to everyone, blindly installing your components into this section is not recommended unless you have a very good reason to do so. You can also add items to the GAC from the command line using the Global Assembly Cache Tool (Gacutil.exe). It enables you to view and manipulate the contents of the global assembly cache and download cache. Although the Explorer view of the GAC provides similar functionality, you can use Gacutil.exe from build scripts, makefi le fi les, and batch fi les. Finding a very good reason to install your ActiveX Interop Assemblies into the GAC is hard. If we had to pick a time to do this, it would be if and when we had a highly shared ActiveX component that many

www.it-ebooks.info

Using .NET from Unmanaged Code  ❘ 

1095

.NET applications would be utilizing on the same machine. In a corporate environment, this might occur when you are upgrading existing business logic from ActiveX to .NET enablement on a server that many applications use. In a commercial setting, we avoid using the GAC.

Using .NET from Unmanaged Code .NET provides the opposite of COM interoperability by enabling you to use your newly created .NET components within unmanaged code. This section discusses using .NET components with Visual Basic 6 executables. The techniques shown in this section are identical when you are using ActiveX OCXs or DLLs instead of executables. The COM-Callable Wrapper (CCW) is the piece of the .NET framework that enables unmanaged code to communicate with your .NET component. The CCW, unlike the RCW, is not a separate DLL that you distribute with your application. Instead, the CCW is part of the .NET framework that gets instantiated once for each .NET component that you are using. Figure 28-12 shows how the CCW marshals the communication between the unmanaged code and the .NET component in much the same way that the RCW marshals the code between managed code and COM code.

Your ActiveX Code

COM Code

.NET’s Built-In Interoperability Technology

COMCallable Wrapper (CCW)

Unmanaged Code

Your .NET Component Code

.NET Component

Managed Code

Figure 28-12

The COM-Callable Wrapper The COM-Callable Wrapper or CCW, as previously stated, is not a separate DLL like the RCW. Instead, the CCW uses a specially created type library based on the .NET component. This type library is called an Interop Type Library. The Interop Type Library is statically linked with the unmanaged code so that this code can communicate with the CCW about the .NET component included in your application. In order for a .NET component to generate an Interop Type Library, you tell Visual Studio 2010 to generate it when the component is built. Both Visual Basic and C# projects have a setting in the Compile properties section of the Class Library project’s Property Pages dialog. Right-click the project in the Solution Explorer and choose Properties to see the project’s properties. Figure 28-13 shows the project’s properties for a Visual Basic 2010 Class Library application. This is shown directly in the Visual Studio document window.

www.it-ebooks.info

1096  ❘  Chapter 28   Using Business Objects

Figure 28-13

C# has a slightly different dialog, as shown in Figure 28-14. In both dialogs, the property is called Register for COM Interop. In Visual Basic, you can find this property on the Compile page; in C#, you can find it on the Build tab of the properties pages.

Figure 28-14

www.it-ebooks.info

Using .NET from Unmanaged Code  ❘ 

1097

After you set the Register for COM Interop option by checking the check box, when you build the project, a separate type library file (.tlb) is generated for the DLL that you are building. This .tlb file is your key to including .NET components in COM applications. Normally in Visual Basic, when you add a reference to a DLL, you navigate from the References section of the Visual Basic project to find the ActiveX DLL that you want to add. If you use .NET components, they cannot be properly referenced in this manner because they are not ActiveX. Instead, you reference the Interop Type Library, which makes the functionality of the corresponding .NET component available to your application. The .NET framework also gives you a method to create Interop Type Library files manually for .NET components. You do this through a command-line tool called the Type Library Exporter (as compared to the Type Library Importer used for COM Interoperability). The Type Library Exporter is invoked using the tlbexp.exe executable. For example, to create the Interop Type Library for the NameComponent.dll in the next example, you use the following command: tlbexp NameComponent.dll /out:NameComponentEx.tlb

The /out: parameter specifies the name of the Interop Type Library that is to be created. If you omit this parameter, you get a file with the same name as the ActiveX component, but with a .tlb extension. The Type Library Exporter is useful when you are not using Visual Studio 2010 as your development environment, if you want to have more control over the assemblies that get created for you, or if you are automating the process of connecting to .NET components.

Using .NET Components Within COM Objects The next example illustrates how you can utilize .NET components within COM code. To begin, create and compile the .NET code found in Listing 28-5 in either Visual Basic or C#. After you have typed your code into your Class Library project, build the component and call it NameComponent. Remember to choose to include the Register for the COM Interop setting of True (by checking the appropriate check box) from the project properties pages, as shown in Figure 28-13 for Visual Basic code and Figure 28-14 for C# code. If you aren’t using Visual Studio 2010, you can use tblexp.exe to generate the Interop Type Library manually as described previously. Listing 28-5:  The .NET component Public Class NameFunctions

VB

Private m_FirstName As String Private m_LastName As String Public Property FirstName() As String Get Return m_FirstName End Get Set(ByVal Value As String) m_FirstName = Value End Set End Property Public Property LastName() As String Get Return m_LastName End Get

continues

www.it-ebooks.info

1098  ❘  Chapter 28   Using Business Objects

Listing 28-5  (continued) Set(ByVal Value As String) m_LastName = Value End Set End Property Public Property FullName() As String Get Return m_FirstName + " " + m_LastName End Get Set(ByVal Value As String) m_FirstName = Split(Value, " ")(0) m_LastName = Split(Value, " ")(1) End Set End Property Public ReadOnly Property FullNameLength() As Long Get FullNameLength = Len(Me.FullName) End Get End Property End Class

C#

using System; using System.Runtime.InteropServices; namespace NameComponent { [ComVisible(true)] public class NameFunctions { private string m_FirstName; private string m_LastName; public string FirstName { get { return m_FirstName; } set { m_FirstName=value; } } public string LastName { get { return m_LastName; } set { m_LastName=value; } }

www.it-ebooks.info

Using .NET from Unmanaged Code  ❘ 

1099

public string FullName { get { return m_FirstName + " " + m_LastName; } set { m_FirstName=value.Split(' ')[0]; m_LastName=value.Split(' ')[1]; } } public long FullNameLength { get { return this.FullName.Length; } } } } Filenames NameFunctions.vb and NameFunctions.cs

After you have created the .NET component, you can then create the consuming Visual Basic 6 code shown in Listing 28-6. Listing 28-6:  VB6 code using the .NET component Option Explicit Public Sub Main() Dim o As NameComponent.NameFunctions Set o = New NameComponent.NameFunctions o.FirstName = "Bill" o.LastName = "Evjen" MsgBox "Full Name is: " + o.FullName MsgBox "Length of Full Name is: " + CStr(o.FullNameLength) o.FullName = "Scott Hanselman" MsgBox "First Name is: " + o.FirstName MsgBox "Last Name is: " + o.LastName o.LastName = "Evjen" MsgBox "Full Name is: " + o.FullName Set o = Nothing End Sub

Remember to add a reference to the .NET component. You choose Project ➪ References and select the .NET component that was created either by Visual Studio or by manually using tlbexp.exe.

www.it-ebooks.info

1100  ❘  Chapter 28   Using Business Objects

When you run the code in Listing 28-6, you see that Visual Basic 6 does not miss a beat when communicating with the .NET component. Registering the assemblies yourself is also possible. Earlier you learned how to manually create Interop Type Libraries with the Type Library Exporter. This tool does not register the assemblies created but instead generates only the type library. To register the assemblies yourself, you use the Assembly Registration Tool (regasm.exe). This tool is similar to the regsvr32.exe for .NET components. To use regasm.exe, use a command syntax similar to the following example: regasm NameComponent.dll /tlb:NameComponentEx.tlb /regfile:NameComponent.reg

The /tlb: option specifies the name of the type library, and the /regfile: option specifies the name of a registry file to be created that you can use later in an installation and deployment application.

Early versus Late Binding The preceding example illustrates the use of early binding, the technique most Visual Basic 6 developers are used to. However, in some cases, using late binding is desirable. Performing late binding with .NET components is similar to performing late binding with ActiveX components, as shown in Listing 28-7. Listing 28-7:  Late binding with VB6 Option Explicit Public Sub Main() Dim o As Object Set o = CreateObject("NameComponent.NameFunctions") o.FirstName = "Bill" o.LastName = "Evjen" MsgBox "Full Name is: " + o.FullName MsgBox "Length of Full Name is: " + CStr(o.FullNameLength) o.FullName = "Scott Hanselman" MsgBox "First Name is: " + o.FirstName MsgBox "Last Name is: " + o.LastName o.LastName = "Evjen" MsgBox "Full Name is: " + o.FullName Set o = Nothing End Sub

Error Handling Handling errors that are raised from .NET components in Visual Basic 6 is easily accomplished via the Interop functionality. Listing 28-8 shows code for both Visual Basic and C# to throw exceptions for a custom error. When the Numerator or the Denominator parameters are greater than 1000 in the Divide function, a custom exception is thrown up to the calling code, which is Visual Basic 6 in this example.

www.it-ebooks.info

Using .NET from Unmanaged Code  ❘ 

1101

Notice how the divide-by-zero error possibility is not handled in this example. This is done intentionally to demonstrate how interoperability handles unhandled errors. Listing 28-8:  Raising errors Public Class CustomException Inherits Exception

VB

Sub New(ByVal Message As String) MyBase.New(Message) End Sub End Class Public Class DivideFunction Public Function Divide(ByVal Numerator As Double, _ ByVal Denominator As Double) As Double If ((Numerator > 1000) Or (Denominator > 1000)) Then Throw New CustomException("Numerator and denominator both " & _ "have to be less than or equal to 1000.") End If Divide = Numerator / Denominator End Function End Class

C#

using System; namespace DivideComponent { public class CustomException:Exception { public CustomException(string message):base(message) { } } public class DivideFunction { public double Divide(double Numerator, double Denominator) { if ((Numerator > 1000) || (Denominator > 1000)) throw new CustomException("Numerator and denominator " + "both have to be less than or equal to 1000."); return Numerator / Denominator; } } }

Now that you have the code for the .NET component, compile it with the Register for COM Interop flag set to True in the project’s Property Pages dialog and call the component DivideComponent. Listing 28-9 shows the consuming Visual Basic 6 code. Remember to add a reference to the Interop Type Library of the DivideComponent generated by Visual Studio.

www.it-ebooks.info

1102  ❘  Chapter 28   Using Business Objects

Listing 28-9:  VB6 experiencing .NET errors Option Explicit Public Sub Main() Dim o As DivideComponent.DivideFunction Set o = New DivideComponent.DivideFunction MsgBox "1 divided by 3: " + CStr(o.divide(1, 3)) MsgBox "1 divided by 0: " + CStr(o.divide(1, 0)) MsgBox "2000 divided by 3000: " + CStr(o.divide(2000, 3000)) Set o = Nothing End Sub

The Visual Basic 6 code example in Listing 28-9 does not handle the errors thrown by the .NET component, but it can easily do so using On Error, Visual Basic 6’s method for trapping raised errors. Instead of trapping the errors, make sure that the Error Trapping setting in the Options dialog of Visual Basic 6 is set to Break in Class Module. When the application is run, the first example of 1 divided by 3 works fine; you see the output properly. The second example, which you would expect to end in a divide-by-zero error, does not. Instead, an invalid property value is returned to Visual Basic 6. The final example, which does not pass the custom error handling in the .NET component, raises a Visual Basic error, as you would expect.

Deploying .NET Components with COM Applications Deploying .NET components with COM applications is similar to deploying COM components with .NET applications. Two scenarios exist in this deployment scheme: ➤➤

Using private assemblies

➤➤

Using shared or public assemblies

The following sections discuss these two scenarios. C:\Program Files\First Application Location\

Private Assemblies Private assemblies mean the deployment of the .NET component is installed in each individual directory where the application is installed, within the same machine. The only needed component is the .NET DLL and the calling application. The Interop Type Library that you created earlier with Visual Studio 2010 or tlbexp.exe is statically linked with the component or application that references the .NET component. The only additional task you must complete is to properly register the .NET assembly using regasm.exe. This extra step is not needed in 100 percent of .NET applications; it is required only for the interoperability for the unmanaged code to reference the managed code. Figure 28-15 illustrates using private assemblies.

MyApp.exe

MyDotNet.dll

(Unmanaged Code)

(Managed Code)

C:\Program Files\Second Application Location\ MyApp.exe

MyDotNet.dll

(Unmanaged Code)

(Managed Code)

Figure 28-15

www.it-ebooks.info

Summary  ❘ 

1103

Public Assemblies Figure 28-16 illustrates the use of a public assembly. This scenario involves installing the .NET component into the Global Assembly Cache (GAC). C:\Program Files\First Application Location\

Global Assembly Cache (GAC)

MyApp.exe

MyDotNet.dll

(Unmanaged Code)

(Managed Code)

C:\Program Files\Second Application Location\

YourApp.exe (Unmanaged Code)

Figure 28-16

As with private assemblies, deployment requires only the .NET component and the consuming unmanaged code — besides the need to register the interop assembly using regasm.exe.

Summary When .NET was introduced, some initial concern arose about existing ActiveX controls and their place in Microsoft’s vision for the future of component development. Immediately, Microsoft stepped up to the bat and offered the robust and solid .NET Interop functionality to provide a means to communicate not only from .NET managed code to COM unmanaged code, but also from COM unmanaged code to .NET managed code. The latter was an unexpected, but welcome, feature for many Visual Basic 6 developers and future .NET component builders. This layer of interoperability has given Microsoft the position to push .NET component development as a solution for not only newly created applications, but also applications that are currently in development and ones that have already been rolled out and are now in the maintenance phase. Interoperability has given .NET developers a means to gradually update applications without rewriting them entirely, and it has given them a way to start new .NET projects without having to wait for all the supporting components to be developed in .NET.

www.it-ebooks.info

www.it-ebooks.info

29

ado.neT entity framework whaT’s in This chaPTer? ➤

Understanding mapping and relationships



Creating an Entity Data Model



Using stored procedures and the EntityDataSource control in an EDM

Nowadays, accessing data is one of the main things almost every application must do. Nearly all applications deal with data in some manner, whether that data comes from memory (in-memory data), databases, XML fi les, text fi les, or something else. Many developers fi nd moving from the strongly typed object- oriented world of C# or Visual Basic to the data tier where objects are second- class citizens very difficult. Before the ADO.NET Entity Framework, the transition from the one world to the next was kludgy at best and was full of error-prone actions. Data access can complicate your application development for a number of reasons, including the fact that the database space and the application space are two different worlds entirely. Microsoft consistently attempts to simplify a programmer’s common tasks that are laborious and complicated by abstracting the difficulties of these tasks through a combination of new interfaces and IDE assistance. With the earlier release of the ADO.NET Entity Framework, you will fi nd that navigating the world between the database and your application is easier than ever. With one of the available .NET programming languages, using objects in your code really means a wonderful ability to work with strongly typed objects in your code. As a programmer, you can navigate very easily through namespaces, work with a debugger in the Visual Studio IDE, and more. However, when it comes to accessing data, things are dramatically different. You end up in a world that is not strongly typed, where debugging is painful or even non- existent, and you spend most of the time sending strings to the database as commands. As a developer, you also must be aware of the underlying data and how it is structured or how all the data points relate to one another. Using something like the ADO.NET Entity Framework (along with LINQ), you now have a lightweight façade that provides a strongly typed interface to the underlying datastores that you are working with. Using these technologies, you can stay within the coding environment you are used to, and you have the ability to access the underlying data as objects that work with the IDE, IntelliSense, and even debugging.

www.it-ebooks.info

1106  ❘  Chapter 29   ADO.NET Entity Framework

This chapter provides an overview of the ADO.NET Entity Framework and how you can use this framework within your ASP.NET applications. Note that the ADO.NET Entity Framework is available only as part of the .NET Framework 3.5 SP1 or the .NET Framework 4. This means that you must have one of these versions installed in addition to Visual Studio 2008 SP1, or you must be using Visual Studio 2010.

Can We Speak the Same Language? As discussed earlier, building applications that communicate with databases should be an easier task than it is. The difficulty is that objects in code and objects in the database are inherently different beasts. Communicating objects from the database tier to other tiers of your application stack is the primary reason for the added complexity. The ADO.NET Entity Framework provides the capability to map your application objects to your relational database schemas. For example, when you have an Orders table that is full of references to Customer objects and StoreItem objects, and you are working with a relational database schema, these entities are created using JOIN statements between various tables. However, if you are working with this construction from your C# or VB code, then you are creating an Order object that includes a property reference to a Customer object and a StoreItem object. Mapping these representations together for themselves has Conceptual Layer usually been the job of developers. In the past, an effort was made to address this issue of mapping with the introduction of the DataSet object. This object was an in-memory table representation that included with it multiple tables, with the table joins and constraints in place. However, with the pure object-oriented code that people really wanted to develop, the DataSet approach was often not recommended. When you represent your data within the database, you are representing it as a logical model through the database’s relational schema. However, coding your application is accomplished using a conceptual model. Having both logical and conceptual layers forced the creation of a mapping layer. The mapping layer allows you to transfer objects from the .NET classes that you are working with in your code to the relational database schemas that you are working with within the database, as represented in Figure 29-1.

Mapping Layer

Logical Layer Figure 29-1

This mapping layer is sometimes thought of as a data access layer. Microsoft has provided a number of data access technologies over the past few years, and many third-party companies have formed around the concept of making this mapping as simple as possible. Some of the third parties even came on board to provide mapping or data access transformation to specialized databases. Microsoft has moved forward to make the mapping of entities from the application code to the database schemas as simple as possible. The ADO.NET Entity Framework is in place so that you can write less code to get your applications out the door. Note that you are going to need both the objects in your application code and the entities that you build within the relational schema of the database for some time to come.

www.it-ebooks.info

Creating Your First Entity Data Model  ❘ 

1107

So in the end, the Entity Framework consists of the following: ➤➤

A model of your database that is represented in the code of your application

➤➤

A definition of the datastore that you are working with (for example, your data representation within a SQL Server database)

➤➤

A mapping between the two elements

The Conceptual and Logical Layers The Entity Data Model (EDM) is an abstract conceptual model of data as you want to represent it in your code. It is usually construed as .NET classes that you can manipulate as you would any other object within your code. You create this layer using the Conceptual Schema Definition Language (CSDL), which is an XML definition of your objects. What’s nice about Visual Studio 2010 is that you have a visual designer to create the EDM on your behalf and under your direction. Note that these XML files are not compiled with your application, as you will see later in this chapter. The logical layer is defined using the Store Schema Definition Language (SSDL), which details the relational schema that you are storing within your database. This includes a definition of the tables, their relations, and their constraints.

Mapping Between Layers The last piece of the Entity Data Model is the mapping layer. This layer maps the CSDL and the SSDL instances using the Mapping Specification Language (MSL). It is possible for the combination of these three layers to work together for a few reasons. One is that there is a common type system within the Entity Data Model that all three layers can share. This system enables you to specify types that the code of your application will understand and then these same types will be understood by the database that you are working with. The EDM also provides the ability to work with the concept of inheritance as well as complex objects and to make the appropriate mappings between the layers. The ADO.NET Entity Framework is the first offering from Microsoft for the .NET Framework that provides an implementation of the Entity Data Model and its constructs. In addition to the core pieces of the Entity Framework, Microsoft has also provided an O/R (object relational) designer in working with the creation of your entities. These items are demonstrated next.

Creating Your First Entity Data Model For an example of working with the ADO.NET Entity Framework, the first task is to work through all the steps that are required to read some data from a database and present it in your ASP.NET application. For this example, you must have Visual Studio 2008 SP1 or Visual Studio 2010. From this IDE, create a new ASP.NET Web application called AspnetEntityFx, a standard ASP.NET application. Next, you must get a database in place to work with. You can download the AdventureWorks sample database from www.codeplex.com/MSFTDBProdSamples/Release/ProjectReleases.aspx?ReleaseId=4004. You will find a file called AdventureWorksDB.msi on this page. To add this database, right-click on the App_Data folder from the Solution Explorer and select the option to add an existing item. After you have added the database file to your project, you are ready to create your Entity Data Model. Right-click on your project within the Solution Explorer from Visual Studio and select Add ➪ New Item. The Add New Item dialog appears. From this dialog, select the Data option for the language you are working with (found in the left pane of the dialog). The available items you can add from the Data option include an ADO.NET Entity Data Model item, as shown in Figure 29-2.

www.it-ebooks.info

1108  ❘  Chapter 29   ADO.NET Entity Framework

Figure 29-2

For this example, name your Entity Data Model EmployeeDM.edmx (refer to Figure 29-2).

Working Through the EDM Wizard Adding this file when clicking the Add button does actually not insert a file right away, but instead starts a wizard. You can create an Entity Data Model in two ways. The first option is to create an Entity Data Model based on a pre-existing database. The second option is to create the Entity Data Model from a blank slate. The .NET Framework 4 now really makes the second option something you can work with easily. When you choose this second option, you can create your Entity Data Model and then use a wizard to create the database representation of that model. The first screen in the wizard presents these options, as Figure 29-3 illustrates. For this example, select the Generate from Database option. After you click Next in the wizard, the next step is to establish an entity connection string to the database, as shown in Figure 29-4.

Figure 29-3

Figure 29-4

www.it-ebooks.info

Creating Your First Entity Data Model  ❘ 

1109

This wizard enables you to select the database that you want to work with from the first drop-down. The AdventureWorks_Data.mdf file appears as an option in this drop-down if you have added it to the project as previously instructed. When selecting this database option in the drop-down, you will be presented with the entity connection string. It is much like a normal connection string: metadata=res://*/EmployeeDM.csdl|res://*/EmployeeDM.ssdl|res://*/EmployeeDM.msl; provider=System.Data.SqlClient; provider connection string="Data Source=.\SQLEXPRESS; AttachDbFilename=|DataDirectory|\AdventureWorks_Data.mdf; Integrated Security=True;User Instance=True"

Notice that in addition to the normal connection string information that you might have, such as properties for the provider and the provider connection string, you also have the entity definition for what to use for the logical model and the conceptual model, as well as the mapping. The EmployeeDM.csdl file is for the conceptual model, EmployeeDM.ssdl is for the logical model, and EmployeeDM.msl is for the mapping required. The final option in this dialog is like most providers’ configurations that you have worked with in the past with ASP.NET; it allows you to save the connection string in the web.config file of your project. When you are done with this page of the wizard, click Next to choose the tables, views, and stored procedures that you are going to require for your Entity Data Model. For this example, expand the Table option in the treeview and select the Employee (Human Resources) table by selecting the check box next to the option (see Figure 29-5).

Figure 29-5

Notice that this part of the wizard enables you to define the namespace that you would use in accessing it from code. This example uses the default option of AdventureWorks_DataModel. At this point in the wizard, you will notice that the .NET Framework 4 provides some additional capabilities that the .NET Framework 3.5 SP1 didn’t provide. You now have the option to pluralize or singularize the generated object names as well as to use the foreign key columns in your model. To take advantage of these features, you use the option to generate a database from the model that appears when you right-click on the design surface of the model. This step is the last part of the wizard. When you are done selecting the options, click the Finish button. A designer surface for your Entity Data Model then appears.

Using the ADO.NET Entity Designer The O/R designer built into Visual Studio 2010 for working with the Entity Framework is powerful because it allows you to visually configure your conceptual layer and how it maps to the logical layer. If you worked through the wizard as defined earlier, then you will have a single Employee object represented on the page, as demonstrated in Figure 29-6. Figure 29-6

www.it-ebooks.info

1110  ❘  Chapter 29   ADO.NET Entity Framework

When you highlight the Employee object in the designer, some basic properties will appear in the Properties pane within Visual Studio, as illustrated in Figure 29-7. Here you can change the access modifier of the object and provide some basic documentation for the object. Visual Studio 2010 also provides you with some views to work with the Entity Framework. After you have created your .edmx file, a view is opened on your behalf — the Model Browser. Another important view is the Entity Data Model Mapping Details window. You can get to this view in a couple of ways. You can select View ➪ Other Windows ➪ Entity Data Model Mapping Details from the Visual Studio menu, or you can right-click on the Employee object in the designer and select Table Mapping from the provided menu. Figure 29-8 presents both the Entity Data Model Browser and the Entity Data Model Mapping Details windows.

Figure 29-7

Figure 29-8

Now that this simple object is in place and your .edmx file is ready to go, the next step is to build a small ASP.NET page that will use this construct.

Building an ASP.NET Page Using Your EDM Now that you have your Entity Data Model in place, this section shows you how to build a simple page that uses this model. The first step is to open your BasicGrid.aspx page and add a GridView control on the page. In the end, the code of your ASP.NET page will look like the code presented in Listing 29-1. Listing 29-1:  A basic ASP.NET page that uses your EDM <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="BasicGrid.aspx.cs" Inherits="AspnetEntityFx._Default" %>

www.it-ebooks.info

Creating Your First Entity Data Model  ❘ 

1111

My EDM
Filename BasicGrid.aspx

This is the C# version of the page. Notice that only a GridView control is on the page. You will use this control to populate the results that come out of the Entity Data Model that you created. To use the Entity Data Model, Listing 29-2 shows you the code-behind pages of the ASP.NET page that is presented in Listing 29-1. Listing 29-2:  The code-behind page for the ASP.NET page Partial Public Class BasicGrid Inherits System.Web.UI.Page

VB

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load Dim adventureWorks_DataEntities As New AdventureWorks_DataEntities() Dim query = From emp In adventureWorks_DataEntities.Employees Select emp GridView1.DataSource = query GridView1.DataBind() End Sub End Class

C#

using System; using System.Linq; namespace AspnetEntityFx { public partial class BasicGrid : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { AdventureWorks_DataEntities adventureWorks_DataEntities = new AdventureWorks_DataEntities(); var query = from emp in adventureWorks_DataEntities.Employees select emp;

continues

www.it-ebooks.info

1112  ❘  Chapter 29   ADO.NET Entity Framework

Listing 29-2  (continued) GridView1.DataSource = query; GridView1.DataBind(); } } } Filenames BasicGrid.aspx.vb and BasicGrid.aspx.cs

As you can see, there isn’t much code to this page. Running the page produces the results shown in Figure 29-9.

Figure 29-9

To work with your new Entity Data Model, an instance of the model is created: Dim adventureWorks_DataEntities As New AdventureWorks_DataEntities()

This instance manages your connection to the database and takes care of feeding in the queries that you might perform over the datastore. The next line is a LINQ statement: Dim query = _ From emp In adventureWorks_DataEntities.Employees _ Select emp

Here you are using an implicitly typed variable, query. The value assigned to the query object is the value of the Employees property, which is of type IQueryable. This LINQ query simply pulls the entire contents from the Employee table and places the result in the query object for use within your code. After the LINQ operation is complete, the query object is assigned and bound as a source to the GridView1 control. GridView1.DataSource = query GridView1.DataBind()

www.it-ebooks.info

Understanding Relationships  ❘ 

1113

This simple example was of a one-to-one mapping. The next example looks at how to work with a many-tomany relationship.

Understanding Relationships The previous example was a good showing of a one-to-one mapping — an Employee object that mapped to the Employee table. In this section you take look at one-to-one and one-to-many relationships and many-toone and many-to-many relationships.

One-to-One and One-to-Many Relationships A one-to-one relationship is a construct in which one table maps to one type within your Entity Data Model. This is also called a Table per Type model (TPT). To show this relationship in better detail, you will work with the Employee table from the previous example. If you look through the details of the AdventureWorld_Data.mdf database file, you will see that there are a lot of different tables. In terms of the Employee section of the database that this example uses, you can find the database relationships illustrated in Figure 29-10.

Figure 29-10

As you can see, in addition to the Employee table that you worked with earlier, you will find other tables such as the EmployeeDepartmentHistory, Department, EmployeeAddress, and EmployeePayHistory tables with a specific mapping. You can see from the figure that many of these tables are related through the EmployeeID foreign key.

www.it-ebooks.info

1114  ❘  Chapter 29   ADO.NET Entity Framework

In contrast to this mapping, you can pull up the EmployeeDM.edmx file that you created earlier in this chapter. From the design surface of this file, right-click and select Update Model from Database from the provided menu. An Update Wizard appears, as shown in Figure 29-11. Now add the references to the missing tables — EmployeeDepartmentHistory, Department, EmployeeAddress, and EmployeePayHistory. From this figure, you can see that you can see that a oneto-many relationship exists with the other types of employee data. With this construction in place, you will also find through IntelliSense that now one type (or object) maps to each of the specified tables, as illustrated in Figure 29-12.

Figure 29-11

Figure 29-12

With these tables in place, you can work with all the objects as demonstrated here. For this page, create a simple ASP.NET page that includes only a single BulletedList control. Then from the code-behind of the page, use the code from Listing 29-3.

www.it-ebooks.info

Understanding Relationships  ❘ 

1115

Listing 29-3:  Working with one-to-many mappings Protected Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs) Dim adventureWorks_DataEntities As New AdventureWorks_DataEntities()

VB

For Each employee In adventureWorks_DataEntities.Employees Dim li As New ListItem() li.Text = employee.EmployeeID & " " If (Not employee.EmployeePayHistories.IsLoaded) Then employee.EmployeePayHistories.Load() End If For Each pay In employee.EmployeePayHistories li.Text &= "Pay Rate: " & pay.Rate & " " Next pay BulletedList1.Items.Add(li) Next employee End Sub

C#

using System; using System.Web.UI.WebControls; namespace AspnetEntityFx { public partial class _Default : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { AdventureWorks_DataEntities adventureWorks_DataEntities = new AdventureWorks_DataEntities(); foreach (var employee in adventureWorks_DataEntities.Employees) { ListItem li = new ListItem(); li.Text = employee.EmployeeID + " "; if (!employee.EmployeePayHistories.IsLoaded) { employee.EmployeePayHistories.Load(); } foreach (var pay in employee.EmployeePayHistories) { li.Text += "Pay Rate: " + pay.Rate + " "; } BulletedList1.Items.Add(li); } } } } Filenames OneToMany.aspx.vb and OneToMany.aspx.cs

www.it-ebooks.info

1116  ❘  Chapter 29   ADO.NET Entity Framework

At first, the Employees objects are accessed and none of the other objects are actually loaded. This is why there is a check first to see if the EmployeePaymentHistory object is loaded using the IsLoaded() function. If the content is not loaded, then the Load() function is called. If you run this bit of code, you get the results shown in Figure 29-13.

Figure 29-13

Many-to-One and Many-to-Many Relationships In addition to the one-to-one and the one-to-many relationships, the Entity Framework supports manyto-one and many-to-many relationships. In these relationships, the Entity Framework will perform the appropriate table joins for you when you query the database. Create a new Entity Data Model (Sales.edmx file) that includes the Customer, SalesTerritory, SalesOrderHeader, and the SalesOrderDetail tables. You end up with a model that looks like Figure 29-14.

www.it-ebooks.info

Understanding Relationships  ❘ 

1117

Figure 29-14

You can see the relationships by looking at the line that connects the visual objects on the designer. An asterisk on one end of the line indicates many. The number 1 appearing on the other side of the connection, as the line that is between the SalesOrderHeader and the Customer objects, indicates a many-to-one relationship. You can also view details about the relationship in the Properties window of Visual Studio upon highlighting the relationship line itself in the designer, as presented through the two End property values in the view. Now look at a page that will perform some joins on your behalf across the tables. This operation is illustrated in Listing 29-4. For this example, just keep the simple page that contains only a GridView control and use the code-behind that is presented here. Listing 29-4:  Having the Entity Framework perform joins between tables Imports System Imports System.Linq

VB

Namespace AspnetEntityFx Partial Public Class _Default Inherits System.Web.UI.Page Protected Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs) Dim adventureWorks_DataEntities As

continues

www.it-ebooks.info

1118  ❘  Chapter 29   ADO.NET Entity Framework

Listing 29-4  (continued) New AdventureWorks_DataEntities1() Dim query = _ From o In adventureWorks_DataEntities.SalesOrderHeaders Where o.SalesOrderDetails.Any(Function(Quantity) Quantity.OrderQty > 5) Select New With {Key o.PurchaseOrderNumber, Key o.Customer.CustomerID, Key o.SalesPersonID} GridView1.DataSource = query GridView1.DataBind() End Sub End Class End Namespace

C#

using System; using System.Linq; namespace AspnetEntityFx { public partial class _Default : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { AdventureWorks_DataEntities1 adventureWorks_DataEntities = new AdventureWorks_DataEntities1(); var query = from o in adventureWorks_DataEntities.SalesOrderHeaders where o.SalesOrderDetails.Any(Quantity => Quantity.OrderQty > 5) select new {o.PurchaseOrderNumber, o.Customer.CustomerID, o.SalesPersonID}; GridView1.DataSource = query; GridView1.DataBind(); } } } Filenames ManyToMany.aspx.vb and ManyToMany.aspx.cs

This query pulls content and works from three different tables, and the Entity Framework does all the work of making the appropriate joins for you against the tables. In this case, you are working with all the items in the SalesOrderHeader table where the quantity of the order is more than five. From the items selected, the fields are pulled for the dynamic object from across a couple of tables. Finally, the result is again bound to a standard GridView control. Figure 29-15 presents the final result.

www.it-ebooks.info

Performing Inheritance Within the EDM  ❘ 

Figure 29-15

Performing Inheritance Within the EDM You can perform inheritance when constructing your Entity Data Model just as easily as you can when dealing with your objects within the CLR. Inheritance gives you the capability to work with and look for specialized objects that you determine. For an example of this feature, in this section you modify the Customer object so that you can build a query that will look for a specific type of customer by object reference rather than through value interpretation. Going back to the Sales.edmx file, you are going to modify the Customer object within this model. If you do not yet have this model, simply create a new one that contains only the Customer table. When you open the table definition of the Customer table within Visual Studio, you will see something like Figure 29-16.

Figure 29-16

www.it-ebooks.info

1119

1120  ❘  Chapter 29   ADO.NET Entity Framework

As you can see from this figure, the CustomerType property is of type nchar(1), which means it is a one-character string value. If you look at the documentation for this table online, you will realize that there are only two possible values for this table: I and S. A value of I means that the customer is an individual, whereas a value of S means that the customer is a store. For this example, you will build a specialized type that is a reference to an individual customer and not a store so that you can differentiate between the two types. When your Customer table is in place, right-click on the designer surface and choose Add ➪ Entity from the provided menu. The Add Entity dialog then appears. From this dialog, provide an entity name of IndividualCustomer and have it inherit from a base type of Customer. In the end, your Add Entity dialog should appear as shown in Figure 29-17. This step will add a visual representation to the mapping, as illustrated in Figure 29-18.

Figure 29-17

The next step is to further tie the two objects together and to provide some logic on their relationship. To accomplish this task you first delete the CustomerType scalar property from the Customer entity object, because you will not need it in the example. You then highlight the Customer object within the designer and view the details of the mapping of this object within the Mapping Details view within Visual Studio. From this view, add a condition of CustomerType being equal to S, as demonstrated in Figure 29-19.

Figure 29-18

Figure 29-19

www.it-ebooks.info

Performing Inheritance Within the EDM  ❘ 

1121

This setting really means that if the CustomerType has a value of S, then the object will be of type Customer. Now you set up the IndividualCustomer object. Looking at this object within the Mapping Detail view, you first must add the Customer table in the Mapping Details view. From there, create a condition where the CustomerType is equal to the character I, as illustrated in Figure 29-20. Now with these conditions all in place, you are really saying that if the CustomerType value has a value of S, then it is of type Customer in your Entity Data Model. However, if the CustomerType has a value of I, then the object type is of IndividualCustomer. You can work with this in your code, as illustrated in Listing 29-5.

Figure 29-20

Listing 29-5:  Using inheritance with your Entity Data Model Imports System Imports System.Linq

VB

Namespace AspnetEntityFx Partial Public Class Inheritance Inherits System.Web.UI.Page Protected Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs) Dim adventureWorks_DataEntities As New AdventureWorks_DataEntities3() Dim query = From c In adventureWorks_DataEntities.Customers Where TypeOf c Is IndividualCustomer Select c GridView1.DataSource = query GridView1.DataBind() End Sub End Class End Namespace

C#

using System; using System.Linq; namespace AspnetEntityFx { public partial class Inheritance : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { AdventureWorks_DataEntities3 adventureWorks_DataEntities = new AdventureWorks_DataEntities3(); var query = from c in adventureWorks_DataEntities.Customers where c is IndividualCustomer select c;

continues

www.it-ebooks.info

1122



chaPTer 29 Ado.net entity FrAmework

lisTing 29-5 (continued) GridView1.DataSource = query; GridView1.DataBind(); } } } Filenames Inheritance.aspx.vb and Inheritance.aspx.cs

You can now put in a specific where clause that looks for objects of type IndividualCustomer: Where TypeOf c Is IndividualCustomer

using sTored Procedures One of the fi rst things that you might think of when working with the Entity Framework is stored procedures. Is this new technology asking you to abandon the scores of stored procedures that you have built and have ready to use for your applications? Well, the answer is no, because you are able to work with stored procedures just as easily as everything that was previously shown in this chapter. There are issues, however, with using stored procedures rather than entities, as discussed earlier in this chapter. In the next example, you will see the limitations as you work with stored procedures from the Northwind sample database. Please refer to Chapter 8 on how to download the Northwind sample database.

For this example, open the previous ASP.NET Web Application project from Visual Studio and add the NORTHWND.MDF database fi le to your App_Data folder. This project should contain a single .aspx page that has only a GridView server control on the page. Again, your page will look something like Listing 29- 6, which shows the C# version of the page. lisTing 29 - 6: Your asP.neT page for working with stored procedures <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="StoredProc.aspx.cs" Inherits="AspnetEntityFx2.StoredProc" %> Working with Stored Procedures
Filename StoredProc.aspx

Now that the page is in place, the next step is to create your entity fi le, Northwind.edmx. Again, when creating this fi le, you will work through the wizard that Visual Studio provides, as discussed earlier in this chapter. When you work through the wizard, your primary concern is the screen that

www.it-ebooks.info

Using Stored Procedures  ❘ 

1123

asks you to choose your data objects. In the previous examples in this chapter, you worked directly with the tables that the database provided, but in this case, you are not going to work with the Tables part of the tree found in this wizard. Instead, you are going to expand the Stored Procedures element in the provided tree, and you will find a list of the available stored procedures that the Northwind database provides, as illustrated in Figure 29-21. As shown in the figure, you want to select the Ten Most Expensive Products stored procedure by selecting the check box next to the item. After selecting it, you can click the Finish button in the dialog. Now, instead of defining this entity visually on the EDM design surface, you will not find your stored procedures available in this view. Instead, you will find them in the Model Browser view within Visual Studio, as presented in Figure 29-22. Basically this view is only a reference to the stored procedure as it exists in the datastore. You are not provided an Entity Data Model to work with in this case.

Figure 29-21

Figure 29-22

The reason for this is that when you are working with stored procedures, you are working with entities that cannot be altered. This situation is what it is. After the stored procedure is in place, you can then programmatically work with it, as illustrated in Listing 29-7. Listing 29-7:  Working with stored procedures

VB

Imports Imports Imports Imports

System System.Data System.Data.Common System.Data.EntityClient

Namespace AspnetEntityFx Partial Public Class StoredProc Inherits System.Web.UI.Page Protected Sub Page_Load(ByVal sender As Object, _ ByVal e As EventArgs) Dim northwndEntities As New NorthwindEntities() Dim entityConnection As EntityConnection = _ CType(northwndEntities.Connection, EntityConnection) Dim storeConnection As DbConnection = _

continues

www.it-ebooks.info

1124  ❘  Chapter 29   ADO.NET Entity Framework

Listing 29-7  (continued) entityConnection.StoreConnection Dim command As DbCommand = storeConnection.CreateCommand() command.CommandText = "Ten Most Expensive Products" command.CommandType = CommandType.StoredProcedure Dim openConnection As Boolean = _ command.Connection.State = ConnectionState.Closed If openConnection Then command.Connection.Open() End If Dim result = command.ExecuteReader() GridView1.DataSource = result GridView1.DataBind() End Sub End Class End Namespace

C#

using using using using

System; System.Data; System.Data.Common; System.Data.EntityClient;

namespace AspnetEntityFx { public partial class StoredProc : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { NorthwindEntities northwndEntities = new NorthwindEntities(); EntityConnection entityConnection = (EntityConnection)northwndEntities.Connection; DbConnection storeConnection = entityConnection.StoreConnection; DbCommand command = storeConnection.CreateCommand(); command.CommandText = "Ten Most Expensive Products"; command.CommandType = CommandType.StoredProcedure; bool openConnection = command.Connection.State == ConnectionState.Closed; if (openConnection) { command.Connection.Open(); } var result = command.ExecuteReader(); GridView1.DataSource = result; GridView1.DataBind(); } } } Filenames StoredProc.aspx.vb and StoredProc.aspx.cs

www.it-ebooks.info

Using the EntityDataSource Control  ❘ 

1125

Obviously, there is a lot to this process, and there isn’t much time to be saved in working with the database directly otherwise. If you are going to be working only with stored procedures, and you are not going to be using the EDM concepts, then there is questionable value in why you are using Entity Data Models at all. With that said, you can incorporate stored procedures into your entities in some additional ways. For example, if you have a Product entity (from the Northwind database), you will notice, when looking at the entity in the Mapping Details section, that you are able to map insert, update, and delete procedures to the entity. You can get to this view by clicking on the Map Entity to Functions button in the leftmost panel in the Mapping Details dialog. Still, using stored procedures is a narrow entry point into your database and disallows composable queries, which the Entity Framework powers itself on.

Using the EntityDataSource Control A new control that was made available to ASP.NET 3.5 SP1 and is also present in ASP.NET 4 is the EntityDataSource control. This control makes working with your Entity Data Model from your ASP.NET applications easy. As with the LinqDataSource control, you are able to allow the control itself to do the LINQ work necessary to bind to any of your controls.

Creating the Base Page For an example of using the EntityDataSource control, first create an Entity Data Model that is the complete Northwind database. When working through the wizard in creating Northwind.edmx, add all the tables as shown in Figure 29-23. Next create an ASP.NET page to work with this model. Create the page in your ASP.NET Web Application project and use the code presented in Listing 29-8.

Figure 29-23

Listing 29-8:  Using the EntityDataSource server control <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="EntityDataSource.aspx.cs" Inherits="AspnetEntityFx. EntityDataSource" %> Using the EntityDataSource Control

Filename EntityDataSource.aspx

www.it-ebooks.info

1126  ❘  Chapter 29   ADO.NET Entity Framework

This page appears as a view in the designer portion of Visual Studio, as shown in Figure 29-24. With this page in place, however, the EntityDataSource controls on the page are not configured to work with the Entity Data Model that you created, and the GridView control is not bound to this data source control.

Configuring the Data Source Control Now you configure the data source control on your page so that it will work from your Entity Data Model Figure 29-24 that you created earlier. To do this, you can either code the EntityDataSource control directly in the code of your ASP.NET page or work through the data source configuration wizard. For this example, you will work through the wizard. Highlight the data source control on the design surface of your page, and you will find the Configure Data Source link from the available options. Click the link, and the first screen (see Figure 29-25) appears, asking you to configure the ObjectContext. For this example, you work with the NorthwindEntities object that you created earlier. Click the Next button to configure your data selection process. In this case, as presented in Figure 29-26, you can select the Customers table.

Figure 29-25

Figure 29-26

Notice that you can also very easily enable the insert, update, and delete functions by just selecting the appropriate check boxes in the wizard. Enabling these functions allows the EntityDataSource control to perform the appropriate LINQ operations over your Entity Data Model on your behalf. After you have accomplished this and clicked Finish, the code shown in Listing 29-9 appears (as a C# page). Note that you should also tie the GridView1 control to the EntityDataSource1 control by assigning the DataSourceID property to this control.

www.it-ebooks.info

Using the EntityDataSource Control  ❘ 

1127

Listing 29-9:  Pulling the Customer table using the EntityDataSource control <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="EntityDataSource.aspx.cs" Inherits="AspnetEntityFx. EntityDataSource" %> Using the EntityDataSource Control

continues

www.it-ebooks.info

1128  ❘  Chapter 29   ADO.NET Entity Framework

Listing 29-9  (continued) DefaultContainerName="NorthwindEntities" EnableDelete="True" EnableInsert="True" EnableUpdate="True" EntitySetName="Customers">
Filename EntityDataSource.aspx

Running this page produces the results shown in Figure 29-27 for viewing, editing, and selecting the items from your Entity Data Model.

Figure 29-27

Summary This chapter reviewed some of the core principles of the ADO.NET Entity Framework. These capabilities have been available to .NET developers ever since the release of the .NET Framework 3.5 SP1 and Visual Studio 2008 SP1. ASP.NET also includes a control that works with the Entity Framework: the EntityDataSource control. The Entity Framework provides the ability to work with a multitude of different data sources and allows you to build a mapping layer between your object-oriented API layer and the schema-driven database layer.

www.it-ebooks.info

30

asP.neT dynamic data whaT’s in This chaPTer? ➤

Building an ASP�NET Dynamic Data application



Using dynamic data routes



Handling your application’s display

ASP.NET offers a feature that enables you to dynamically create data-driven Web applications. ASP.NET Dynamic Data is more than purely a code generator, and although it can provide a base application, it is completely modifiable by you. This feature allows you to quickly and easily create data entry or applications that allow your end users to work with and view the backend database. The data-driven applications require a few components in order to work: the template system, a database from which to drive the application, and the ability to work from an object model such as LINQ to SQL or LINQ to Entities. This chapter illustrates how to build and customize an ASP.NET Dynamic Data application.

creaTing your Base aPPlicaTion wiTh visual sTudio 2010 ASP.NET Dynamic Data’s capabilities were fi rst introduced with the .NET Framework 3.5 SP1 and have been enhanced with the release of the .NET Framework 4 and Visual Studio 2010. Figure 30 -1 shows the two projects that are available in this area from Visual Studio 2010: Dynamic Data Linq to SQL Web Site and Dynamic Data Entities Web Site.

www.it-ebooks.info

1130  ❘  Chapter 30   ASP.NET Dynamic DatA

Figure 30-1

Which of the two application types you choose depends on how you plan to approach the object model. If you want to work with LINQ to SQL as your object model, then you will choose the Dynamic Data Linq to SQL Web Site option. If you want to work with LINQ to Entities (as discussed in the previous chapter), then you will choose the Dynamic Data Entities Web Site project. Note that if you were to reverse the choices, then you would experience various expected errors in working with your data-driven application. For an example of working through the creation of your first data-driven application, select the Dynamic Data Linq to SQL Web Site option, as this example will work with LINQ to SQL. Name your project MyNorthwind. Visual Studio 2010 will create a base application that is not connected to any database or object model from the start. It will be your job to make these connections. Before doing this, however, take a look at what Visual Studio has created for you.

Looking at the Core Files Created in the Default Application Before you even assign the pre-generated dynamic data application to a database, much of the core application is created for you through the Visual Studio process just mentioned. When you create the application, you will find a lot of new pieces to your ASP.NET application in the Visual Studio Solution Explorer. Figure 30-2 presents these extra items. The items that are generated for you and what is presented here in the Visual Studio Solution Explorer are generally referred to as scaffolding. Even though a lot of code seems to be generated for you, do not be alarmed thinking that you are locked into a specific data-driven application. What is generated is termed “scaffolding” because it is a framework that can be taken holistically or modified and extended for any purpose. This framework is the presentation and database layer support that you will need for the auto-generation of your application. You are in no way locked into a specific set of models, looks and feels, or even an approach that you are unable to modify to suit your specific needs.

www.it-ebooks.info

Figure 30-2

Creating Your Base Application with Visual Studio 2010  ❘ 

1131

Even though you will find a lot of pre-generated code in your Solution Explorer, you are not even required to use this code to work with ASP.NET Dynamic Data. In fact, you can even add ASP.NET Dynamic Data to a pre-existing application. Next, this chapter looks at the pre-generated application that enables you to work with your backend database.

The Dynamic Data Application One of the biggest additions to this application operation that is dramatically different from the standard ASP.NET application is a folder called DynamicData. This folder contains the pre-generated ASP.NET application that enables you to work with your database through a browser. The goal of this application is to enable you to work with your database through the entire CRUD process (Create, Read, Update, and Delete). Again, you can limit the amount of interactivity you provide from your application. To view how this application works against a database, you must dig further into the controls and pages that make up the application. Expanding the DynamicData folder, you find the following folders: ➤➤

Content

➤➤

CustomPages

➤➤

EntityTemplates

➤➤

FieldTemplates

➤➤

Filters

➤➤

PageTemplates

In addition to these folders, you will find a web.config file that is specific to this application. The Content folder in this part of the application includes a user control that is used in the page templates, as well as the underlying images that are used by the style sheet of the application. The CustomPages folder is a separate folder that allows you to put any custom pages that you might include in the data-driven Web application. When you create an application from scratch, you will not find any file in this folder. It is intentionally blank. The EntityTemplates folder is a new folder provided in ASP.NET 4 that makes getting the layout you want quite easy, thereby not requiring you to build a custom page. Initially, there is a Default.ascx (user control), and the edit and insert versions of this control are found in the folder. The FieldTemplates folder is interesting because it has some of the more granular aspects of the application. The entire application is designed to work off a database, but it really does not have any idea what type of database it is going to be working from. The FieldTemplates folder is a way that the application can present any of the underlying data types that are coming from the database. Figure 30-3 shows the data types as presented in the Visual Studio Solution Explorer. In this case, you will find data types for date/time values, integers, text, foreign keys, and more. They are represented as ASP.NET user controls, or .ascx files, which makes them easy for you to modify.

www.it-ebooks.info

Figure 30-3

1132  ❘  Chapter 30   ASP.NET Dynamic DatA

As an example, open the DateTime.ascx file, and you will get the bit of code presented in Listing 30-1. Listing 30-1:  The DateTime.ascx file <%@ Control Language="VB" CodeBehind="DateTime.ascx.vb" Inherits="DateTimeField" %>

VB

C#

" /> <%@ Control Language="C#" CodeBehind="DateTime.ascx.cs" Inherits="DateTimeField" %> " />

You can see that there isn’t much to the code shown in Listing 30-1. There is a simple Literal server control and nothing more. The Text property of the control gets populated with the variable FieldValueString from the code-behind file. Listing 30-2 presents this file. Listing 30-2:  The code-behind file for DateTime.ascx

VB

Imports System.ComponentModel.DataAnnotations Imports System.Web.DynamicData Imports System.Web Class DateTimeField Inherits FieldTemplateUserControl

Public Overrides ReadOnly Property DataControl As Control Get Return Literal1 End Get End Property

End Class

C#

using using using using using using using

System; System.Collections.Specialized; System.ComponentModel.DataAnnotations; System.Web.DynamicData; System.Web; System.Web.UI; System.Web.UI.WebControls;

public partial class DateTimeField : System.Web.DynamicData.FieldTemplateUserControl { public override Control DataControl { get { return Literal1; } } }

The data field templates inherit from System.Web.DynamicData.FieldTemplateUserControl, as you can see from Listing 30-2. This user control provides a read version of the data field for your application.

www.it-ebooks.info

Creating Your Base Application with Visual Studio 2010  ❘ 

1133

In addition to the data field .ascx files, there is an edit version of the control that is represented as a user control in addition to the read version of the data field. The edit versions have a filename ending in _Edit, as in the DateTime_Edit.ascx file. Listing 30-3 presents the contents of the DateTime_Edit.ascx control. Listing 30-3:  The DateTime_Edit.ascx file <%@ Control Language="VB" CodeBehind="DateTime_Edit.ascx.vb" Inherits="DateTime_EditField" %>

VB



C#

<%@ Control Language="C#" CodeBehind="DateTime_Edit.ascx.cs" Inherits="DateTime_EditField" %>

As you can see, the date will be populated into a TextBox server control. In addition to this representation, a series of validation server controls validate the input before applying an edit to the database. Listing 30-4 shows the code-behind of these pages. Listing 30-4:  The code-behind pages from the DateTime_Edit.ascx file

VB

Imports System.ComponentModel.DataAnnotations Imports System.Web.DynamicData Imports System.Web

continues

www.it-ebooks.info

1134  ❘  Chapter 30   ASP.NET Dynamic DatA

Listing 30-4  (continued) Class DateTime_EditField Inherits FieldTemplateUserControl Private Shared DefaultDateAttribute As DataTypeAttribute = New DataTypeAttribute(DataType.DateTime) Public Overrides ReadOnly Property DataControl As Control Get Return TextBox1 End Get End Property Protected Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs) TextBox1.ToolTip = Column.Description SetUpValidator(RequiredFieldValidator1) SetUpValidator(RegularExpressionValidator1) SetUpValidator(DynamicValidator1) SetUpCustomValidator(DateValidator) End Sub Private Sub SetUpCustomValidator(ByVal validator As CustomValidator) If Column.DataTypeAttribute IsNot Nothing Then Select Case (Column.DataTypeAttribute.DataType) Case DataType.Date, DataType.DateTime, DataType.Time validator.Enabled = True DateValidator.ErrorMessage = HttpUtility.HtmlEncode( Column.DataTypeAttribute.FormatErrorMessage(Column.DisplayName)) End Select ElseIf Column.ColumnType.Equals(GetType(DateTime)) Then validator.Enabled = True DateValidator.ErrorMessage = HttpUtility.HtmlEncode( DefaultDateAttribute.FormatErrorMessage(Column.DisplayName)) End If End Sub Protected Sub DateValidator_ServerValidate(ByVal source As Object, ByVal args As ServerValidateEventArgs) Dim dummyResult As DateTime args.IsValid = DateTime.TryParse(args.Value, dummyResult) End Sub Protected Overrides Sub ExtractValues(ByVal dictionary As IOrderedDictionary) dictionary(Column.Name) = ConvertEditedValue(TextBox1.Text) End Sub End Class

C#

<%@ Control Language="C#" CodeFile="DateTime_Edit.ascx.cs" Inherits="DateTime_EditField" %> using System; using System.Collections.Specialized; using System.ComponentModel.DataAnnotations;

www.it-ebooks.info

Creating Your Base Application with Visual Studio 2010  ❘ 

using using using using

System.Web.DynamicData; System.Web; System.Web.UI; System.Web.UI.WebControls;

public partial class DateTime_EditField : System.Web.DynamicData.FieldTemplateUserControl { private static DataTypeAttribute DefaultDateAttribute = new DataTypeAttribute(DataType.DateTime); protected void Page_Load(object sender, EventArgs e) { TextBox1.ToolTip = Column.Description; SetUpValidator(RequiredFieldValidator1); SetUpValidator(RegularExpressionValidator1); SetUpValidator(DynamicValidator1); SetUpCustomValidator(DateValidator); } private void SetUpCustomValidator(CustomValidator validator) { if (Column.DataTypeAttribute != null) { switch (Column.DataTypeAttribute.DataType) { case DataType.Date: case DataType.DateTime: case DataType.Time: validator.Enabled = true; DateValidator.ErrorMessage = HttpUtility.HtmlEncode( Column.DataTypeAttribute.FormatErrorMessage(Column.DisplayName)); break; } } else if (Column.ColumnType.Equals(typeof(DateTime))) { validator.Enabled = true; DateValidator.ErrorMessage = HttpUtility.HtmlEncode( DefaultDateAttribute.FormatErrorMessage(Column.DisplayName)); } } protected void DateValidator_ServerValidate(object source, ServerValidateEventArgs args) { DateTime dummyResult; args.IsValid = DateTime.TryParse(args.Value, out dummyResult); } protected override void ExtractValues(IOrderedDictionary dictionary) { dictionary[Column.Name] = ConvertEditedValue(TextBox1.Text); } public override Control DataControl { get { return TextBox1; } } }

You can see that every time you edit something in the database that includes a DateTime data type, it will also appear in a textbox HTML element for that purpose. In the code-behind of the file, you assign a tooltip to the textbox element, and assign four separate validation server controls to the control.

www.it-ebooks.info

1135

1136  ❘  Chapter 30   ASP.NET Dynamic DatA

Again, these user controls are the most granular example of how you can modify the output of the application. Another option, which you will review shortly, is to work with the page templates where these granular user controls are used. The Filters folder is used to create drop-down menus for Booleans (true/false values), foreign keys, and enumerations. These menus enable the end user to filter tables based upon keys within the database. This folder is new for ASP.NET 4. The PageTemplates folder contains the core pages that you use to bring the application together. Notice that pages exist for many of the core constructs that you will use in representing your tables in the application. The PageTemplates folder includes the following pages: ➤➤

Details.aspx

➤➤

Edit.aspx

➤➤

Insert.aspx

➤➤

List.aspx

➤➤

ListDetails.aspx

You use the List.aspx page for the tables in your connected database. You use the Details.aspx pages when you are examining a single row from the table, and you use the ListDetails.aspx page for examining master details views of the table and row relationships. You use the Edit.aspx and Insert.aspx pages, in turn, for the types of operations that they describe. Listing 30-5 shows a partial listing of the List.aspx page, specifically how it represents a table. Listing 30-5:  A partial code example from the List.aspx page <%@ Page Language="C#" MasterPageFile="~/Site.master" CodeBehind="List.aspx.cs" Inherits="List" %> <%@ Register src="~/DynamicData/Content/GridViewPager.ascx" tagname="GridViewPager" tagprefix="asp" %>

<%= table.DisplayName%>



Again, these controls and templates do not have any built-in thoughts of what the table is going to be like. These pages are built oblivious to the underlying tables to which this application will connect. This is evident through the use of the code <%= table.DisplayName%>. The next step is to actually tie this application to a database.

www.it-ebooks.info

Creating Your Base Application with Visual Studio 2010  ❘ 

1137

Incorporating the Database The next step required to build your first ASP.NET Dynamic Data application is to incorporate a database that you are able to work with. For this example, you need to include the Northwind database. The previous chapter explained how to get a copy of it or the AdventureWorks database, both of which are SQL Server Express Edition databases. Include the Northwind.mdf database file in the App_Data folder of your solution for this example. After the database is in place, the next step is to establish a defined entity data model layer that will work with the underlying database. Because this example is working with the LINQ to SQL approach rather than the LINQ to Entities approach, you must add a LINQ to SQL class to your application. To accomplish this task, right-click on your project within the Visual Studio Solution Explorer and select Add ➪ New Item from the provided menu. Then add a LINQ to SQL class by selecting that option from the middle section of the Add New Item dialog, as illustrated in Figure 30-4.

Figure 30-4

Name your .dbml file Northwind.dbml, as shown in Figure 30-4. The O/R (object relational) Designer for working with your LINQ to SQL classes then appears. It is a visual representation of the Northwind.dbml file (which currently does not contain any references to the database tables). The O/R designer has two parts. The first part is for data classes, which can be tables, classes, associations, and inheritances. Dragging such items onto the design surface gives you a visual representation of the object that you can work with. The second part (on the right side of the dialog) is for methods, which map to stored procedures within a database. Working with the Northwind database is quite simple — it’s really a matter of opening the database in the Visual Studio Server Explorer and dragging and dropping all the tables onto the design surface of the O/R Designer. Figure 30-5 shows the results.

www.it-ebooks.info

1138  ❘  Chapter 30   ASP.NET Dynamic DatA

Figure 30-5

With this action, a bunch of code is added to the designer files of the Northwind.dbml file on your behalf. These classes will now give you strongly typed access to the tables of the database, and more importantly, they will be able to tie themselves to the data-driven application that has been the focus of this chapter. You will find the code that Visual Studio generated by opening the Northwind.designer.vb or Northwind.designer.cs file within Visual Studio. Here you can find the programmatic representation of the database and the tables that you chose to add to the data model. Listing 30-6 shows a partial view of the generated code of this class. Listing 30-6:  A partial look at the generated LINQ to SQL class Option Strict On Option Explicit On

VB

Imports Imports Imports Imports Imports Imports Imports Imports Imports

System System.Collections.Generic System.ComponentModel System.Data System.Data.Linq System.Data.Linq.Mapping System.Linq System.Linq.Expressions System.Reflection

Partial Public Class NorthwindDataContext Inherits System.Data.Linq.DataContext ' Code removed for clarity End Class

C#

using System; using System.Collections.Generic; using System.ComponentModel;

www.it-ebooks.info

_

Creating Your Base Application with Visual Studio 2010  ❘ 

using using using using using using

1139

System.Data; System.Data.Linq; System.Data.Linq.Mapping; System.Linq; System.Linq.Expressions; System.Reflection;

[global::System.Data.Linq.Mapping.DatabaseAttribute(Name="NORTHWND")] public partial class NorthwindDataContext : System.Data.Linq.DataContext { // Code removed for clarity } Filenames Northwind.designer.vb and Northwind.designer.cs

This code creates an implementation of the DataContext object called NorthwindDataContext, which manages the transactions that occur with the database when you are working with LINQ to SQL. The DataContext, in addition to managing the transactions with the database, also contains the connection string, takes care of any logging, and manages the output of the data. There is a lot to this file. For example, you will find the tables represented as classes within the generated code. Listing 30-7 shows the class that represents the Customers table from the database. Listing 30-7:  The code representation of the Customers table

VB

Partial Public Class Customer Implements System.ComponentModel.INotifyPropertyChanging, System.ComponentModel.INotifyPropertyChanged

_

' Code removed for clarity End Class

C#

[global::System.Data.Linq.Mapping.TableAttribute(Name="dbo.Customers")] public partial class Customer : INotifyPropertyChanging, INotifyPropertyChanged { // Code removed for clarity } Filenames Northwind.designer.vb and Northwind.designer.cs

Now that the database is residing within the application and is ready to use, the next step is to wire the database to the overall application.

Registering the Data Model Within the Global.asax File The next step is to register the NorthwindDataContext object within the overall solution. The NorthwindDataContext is the data model that was built using the LINQ to SQL class that you just created. One of the files that was included when you built the ASP.NET Dynamic Data application from the start was a Global.asax file. This file is normally not included with your standard ASP.NET application by Visual Studio. When looking at this file, you will notice that there is a lot more to this file than the normal Global.asax file you might be used to (if you ever included one). Listing 30-8 shows the Global.asax file that is provided to you (minus some of the comments).

www.it-ebooks.info

1140  ❘  Chapter 30   ASP.NET Dynamic DatA

Listing 30-8:  The Global.asax file

VB

<%@ <%@ <%@ <%@

Application Language="VB" %> Import Namespace="System.ComponentModel.DataAnnotations" %> Import Namespace="System.Web.Routing" %> Import Namespace="System.Web.DynamicData" %>



C#

<%@ <%@ <%@ <%@

Application Language="C#" %> Import Namespace="System.ComponentModel.DataAnnotations" %> Import Namespace="System.Web.Routing" %> Import Namespace="System.Web.DynamicData" %>



www.it-ebooks.info

Creating Your Base Application with Visual Studio 2010  ❘ 

1141

If you are familiar with the standard Global.asax file, then you will recognize the standard Application_ Start() method that is part of the file. This method fires whenever the ASP.NET application starts up for the first time or is recycled in some manner. The Application_Start() method here calls a method named Register Routes(), which does the wiring between the generated application and the data model that you created earlier. You must register the data model that you created, the instance of the DataContext object. In this example, you must wire up the NorthwindDataContext object. Looking over the code from Listing 30-8, you can see that one of the lines of code is commented out. This is the RegisterContext() method call. Uncomment this line of code, and instead of having a reference to an object of type YourDataContextType, change it to NorthwindDataContext. In the end, your line of code will be as shown in Listing 30-9. Listing 30-9:  Wiring the NorthwindDataContext object

VB

model.RegisterContext(GetType(NorthwindDataContext), _ New ContextConfiguration() With {.ScaffoldAllTables = True})

C#

model.RegisterContext(typeof(NorthwindDataContext), new ContextConfiguration() { ScaffoldAllTables = true });

So, here the model is registered as a DataContext object of type NorthwindDataContext, and the ScaffoldAllTables property is set to True (the default is set to False), signifying that you want all the table representations in the model to be included in the generated application.

Styles and Layout With the data model wired to the application, you are actually ready to run the application and see what it produces. However, before this operation, you must be aware of two more pieces of this application. When you created the application, a master page and a stylesheet were also included. The master page, Site.master, is rather simple but does include the overall page framework that allows you to navigate the tables. There is also a simple stylesheet called Site.css that provides the overall style of the page.

Results of the Application As you run the application, notice that the first page allows you to see all the tables that you made a part of your data model, as illustrated in Figure 30-6.

Figure 30-6

www.it-ebooks.info

1142  ❘  Chapter 30   ASP.NET Dynamic DatA

As an application that reads the contents of your database, it works quite simply. Clicking on a table name (which is a hyperlink in the application) provides the contents of the table. Figure 30-7 shows this view.

Figure 30-7

The table view is nicely styled and includes the ability to edit, delete, or view the details of each row in the database. In cases where a one-to-many relationship exists, you can drill down deeper into it. Another interesting part of the page is the navigation through the table. Pagination appears through the table, as shown at the bottom of the table in Figure 30-7. In addition to being able to use the edit, delete, or detail view of the row of information in the table, you can insert new rows into the database by clicking on the Insert New Item link below the table. A view similar to that shown in Figure 30-8 appears.

Figure 30-8

www.it-ebooks.info

Creating Your Base Application with Visual Studio 2010  ❘ 

1143

Editing a row makes the same type of page appear, as shown in Figure 30-9. This page resulted from clicking the Edit link next to one of the rows in the Products table.

Figure 30-9

Another interesting aspect of this application is how it works with the one-to-many relationships of the elements in the data model. For example, clicking the Orders table link produces the view shown in Figure 30-10.

Figure 30-10

www.it-ebooks.info

1144  ❘  Chapter 30   ASP.NET Dynamic DatA

Here, in addition to the table and its contents, you are presented with a filtering capability via the various drop-down menus at the top of the page. In this case, you can filter the orders by customer, employee, or shipper. You can even use a combination of these elements to filter items. The other aspect to notice in Figure 30-10 about the contents of the table is that instead of a CustomerID, an EmployeeID, or a ShipperID, you see the names of these items, and the application is making the reference to the identifier for these items when drilling further into the views. The final aspect to understand about this application is that because it is an ASP.NET 4 application, it makes proper use of AJAX. For example, when filtering items in the table, notice that the page makes partial refreshes without refreshing the entire page.

Working with Dynamic Data Routes As you can see, the application created was quite easy to implement and took only a couple of steps to accomplish. An interesting aspect of this page is how easily configurable it is overall. In particular, you can configure how the URLs are generated and what pages the routing uses. Going back to the Global.asax page that you set up in the previous example, you will find that underneath the RegisterContext() call is a method call that sets up the page construct and URL that you will use for the application. Listing 30-10 presents what is established as the default. Listing 30-10:  Viewing the default routing URL

VB

routes.Add(New DynamicDataRoute("{table}/{action}.aspx") With { .Constraints = New RouteValueDictionary(New With {.Action = "List|Details|Edit|Insert"}), .Model = DefaultModel})

C#

routes.Add(new DynamicDataRoute("{table}/{action}.aspx") { Constraints = new RouteValueDictionary(new { action = "List|Details|Edit|Insert" }), Model = DefaultModel });

If you go back to the URL that was used in something like an Edit mode operation, you will find a URL like the following: http://localhost:2116/Orders/Edit.aspx?OrderID=10249

After the default application, you will find Orders/Edit.aspx?OrderID=10249. Here, Orders is the name of the table, Edit is the operation, and the OrderID is the filter on the order that is being worked on. Returning to Listing 30-10, you can see this model for the URL defined directly in the code through the use of the string: {table}/{action}.aspx

Now, if you want to change this model, you can! These are all dynamic URLs and not actual URLs as you know them from the past. For instance, there isn’t an Orders folder present in the application. It is simply there as a dynamic route. This is evident when you change the dynamic route path yourself; for example, change the routes.Add() call so that it now says the following (shown here in VB as a partial code sample): Routes.Add(New DynamicDataRoute("{action}/{table}.aspx") ...

Now, when you go to edit an item, you get the following construct: http://localhost:2116/Edit/Orders.aspx?OrderID=10249

From this view, you can see that the action and the table reference have been reversed. Looking deeper in the Global.asax file, you can see that two more pre-established models are at your disposal. Listing 30-11 presents both of them.

www.it-ebooks.info

Working with Dynamic Data Routes  ❘ 

1145

Listing 30-11:  The other two routing models

VB

'routes.Add(New DynamicDataRoute("{table}/ListDetails.aspx") With { _ ' .Action = PageAction.List, _ ' .ViewName = "ListDetails", _ ' .Model = DefaultModel}) 'routes.Add(New DynamicDataRoute("{table}/ListDetails.aspx") With { _ ' .Action = PageAction.Details, _ ' .ViewName = "ListDetails", _ ' .Model = DefaultModel})

C#

//routes.Add(new DynamicDataRoute("{table}/ListDetails.aspx") { // Action = PageAction.List, // ViewName = "ListDetails", // Model = DefaultModel //}); //routes.Add(new DynamicDataRoute("{table}/ListDetails.aspx") { // Action = PageAction.Details, // ViewName = "ListDetails", // Model = DefaultModel //});

The example previously presented in this chapter showed each of the operations (for inserting, editing, and so on) on its own separate page. Commenting this previous routes.Add() method out of the Global.asax file and uncommenting out the second and third options from this file changes the page that is output, and a master/details view now appears on the same page. Figure 30-11 shows one such view of the tables using this new routing.

Figure 30-11

Now when you select an item, the details of that item also appear on the same page. Also, you are now working on a single page, so when you click the Edit link in the table, you are not sent to another page, but instead, you can now edit the contents of the row directly in the table, as shown in Figure 30-12.

www.it-ebooks.info

1146  ❘  Chapter 30   ASP.NET Dynamic DatA

Figure 30-12

You need to pay attention to a couple of things when you make these routing changes. If you return to the original routing definition, you can then include the following: VB

routes.Add(New DynamicDataRoute("{table}/{action}.aspx") With { _ .Constraints = New RouteValueDictionary(New With {.Action = _ "List|Details|Edit|Insert"}), _ .Model = DefaultModel})

C#

routes.Add(new DynamicDataRoute("{table}/{action}.aspx") { Constraints = new RouteValueDictionary(new { action = "List|Details|Edit|Insert" }), Model = DefaultModel });

From this code, it is true that the route is declared using the table/action options, but just as important, notice that actions are also defined. This code example basically identifies this route for the List, Details, Edit, and Insert actions. You could have also pointed to other options for each of the action types. The second and third entries in the Global.asax file do just that: VB

'routes.Add(New DynamicDataRoute("{table}/ListDetails.aspx") With { _ ' .Action = PageAction.List, _ ' .ViewName = "ListDetails", _ ' .Model = DefaultModel})

C#

//routes.Add(new DynamicDataRoute("{table}/ListDetails.aspx") { // Action = PageAction.List, // ViewName = "ListDetails", // Model = DefaultModel //});

In this partial code example, you can see that this dynamic data route is defined only for the List operations and does not include the Edit, Insert, or Details operations. In addition, instead of going to List.aspx or Details.aspx, this operation will go to a specific page — ListDetails.aspx. The ListDetails.aspx

www.it-ebooks.info

Controlling Display Aspects  ❘ 

1147

page provides you the ability to perform everything from a single page, which is why you don’t need to define the Edit, Insert, and Details operations in this case. In addition to everything this chapter has presented thus far, another interesting task you can perform is table-specific routing. Suppose you want to use the ListDetails.aspx page for all the tables except the Customers table. For this example, assume that you want to return to the base List.aspx, Details.aspx, and Edit.aspx just when the end user is working with the contents of the Customers table. In this case, you need to establish your routing as illustrated in Listing 30-12. Listing 30-12:  Routing for specific tables

VB

routes.Add(New DynamicDataRoute(“Customers/{action}.aspx”) With { _ .Constraints = New RouteValueDictionary(New With {.Action = _ “List|Details|Edit|Insert”}), _ .Model = DefaultModel, _ .Table = “Customers”}) routes.Add(New DynamicDataRoute(“{table}/ListDetails.aspx”) With { _ .Action = PageAction.List, _ .ViewName = “ListDetails”, _ .Model = DefaultModel}) routes.Add(New DynamicDataRoute(“{table}/ListDetails.aspx”) With { _ .Action = PageAction.Details, _ .ViewName = “ListDetails”, _ .Model = DefaultModel})

C#

routes.Add(new DynamicDataRoute(“Customers/{action}.aspx”) { Constraints = new RouteValueDictionary(new { action = “List|Details|Edit|Insert” }), Model = DefaultModel, Table = “Customers” }); routes.Add(new DynamicDataRoute(“{table}/ListDetails.aspx”) { Action = PageAction.List, ViewName = “ListDetails”, Model = DefaultModel }); routes.Add(new DynamicDataRoute(“{table}/ListDetails.aspx”) { Action = PageAction.Details, ViewName = “ListDetails”, Model = DefaultModel });

In this case, a dynamic data route of Customers/{action}.aspx is provided and will be used when the Table reference is equal to the Customers table through the Table = “Customers” declaration in the code. With these three routing statements, the Customers table will use the List.aspx, Details.aspx, Edit.aspx, and Insert.aspx pages, whereas all the other tables will use the ListDetails.aspx page.

Controlling Display Aspects ASP.NET Dynamic Data has a wealth of features — more than a single chapter can possibly cover. One final aspect to note is how easy removing tables and columns from the overall application is.

www.it-ebooks.info

1148  ❘  Chapter 30   ASP.NET Dynamic DatA

As shown in this chapter, you provided a data model that ended up controlling which parts of the database the data-driven application would work with. In this way, you can create new data models or even use preexisting data models that might be present in your application. Even though something is defined within your data model, you might not want that item present in the application. For this reason, you can use a couple of attributes to control whether a table or a column is present and can be worked with by the end user of the application. For example, suppose you did not want the Employees table to appear in the data-driven application even though this table is present in the data model that you created. In this case, you should use the ScaffoldTable attribute on the class that defines the table and set it to False. For this method to work, open the Northwind .designer.vb or Northwind.designer.cs file and make the addition illustrated in Listing 30-13. Listing 30-13:  Removing the Employees table from view

VB

_ _ Partial Public Class Employee Implements System.ComponentModel.INotifyPropertyChanging, System.ComponentModel.INotifyPropertyChanged ' Code removed for clarity End Class

C#

[System.ComponentModel.DataAnnotations.ScaffoldTable(false)] [global::System.Data.Linq.Mapping.TableAttribute(Name="dbo.Employees")] public partial class Employee : INotifyPropertyChanging, INotifyPropertyChanged { // Code removed for clarity }

To use the ScaffoldTable attribute, reference the System.ComponentModel.DataAnnotations namespace. With this reference in place, running the page will now show a list of tables that excludes the Employees table (see Figure 30-13).

Figure 30-13

www.it-ebooks.info

Adding Dynamic Data to Existing Pages  ❘ 

1149

Similar to the ScaffoldTable attribute, the ScaffoldColumn attribute is at your disposal (along with many other attributes). When this attribute is applied to a column, that column will be removed from view in the data-driven application. For example, suppose that you want the Employees table to be viewable, but you do not want users to be able to see employees’ home telephone numbers. In this case, you can remove that column by using the ScaffoldColumn attribute to set the column’s visibility to False, as illustrated in Listing 30-14. Listing 30-14:  Setting the visibility of a column to False

VB

_ _ Public Property HomePhone() As String Get Return Me._HomePhone End Get Set(ByVal value As String) If (String.Equals(Me._HomePhone, value) = False) Then Me.OnHomePhoneChanging(value) Me.SendPropertyChanging() Me._HomePhone = value Me.SendPropertyChanged("HomePhone") Me.OnHomePhoneChanged() End If End Set End Property

C#

[System.ComponentModel.DataAnnotations.ScaffoldColumn(false)] [global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_HomePhone", DbType="NVarChar(24)")] public string HomePhone { get { return this._HomePhone; } set { if ((this._HomePhone != value)) { this.OnHomePhoneChanging(value); this.SendPropertyChanging(); this._HomePhone = value; this.SendPropertyChanged("HomePhone"); this.OnHomePhoneChanged(); } } }

With this code in place, users would see the table and all the columns of the table, except for the HomePhone column.

Adding Dynamic Data to Existing Pages When ASP.NET Dynamic Data was first introduced with the .NET Framework 3.5 SP1, it took a bit of setup in order to get dynamic aspects on your pages. With the release of the .NET Framework 4, you will find that it is a lot easier to add dynamic portions to your Web pages.

www.it-ebooks.info

1150  ❘  Chapter 30   ASP.NET Dynamic DatA

This is now possible by using the new DynamicDataManager server control. For an example of this in action, take a look at Listing 30-15: Listing 30-15:  Using the DynamicDataManager server control with an existing GridView control <%@ Page Language="C#" %> DynamicDataManager Example
Filename DynamicDataManager.aspx

Using the same Northwind data context from the previous examples, you will find a basic LinqDataSource control has been added to the page to work with the GridView1 server control. The LinqDataSource has also been assigned to work off the Customers table in this example. What you want is a grid that shows a list of the customers. The next step in building this example is to add a DynamicDataManager control to the page and to have it work with the GridView1 control as well. This is done by adding a section and making a control reference using the ControlID attribute of the DataControlReference control. Running this page will produce the following results as demonstrated here in Figure 30-14:

www.it-ebooks.info

Summary  ❘ 

1151

Figure 30-14

From this example, you can see that the hierarchical content associated with the customers is interpreted and displayed as links in the table. All of this is dynamic. Another control to be aware of is the DynamicControl server control. This is utilized when you have a templated control and want ASP.NET to make the smart decision on how to render the appropriate HTML for this control. For an example of this, redo the previous example from Listing 30-15 and change the GridView server control to a ListView server control. You will also need to change the reference in the DynamicDataManager control for this example to work. Dragging and dropping the ListView server control onto the design surface of Visual Studio, you will notice that it now has the ability to enable the control for Dynamic Data, as is shown when configuring the ListView control in Figure 30-15.

Figure 30-15

Checking the Enable Dynamic Data checkbox in this dialog will generate a lot of controls for all the different views provided by the template. With the code generated by Visual Studio, you will see a lot of use of the DynamicControl control. An example of one of the control instances generated (in an edit mode) is presented here:

With the combination of these new controls, you can now quickly and easily add Dynamic Data to any of your ASP.NET pages.

Summary This chapter explored the capabilities in ASP.NET for building dynamic data-driven applications quickly and easily. These capabilities, in conjunction with Visual Studio, enable you to build a reporting application that provides full CRUD capabilities in less than five minutes. At the same time, you can delve deep into the application and modify how the application presents the contents of your database.

www.it-ebooks.info

www.it-ebooks.info

31

Working with services whaT’s in This chaPTer? ➤

Building and consuming Web services



Understanding the Windows Communication Foundation



Working with WCF Data Services

When the .NET Framework 1.0 was fi rst introduced, much of the hype around its release was focused on XML Web services. In fact, Microsoft advertised that the main purpose of the newly released .NET Framework 1.0 was to enable developers to build and consume XML Web services with ease. Unfortunately, the new Web services model was slow to be accepted by the development community because it was so radically different from messaging frameworks that came before (even though it was outright better). At the time, decision makers in the development community regarded this new Web services model with a cautious eye. Since then, Microsoft has stopped trumpeting that .NET is all about Web services and, instead, has really expanded the power of .NET and its relation to applications built within the enterprise. Still, the members of the IT community continued to look long and hard at the Web services model (Microsoft is no longer alone in hyping this technology), examining how it could help them with their current issues and problems. This chapter looks at building XML Web services and how you can consume XML Web service interfaces and integrate them into your ASP.NET applications. It begins with the foundations of XML Web services in the .NET world by examining some of the underlying technologies such as SOAP, WSDL, and more. The middle part of this chapter focuses on the latest Microsoft messaging framework — Windows Communication Foundation, also known as WCF. Finally, the last part of this chapter shows you how to create RESTful-type services using WCF Data Services. This relatively new framework is something that you can use to quickly and easily expose your data.

communicaTion BeTween disParaTe sysTems It is a diverse world. In a major enterprise, very rarely do you fi nd that the entire organization and its data repositories reside on a single vendor’s platform. In most instances, organizations are made up of a patchwork of systems — some based on UNIX, some on Microsoft, and some on other systems. There probably will not be a day when everything resides on a single platform where all the data

www.it-ebooks.info

1154  ❘  Chapter 31   Working with Services

moves seamlessly from one server to another. For that reason, these various systems must be able to talk to one another. If disparate systems can communicate easily, moving unique datasets around the enterprise becomes a simple process — alleviating the need for replication systems and data stores. When XML (eXtensible Markup Language) was introduced, it became clear that the markup language would be the structure to bring the necessary integration into the enterprise. XML’s power comes from the fact that it can be used regardless of the platform, language, or data store of the system using it to expose DataSets. XML has its roots in the Standard Generalized Markup Language (SGML), which was created in 1986. Because SGML was so complex, something a bit simpler was needed — thus, the birth of XML. XML is considered ideal for data representation purposes because it enables developers to structure XML documents as they see fit. For this reason, it is also a bit chaotic. Sending self-structured XML documents between dissimilar systems does not make a lot of sense — you would have to custom build the exposure and consumption models for each communication pair. Vendors and the industry as a whole soon realized that XML needed a specific structure that put some rules in place to clarify communication. The rules defining XML structure make the communication between the disparate systems just that much easier. Tool vendors can now automate the communication process, as well as provide for the automation of the possible creation of all the components of applications using the communication protocol. The industry settled on using SOAP (Simple Object Access Protocol) to make the standard XML structure work. Previous attempts to solve the communication problem that arose included component technologies such as Distributed Component Object Model (DCOM), Remote Method Invocation (RMI), Common Object Request Broker Architecture (CORBA), and Internet Inter-ORB Protocol (IIOP). These first efforts failed because each of these technologies was either driven by a single vendor or (worse yet) very vendorspecific. Implementing them across the entire industry was, therefore, impossible. SOAP enables you to expose and consume complex data structures, which can include items such as DataSets, or just tables of data that have all their relations in place. SOAP is relatively simple and easy to understand. Like ASP.NET, XML Web services are also primarily engineered to work over HTTP. The DataSets you send or consume can flow over the same Internet wires (HTTP), thereby bypassing many firewalls (as they move through port 80). So what is actually going across the wire? ASP.NET Web services generally use SOAP over HTTP using the HTTP Post protocol. An example SOAP request (from the client to the Web service residing on a Web server) takes the structure shown in Listing 31-1. Listing 31-1:  A SOAP request POST /MyWebService/Service.asmx HTTP/1.1 Host: www.wrox.com Content-Type: text/xml; charset=utf-8 Content-Length: 19 SOAPAction: “http://tempuri.org/HelloWorld”

The request is sent to the Web service to invoke the HelloWorld WebMethod (WebMethods are discussed later in this chapter). Listing 31-2 shows the SOAP response from the Web service.

www.it-ebooks.info

Building a simple XMl Web service

❘ 1155

lisTing 31 -2: a soaP response HTTP/1.1 200 OK Content-Type: text/xml; charset=utf-8 Content-Length: 14 Hello World

In the examples from Listings 31-1 and 31-2, you can see that what is contained in this message is an XML fi le. In addition to the normal XML declaration of the node, you see a structure of XML that is the SOAP message. A SOAP message uses a root node of that contains the or the body of the SOAP message. Other elements that can be contained in the SOAP message include a SOAP header, , and a SOAP fault, . For more information about the structure of a SOAP message, be sure to check out the SOAP specifications. You can fi nd them at the W3C Web site, www.w3.org/tr/soap.

Building a simPle xml weB service Building an XML Web service means that you are interested in exposing some information or logic to another entity either within your organization, to a partner, or to your customers. In a more granular sense, building a Web service means that you, as a developer, simply make one or more methods from a class you create that is enabled for SOAP communication. You can use Visual Studio 2010 to build an XML Web service. The fi rst step is to actually create a new Web site by selecting File ➪ New ➪ Web Site from the IDE menu. The New Web Site dialog opens. Select ASP.NET Empty Web Site, as shown in Figure 31-1.

figure 31-1

www.it-ebooks.info

1156  ❘  Chapter 31   Working with Services

Once the project is created, right click to add a new file to the project. Select Web Service (WebService.asmx) from the list of options. This will create a single XML Web service named WebService.asmx. You will find it’s code-behind file, WebService.vb or WebService.cs, in the App_Code folder (See Figure 31-2). Check out the WebService.asmx file. All ASP.NET Web service files use the .asmx file extension instead of the .aspx extension used by typical ASP.NET pages.

The WebService Page Directive

Figure 31-2

Open the WebService.asmx file in Visual Studio, and you see that the file contains only the WebService page directive, as illustrated in Listing 31-3. Listing 31-3:  Contents of the Service.asmx file <%@ WebService Language="VB" CodeBehind="~/App_Code/WebService.vb" Class="WebService" %>

You use the @WebService directive instead of the @Page directive. The simple @WebService directive has only four possible attributes. The following list explains these attributes: ➤➤

Class: Required. It specifies the class used to define the methods and data types visible to the XML

➤➤

CodeBehind: Required only when you are working with an XML Web service file using the code-

Web service clients. behind model. It enables you to work with Web services in two separate and more manageable pieces instead of a single file. The CodeBehind attribute takes a string value that represents the physical location of the second piece of the Web service — the class file containing all the Web service logic. In ASP.NET, placing the code-behind files in the App_Code folder is best, starting with the default Web service created by Visual Studio when you initially opened the Web service project. ➤➤

Debug: Optional. It takes a setting of either True or False. If the Debug attribute is set to True, the XML Web service is compiled with debug symbols in place; setting the value to False ensures that the Web service is compiled without the debug symbols in place.

➤➤

Language: Required. It specifies the language that is used for the Web service.

Looking at the Base Web Service Class File Now look at the WebService.vb or WebService.cs file — the code-behind file for the XML Web. By default, a structure of code is already in place in the WebService.vb or WebService.cs file, as shown in Listing 31-4. Listing 31-4:  Default code structure provided by Visual Studio for your Web service

VB

Imports System.Web Imports System.Web.Services Imports System.Web.Services.Protocols ' To allow this Web Service to be called from script, using ASP.NET AJAX,uncomment ' the following line. ' _ _ _

www.it-ebooks.info

Building a Simple XML Web Service  ❘ 

_ Public Class Service Inherits System.Web.Services.WebService _ Public Function HelloWorld() As String Return "Hello World" End Function End Class

C#

using using using using using

System; System.Collections.Generic; System.Linq; System.Web; System.Web.Services;

[WebService(Namespace = "http://tempuri.org/")] [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)] // To allow this Web Service to be called from script, using ASP.NET AJAX, // uncomment the following line. // [System.Web.Script.Services.ScriptService] public class Service : System.Web.Services.WebService { public Service () { //Uncomment the following line if using designed components //InitializeComponent(); } [WebMethod] public string HelloWorld() { return "Hello World"; } }

Some minor changes to the structure have been made since the release of the .NET Framework 3.5. You will notice that the System.Linq namespace is now included in the C# solution. In addition, the other change in this version is the inclusion of the commented System.Web.Script.Services .ScriptService object to work with ASP.NET AJAX scripts. To make use of this attribute, you simply uncomment the item. Since the .NET 1.0/1.1 days, there also have been some big changes. One is that the System.Web .Services.Protocols namespace is included by default in the VB version. Therefore, in working with SOAP headers and other capabilities provided via this namespace, you do not need to worry about including it. The other addition is the attribute. It builds the XML Web service responses that conform to the WS-I Basic Profile 1.0 release (found at www.ws-i.org/Profiles/BasicProfile-1.02004-04-16.html). Besides these changes, very little has changed in this basic Hello World structure.

Exposing Custom Datasets as SOAP To build your own Web service example, delete the Service.asmx file and create a new file called Customers.asmx. This Web service will expose the Customers table from SQL Server. Then jump into the code shown in Listing 31-5.

www.it-ebooks.info

1157

1158  ❘  Chapter 31   Working with Services

Listing 31-5:  An XML Web service that exposes the Customers table from Northwind

VB

Imports Imports Imports Imports Imports

System.Web System.Web.Services System.Web.Services.Protocols System.Data System.Data.SqlClient

_ _ Public Class Customers Inherits System.Web.Services.WebService _ Public Function GetCustomers() As DataSet Dim conn As SqlConnection Dim myDataAdapter As SqlDataAdapter Dim myDataSet As DataSet Dim cmdString As String = "Select * From Customers" conn = New SqlConnection("Server=localhost;uid=sa;pwd=;database=Northwind") myDataAdapter = New SqlDataAdapter(cmdString, conn) myDataSet = New DataSet() myDataAdapter.Fill(myDataSet, "Customers") Return myDataSet End Function End Class

C#

using using using using using using

System; System.Web; System.Web.Services; System.Web.Services.Protocols; System.Data; System.Data.SqlClient;

[WebService(Namespace = "http://www.wrox.com/customers")] [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)] public class Customers : System.Web.Services.WebService { [WebMethod] public DataSet GetCustomers() { SqlConnection conn; SqlDataAdapter myDataAdapter; DataSet myDataSet; string cmdString = "Select * From Customers"; conn = new SqlConnection("Server=localhost;uid=sa;pwd=;database=Northwind"); myDataAdapter = new SqlDataAdapter(cmdString, conn); myDataSet = new DataSet(); myDataAdapter.Fill(myDataSet, "Customers"); return myDataSet; } }

www.it-ebooks.info

Building a Simple XML Web Service  ❘ 

1159

The WebService Attribute All Web services are encapsulated within a class. The class is defined as a Web service by the WebService attribute placed before the class declaration. Here is an example: _

The WebService attribute can take a few properties. By default, the WebService attribute is used in your Web service along with the Namespace property, which has an initial value of http://tempuri.org/. This is meant to be a temporary namespace, and you should replace it with a more meaningful and original name, such as the URL where you are hosting the XML Web service. In the example, the Namespace value was changed to www.wrox.com/customers. Remember that the value does not have to be an actual URL; it can be any string value you want. The idea is that it should be unique. Using a URL is common practice because a URL is always unique. Notice that the two languages define their properties within the WebService attribute differently. Visual Basic 2010 uses a colon and an equal sign to set the property: Namespace:="http://www.wrox.com/customers"

C# uses just an equal sign to assign the properties within the WebService attribute values: Namespace="http://www.wrox.com/customers"

Other possible WebService properties include Name and Description. Name enables you to change how the name of the Web service is presented to the developer via the ASP.NET test page (the test page is discussed a little later in the chapter). Description allows you to provide a textual description of the Web service. The description is also presented on the ASP.NET Web service test page. If your WebService attribute contains more than a single property, separate the properties using a comma. Here’s an example: _

The WebMethod Attribute In Listing 31-5, the class called Customers has only a single WebMethod. A WebService class can contain any number of WebMethods, or a mixture of standard methods along with methods that are enabled to be WebMethods via the use of the attribute preceding the method declaration. The only methods that are accessible across the HTTP wire are the ones to which you have applied the WebMethod attribute. As with the WebService attribute, WebMethod can also contain some properties, which are described in the following list: ➤➤

BufferResponse: When BufferResponse is set to True, the response from the XML Web service is held in memory and sent as a complete package. If it is set to False, the default setting, the response is sent to the client as it is constructed on the server.

➤➤

CacheDuration: Specifies the number of seconds that the response should be held in the system’s cache. The default setting is 0, which means that caching is disabled. Putting an XML Web service’s response in the cache increases the Web service’s performance.

➤➤

Description: Applies a text description to the WebMethod that appears on the .aspx test page of the XML Web service.

➤➤

EnableSession: Setting EnableSession to True enables session state for a particular WebMethod. The default setting is False.

www.it-ebooks.info

1160  ❘  Chapter 31   Working with Services

➤➤

MessageName: Applies a unique name to the WebMethod. This step is required if you are working with overloaded WebMethods (discussed later in the chapter).

➤➤

TransactionOption: Specifies the transactional support for the WebMethod. The default setting is Disabled. If the WebMethod is the root object that initiated the transaction, the Web service can participate in a transaction with another WebMethod that requires a transaction. Other possible values include NotSupported, Supported, Required, and RequiresNew.

The XML Web Service Interface The Customers Web service from Listing 31-5 has only a single WebMethod that returns a DataSet containing the complete Customers table from the SQL Server Northwind database. Running Customers.asmx in the browser pulls up the ASP.NET Web service test page. This visual interface to your Web service is really meant either for testing purposes or as a reference page for developers interested in consuming the Web services you expose. Figure 31-3 shows the page generated for the Customers Web service.

Figure 31-3

The interface shows the name of the Web service in the blue bar (the dark bar in this black-and-white image) at the top of the page. By default, the name of the class is used unless you changed the value through the Description property of the WebService attribute, as defined earlier. A bulleted list of links to the entire Web service’s WebMethods is displayed. This example has only one WebMethod: GetCustomers(). A link to the Web service’s Web Services Description Language (WSDL) document is also available (the link is titled “Service Description” in the figure). The WSDL file is the actual interface with the Customers Web service. The XML document (shown in Figure 31-4) is not really meant for human consumption; it is designed to work with tools such as Visual Studio, informing the tool what the Web service requires to be consumed. Each Web service requires a request that must have parameters of a specific type. When the request is made, the Web service response comes back with a specific set of data defined using specific data types. Everything you need for the request and a listing of exactly what you are getting back in a response (if you are the consumer) is described in the WSDL document.

www.it-ebooks.info

Building a Simple XML Web Service  ❘ 

Figure 31-4

Clicking the GetCustomers link gives you a new page, shown in Figure 31-5, that not only describes the WebMethod in more detail, but also allows you to test the WebMethod directly in the browser.

Figure 31-5

www.it-ebooks.info

1161

1162  ❘  Chapter 31   Working with Services

At the top of the page is the name of the XML Web service (Customers); below that is the name of this particular WebMethod (GetCustomers). The page shows you the structure of the SOAP messages that are required to consume the WebMethod, as well as the structure the SOAP message takes for the response. Below the SOAP examples is an example of consuming the XML Web service using HTTP Post (with name/ value pairs). Using this method of consumption instead of using SOAP is possible. You can test the WebMethod directly from the page. In the Test section, you find a form. If the WebMethod you are calling requires an input of some parameters to get a response, you see some text boxes included so you can provide the parameters before clicking the Invoke button. If the WebMethod you are calling does not require any parameters, you see only the Invoke button and nothing more. Clicking Invoke actually sends a SOAP request to the Web service, causing a new browser instance with the result to appear, as illustrated in Figure 31-6.

Figure 31-6

Now that everything is in place to expose the XML Web service, you can consume it in an ASP.NET application.

Consuming a Simple XML Web Service So far, you have seen only half of the XML Web service story. Exposing data and logic as SOAP to disparate systems across the enterprise or across the world is a simple task using .NET, and particularly ASP.NET. The other half of the story is the actual consumption of an XML Web service into an ASP.NET application. You are not limited to consuming XML Web services only into ASP.NET applications; but because this is an ASP.NET book, it focuses on that aspect of the consumption process. Consuming XML Web services into other types of applications is not that difficult and, in fact, is rather similar to how you would consume them using ASP.NET. Remember that the Web services you come across can be consumed in Windows

www.it-ebooks.info

Consuming a simple XMl Web service

❘ 1163

Forms, mobile applications, databases, and more. You can even consume XML Web services with other Web services, so you can have a single Web service made up of what is basically an aggregate of other Web services.

adding a web reference To consume the Customers Web service that you created earlier in this chapter, create a new ASP.NET Web site called CustomerConsumer. The fi rst step in consuming an XML Web service in an ASP.NET application is to make a reference to the remote object — the Web service. You do so by right- clicking on the root node of your project from within the Visual Studio Solution Explorer and selecting Add Web Reference. The Add Web Reference dialog box appears, shown in Figure 31-7.

figure 31-7

The Add Web Reference dialog box enables you to point to a particular .asmx fi le to make a reference to it. Understand that the Add Web Reference dialog box is really looking for WSDL fi les. Microsoft’s XML Web services automatically generate WSDL fi les based on the .asmx fi les themselves. To pull up the WSDL fi le in the browser, simply type in the URL of your Web service’s .asmx fi le and add a ?WSDL at the end of the string. For example, you might have the following construction (this is not an actual Web service, but simply an example): http://www.wrox.com/MyWebService/Customers.asmx?WSDL

Because the Add Web Reference dialog box automatically fi nds where the WSDL fi le is for any Microsoft-based XML Web service, you should simply type in the URL of the actual WSDL fi le for any non – Microsoft-based XML Web service. If you are using Microsoft’s Visual Studio and its built-in Web server instead of IIS, you will be required to also interject the port number the Web server is using into the URL. In this case, your URL would be structured similar to http://localhost:5444/MyWebService/Customers.asmx?WSDL.

www.it-ebooks.info

1164  ❘  Chapter 31   Working with Services

In the Add Web Reference dialog box, change the reference from the default name to something a little more meaningful. If you are working on a single machine, the Web reference might have the name of localhost; if you are actually working with a remote Web service, the name is the inverse of the URL, such as com.wrox.www. In either case, renaming it so that the name makes a little more sense and is easy to use within your application is best. In the example here, the Web reference is renamed WroxCustomers. Clicking the Add Reference button causes Visual Studio to make an actual reference to the Web service from the web.config file of your application (shown in Figure 31-8). You might find some additional files under the App_WebReferences folder — such as a copy of the Web service’s WSDL file. Your consuming application’s web.config file contains the reference to the Web service in its section. Listing 31-6 shows the addition.

Figure 31-8

Listing 31-6:  Changes to the web.config file after making a reference to the Web service

You can see that the WroxCustomers reference has been made along with the name of the Web service, providing a key value of WroxCustomers.Customers. The value attribute takes a value of the location of the Customers Web service, which is found within the Customers.asmx page.

Invoking the Web Service from the Client Application Now that a reference has been made to the XML Web service, you can use it in your ASP.NET application. Create a new Web Form in your project. With this page, you can consume the Customers table from the remote Northwind database directly into your application. The data is placed in a GridView control. On the design part of the page, place a Button and a GridView control so that your page looks something like the one shown in Figure 31-9.

Figure 31-9

www.it-ebooks.info

Consuming a Simple XML Web Service  ❘ 

1165

The idea is that, when the end user clicks the button contained on the form, the application sends a SOAP request to the Customers Web service and gets back a SOAP response containing the Customers table, which is then bound to the GridView control on the page. Listing 31-7 shows the code for this simple application. Listing 31-7:  Consuming the Customers Web service in an ASP.NET page <%@ Page Language="VB" %>

VB

Web Service Consumer Example




C#

<%@ Page Language="C#" %>

The end user is presented with a simple button. Clicking it causes the ASP.NET application to send a SOAP request to the remote XML Web service. The returned DataSet is bound to the GridView control, and the page is redrawn, as shown in Figure 31-10.

www.it-ebooks.info

1166  ❘  Chapter 31   Working with Services

Figure 31-10

The Customers Web service is invoked by the instantiation of the WroxCustomers.Customers proxy object: Dim ws As New WroxCustomers.Customers()

Then, you can use the ws object like any other object within your project. In the code example from Listing 31-7, the result of the ws.GetCustomers() method call is assigned to the DataSource property of the GridView control: GridView1.DataSource = ws.GetCustomers()

As you develop or consume more Web services within your applications, you will see more of their power and utility.

Overloading WebMethods In the object-oriented world of .NET, using method overloading in the code you develop is quite possible. A true object-oriented language has support for polymorphism, of which method overloading is a part. Method overloading enables you to have multiple methods that use the same name but have different signatures. With method overloading, one method can be called, but the call is routed to the appropriate method based on the full signature of the request. Listing 31-8 shows an example of standard method overloading. Listing 31-8:  Method overloading in .NET

VB

Public Function HelloWorld() As String Return "Hello" End Function Public Function HelloWorld(ByVal FirstName As String) As String Return "Hello " & FirstName End Function

www.it-ebooks.info

Overloading WebMethods  ❘ 

C#

1167

public string HelloWorld() { return "Hello"; } public string HelloWorld(string FirstName) { return "Hello " + FirstName; }

In this example, both methods have the same name, HelloWorld. So, which one is called when you invoke HelloWorld? Well, it depends on the signature you pass to the method. For example, you might provide the following: Label1.Text = HelloWorld()

This yields a result of just Hello. However, you might invoke the HelloWorld() method using the following signature: Label1.Text = HelloWorld("Bill Evjen")

Then you get back a result of Hello Bill Evjen. As you can see, method overloading is a great feature that your ASP.NET applications can effectively utilize — but how do you go about overloading WebMethods? If you have already tried to overload any of your WebMethods, you probably got the following error when you pulled up the Web service in the browser: Both System.String HelloWorld(System.String) and System.String HelloWorld() use the message name 'HelloWorld'. Use the MessageName property of the WebMethod custom attribute to specify unique message names for the methods.

As this error states, the extra step you have to take to overload WebMethods is to use the MessageName property. Listing 31-9 shows how. Listing 31-9:  WebMethod overloading in .NET

VB

_ Public Function HelloWorld() As String Return "Hello" End Function _ Public Function HelloWorld(ByVal FirstName As String) As String Return "Hello " & FirstName End Function

C#

[WebMethod(MessageName="HelloWorld")] public string HelloWorld() { return "Hello"; } [WebMethod(MessageName="HelloWorldWithFirstName")] public string HelloWorld(string FirstName) { return "Hello " + FirstName; }

In addition to adding the MessageName property of the WebMethod attribute, you must disable your Web service’s adherence to the WS-I Basic Profile specification — which it wouldn’t be doing if you performed WebMethod overloading with your Web services. You can disable the conformance to the WS-I Basic Profile specification in a couple of ways. One way is to add the attribute to your code, as illustrated in Listing 31-10.

www.it-ebooks.info

1168  ❘  Chapter 31   Working with Services

Listing 31-10:  Changing your Web service so it does not conform to the WS-I Basic

Profile spec VB

_ Public Class MyOverloadingExample ' Code here End Class

C#

[WebServiceBinding(ConformsTo = WsiProfiles.None)] public class WroxMath : System.Web.Services.WebService { // Code here }

The other option is to turn off the WS-I Basic Profile capability in the web.config file, as shown in Listing 31-11. Listing 31-11:  Turning off conformance using the web.config file

After you have enabled your Web service to overload WebMethods, you can see both WebMethods defined by their MessageName value properties when you pull up the Web service’s interface test page in the browser (see Figure 31-11).

Figure 31-11

Although you can see the names of the WebMethods are the same, the MessageName property shows that they are distinct methods. When the developer consuming the Web service makes a Web reference to your Web service, he will see only a single method name available (in this example, HelloWorld). It is shown via the Visual Studio 2010 IntelliSense in the application consuming these methods (see Figure 31-12).

www.it-ebooks.info

Caching Web Service Responses  ❘ 

1169

Figure 31-12

In the box that pops up to guide developers on the signature structure, you can see two options available — one is an empty signature, and the other requires a single string.

Caching Web Service Responses Caching is an important feature in almost every application that you build with .NET. Chapter 22 covers most of the caching capabilities available to you in ASP.NET, but a certain feature of Web services in .NET enables you to cache the SOAP response sent to any of the service’s consumers. First, by way of review, remember that caching is the capability to maintain an in-memory store where data, objects, and various items are stored for reuse. This feature increases the responsiveness of the applications you build and manage. Sometimes, returning cached results can greatly affect performance. XML Web services use an attribute to control caching of SOAP responses — the CacheDuration property. Listing 31-12 shows its use. Listing 31-12:  Utilizing the CacheDuration property

VB C#

_ Public Function GetServerTime() As String Return DateTime.Now.ToLongTimeString() End Function [WebMethod(CacheDuration=60)] public string GetServerTime() { return DateTime.Now.ToLongTimeString(); }

As you can see, CacheDuration is used within the WebMethod attribute much like the Description and Name properties. CacheDuration takes an Integer value that is equal to the number of seconds during which the SOAP response is cached. When the first request comes in, the SOAP response is cached by the server, and the consumer gets the same timestamp in the SOAP response for the next minute. After that minute is up, the stored cache is discarded, and a new response is generated and stored in the cache again for servicing all other requests for the next minute.

www.it-ebooks.info

1170  ❘  Chapter 31   Working with Services

Among the many benefits of caching your SOAP responses, you will find that the performance of your application is greatly improved when you have a response that is basically re-created again and again without any change.

Using SOAP Headers

Soap Envelope

One of the more common forms of extending the capabilities of SOAP messages is to add metadata of the request to the SOAP message itself. The metadata is usually added to a section of the SOAP envelope called the SOAP header. Figure 31-13 shows the structure of a SOAP message. The entire SOAP message is referred to as a SOAP envelope. Contained within the SOAP message is the SOAP body — a piece of the SOAP message that you have been working with in every example thus far. It is a required element of the SOAP message.

Soap Header Soap Body Figure 31-13

The one optional component of the SOAP message is the SOAP header. It is the part of the SOAP message in which you can place any metadata about the overall SOAP request instead of incorporating it in the signature of any of your WebMethods. Keeping metadata separate from the actual request is important. What kind of information should you include in a header? It could include many things. One of the more common items placed in the SOAP header is any authentication/authorization functionality required to consume your Web service or to get at specific pieces of logic or data. Placing usernames and passwords inside the SOAP headers of your messages is a good example of what you might include.

Building a Web Service with SOAP Headers You can build upon the sample HelloWorld Web service that is presented in the default .asmx page when it is first pulled up in Visual Studio (from Listing 31-4). Name the new .asmx file HelloSoapHeader.asmx. The initial step is to add a class that is an object representing what is to be placed in the SOAP header by the client, as shown in Listing 31-13. Listing 31-13:  A class representing the SOAP header Public Class HelloHeader Inherits System.Web.Services.Protocols.SoapHeader

VB

C#

Public Username As String Public Password As String End Class public class HelloHeader : System.Web.Services.Protocols.SoapHeader { public string Username; public string Password; }

The class, representing a SOAP header object, has to inherit from the SoapHeader class from System .Web.Services.Protocols.SoapHeader. The SoapHeader class serializes the payload of the element into XML for you. In the example in Listing 31-13, you can see that this SOAP header requires two elements — simply a username and a password, both of type String. The names you create in this class are those used for the subelements of the SOAP header construction, so naming them descriptively is important. Listing 31-14 shows the Web service class that creates an instance of the HelloHeader class.

www.it-ebooks.info

Using SOAP Headers  ❘ 

1171

Listing 31-14:  A Web service class that utilizes a SOAP header

VB

_ Public Class HelloSoapHeader Inherits System.Web.Services.WebService Public myHeader As HelloHeader Public Function HelloWorld() As String If (myHeader Is Nothing) Then Return "Hello World" Else Return "Hello " & myHeader.Username & ". " & _ "
Your password is: " & myHeader.Password End If End Function End Class

C#

[WebService(Namespace = "http://www.wrox.com/helloworld")] [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)] public class HelloSoapHeader : System.Web.Services.WebService { public HelloHeader myHeader; [WebMethod] [SoapHeader("myHeader")] public string HelloWorld() { if (myHeader == null) { return "Hello World"; } else { return "Hello " + myHeader.Username + ". " + "
Your password is: " + myHeader.Password; } } }

The Web service, HelloSoapHeader, has a single WebMethod — HelloWorld. Within the Web service class, but outside of the WebMethod itself, you create an instance of the SoapHeader class. You can do so with the following line of code: Public myHeader As HelloHeader

Now that you have an instance of the HelloHeader class that you created earlier called myHeader, you can use that instantiation in your WebMethod. Because Web services can contain any number of WebMethods, it is not a requirement that all WebMethods use an instantiated SOAP header. You specify whether a WebMethod will use a particular instantiation of a SOAP header class by placing the SoapHeader attribute before the WebMethod declaration. Public Function HelloWorld() As String ' Code here End Function

In this example, the SoapHeader attribute takes a string value of the name of the instantiated SoapHeader class — in this case, myHeader.

www.it-ebooks.info

1172  ❘  Chapter 31   Working with Services

From here, the WebMethod actually makes use of the myHeader object. If the myHeader object is not found (meaning that the client did not send in a SOAP header with his constructed SOAP message), a simple “Hello World” is returned. However, if values are provided in the SOAP header of the SOAP request, those values are used within the returned string value.

Consuming a Web Service Using SOAP Headers Building an ASP.NET application that makes a SOAP request to a Web service using SOAP headers is not really difficult. Just as with the Web services that do not include SOAP headers, you make a Web Reference to the remote Web service directly in Visual Studio. For the ASP.NET page, create a simple page with a single Label control. The output of the Web service is placed in the Label control. Listing 31-15 shows the code for the ASP.NET page. Listing 31-15:  An ASP.NET page working with an XML Web service using SOAP headers <%@ Page Language="VB" %>

VB

Working with SOAP headers


C#

<%@ Page Language="C#" %>

Two objects are instantiated. The first is the actual Web service, HelloSoapHeader. The second, which is instantiated as wsHeader, is the SoapHeader object. After both of these objects are instantiated and

www.it-ebooks.info

Using SOAP Headers  ❘ 

1173

before making the SOAP request in the application, you construct the SOAP header. This is as easy as assigning values to the Username and Password properties of the wsHeader object. After these properties are assigned, you associate the wsHeader object to the ws object through the use of the HelloHeaderValue property. After you have made the association between the constructed SOAP header object and the actual WebMethod object (ws), you can make a SOAP request just as you would normally do: Label1.Text = ws.HelloWorld()

Running the page produces the result in the browser shown in Figure 31-14.

Figure 31-14

What is more interesting, however, is that the SOAP request reveals that the SOAP header was indeed constructed into the overall SOAP message, as shown in Listing 31-16. Listing 31-16:  The SOAP request Bill Evjen Bubbles

This code returns the SOAP response shown in Listing 31-17. Listing 31-17:  The SOAP response Hello Bill Evjen. Your password is: Bubbles

www.it-ebooks.info

1174  ❘  Chapter 31   Working with Services

Requesting Web Services Using SOAP 1.2 Most Web services out there use SOAP version 1.1 for the construction of their messages. With that said, SOAP 1.2 became a W3C recommendation in June 2003 (see www.w3.org/TR/soap12-part1/). The nice thing about XML Web services in the .NET Framework platform is that they are capable of communicating in both the 1.1 and 1.2 versions of SOAP. In an ASP.NET application that is consuming a Web service, you can control whether the SOAP request is constructed as a SOAP 1.1 message or a 1.2 message. Listing 31-18 changes the previous example so that the request uses SOAP 1.2 instead of the default setting of SOAP 1.1. Listing 31-18:  An ASP.NET application making a SOAP request using SOAP 1.2 <%@ Page Language=”VB” %>

VB



C#

<%@ Page Language=”C#” %>

In this example, you first provide an instantiation of the Web service object and use the SoapVersion property. The property takes a value of System.Web.Services.Protocols.SoapProtocolVersion .Soap12 to work with SOAP 1.2 specifically. With this bit of code in place, the SOAP request takes the structure shown in Listing 31-19. Listing 31-19:  The SOAP request using SOAP 1.2

www.it-ebooks.info

Consuming Web Services Asynchronously  ❘ 

1175

Bill Evjen Bubbles


One difference between the two examples is the xmlns:soap namespace that is used. The difference actually resides in the HTTP header. When you compare the SOAP 1.1 and 1.2 messages, you see a difference in the Content-Type attribute. In addition, the SOAP 1.2 HTTP header does not use the soapaction attribute because this is now combined with the Content-Type attribute. You can turn off either SOAP 1.1 or 1.2 capabilities with the Web services that you build by making the proper settings in the web.config file, as shown in Listing 31-20. Listing 31-20:  Turning off SOAP 1.1 or 1.2 capabilities

Consuming Web Services Asynchronously All the Web services that you have been working with in this chapter have been done synchronously. This means that after a request is sent from the code of an ASP.NET application, the application comes to a complete standstill until a SOAP response is received. The process of invoking a WebMethod and getting back a result can take some time for certain requests. At times, you are not in control of the Web service from which you are requesting data and, therefore, you are not in control of the performance or response times of these services. For these reasons, you should consider consuming Web services asynchronously. An ASP.NET application that makes an asynchronous request can work on other programming tasks while the initial SOAP request is awaiting a response. When the ASP.NET application is done working on the additional items, it can return to get the result from the Web service. The great news is that to build an XML Web service that allows asynchronous communication, you don’t have to perform any additional actions. All .asmx Web services have the built-in capability for asynchronous communication with consumers. The Web service in Listing 31-21 is an example. Listing 31-21:  A slow Web service

VB

Imports System.Web Imports System.Web.Services Imports System.Web.Services.Protocols _ Public Class Async

continues

www.it-ebooks.info

1176  ❘  Chapter 31   Working with Services

Listing 31-21  (continued) Inherits System.Web.Services.WebService _ Public Function HelloWorld() As String System.Threading.Thread.Sleep(1000) Return "Hello World" End Function End Class

C#

using using using using

System; System.Web; System.Web.Services; System.Web.Services.Protocols;

[WebService(Namespace = "http://www.wrox.com/AsyncHelloWorld")] [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)] public class Async : System.Web.Services.WebService { [WebMethod] public string HelloWorld() { System.Threading.Thread.Sleep(1000); return "Hello World"; } }

This Web service returns a simple Hello World as a string, but before it does, the Web service makes a 1000-millisecond pause. This is done by putting the Web service thread to sleep using the Sleep method. Next, take a look at how an ASP.NET application can consume this slow Web service asynchronously, as shown in Listing 31-22. Listing 31-22:  An ASP.NET application consuming a Web service asynchronously <%@ Page Language="VB" %>

VB

Async consumption

www.it-ebooks.info

Consuming Web Services Asynchronously  ❘ 

1177



C#

<%@ Page Language="C#" %>

When you make the Web reference to the remote Web service in the consuming ASP.NET application, you not only see the HelloWorld WebMethod available to you in IntelliSense, but you also see a BeginHelloWorld() and an EndHelloWorld(). To work with the Web service asynchronously, you must utilize the BeginHelloWorld() and EndHelloWorld() methods. Use the BeginHelloWorld() method to send a SOAP request to the Web service, but instead of the ASP.NET application waiting idly for a response, it moves on to accomplish other tasks. In this case, it is not doing anything that important — just counting the amount of time it is taking in a loop. After the SOAP request is sent from the ASP.NET application, you can use the IAsyncResult object to check whether a SOAP response is waiting. You do this by using myIar.IsCompleted. If the asynchronous invocation is not complete, the ASP.NET application increases the value of x by one before making the same check again. The ASP.NET application continues to do this until the XML Web service is ready to return a response. The response is retrieved using the EndHelloWorld() method call. The results of running this application are similar to what is shown in Figure 31-15.

Figure 31-15

www.it-ebooks.info

1178  ❘  Chapter 31   Working with Services

Windows Communication Foundation Since the introduction of the .NET Framework 3.0, Microsoft has made available a new way to build Web services beyond the ASP.NET-based Web services presented in this chapter. Until the .NET Framework 3.0 came out, building components that were required to communicate a message from one point to another was not a simple task because Microsoft offered more than one technology that you could use for such an action. For instance, you could have used ASP.NET Web services (as just discussed), Web Service Enhancements 3.0 (WSE), MSMQ, Enterprise Services, .NET Remoting, and even the System.Messaging namespace. Each technology has its own pros and cons. ASP.NET Web Services (also known by some as ASMX Web Services) provided the capability to easily build interoperable Web services. The WSE enabled you to easily build services that took advantage of some of the WS-* message protocols. MSMQ enabled the queuing of messages, which made working with solutions that were only intermittently connected easy. Enterprise Services, provided as a successor to COM+, offered an easy means to build distributed applications. .NET Remoting was a fast way to move messages from one .NET application to another. Moreover, these are Microsoft options only. These options do not include all the ones available in other environments, such as the Java world. With so many options available to a Microsoft developer, deciding which path to take with the applications you are trying to build can be tough. With this in mind, Microsoft has created the Windows Communication Foundation (WCF). WCF is a relatively new framework for building service-oriented applications. Microsoft wanted to provide its developers with a framework to quickly get a proper service-oriented architecture up and running. Using the WCF, you will be able to take advantage of all the items that make distribution technologies powerful. WCF is the answer and the successor to all these other message distribution technologies.

The Larger Move to SOA Upon examining WCF, you will find that it is part of a larger movement that organizations are making toward the much-talked-about service-oriented architecture, or SOA. An SOA is a message-based service architecture that is vendor-agnostic. As a result, you have the capability to distribute messages across a system, and the messages are interoperable with other systems that would otherwise be considered incompatible with the provider system. Looking back, you can see the gradual progression to the service-oriented architecture model. In the 1980s, the revolution arrived with the concept of everything being an object. When object-oriented programming came on the scene, it was enthusiastically accepted as the proper means to represent entities within a programming model. The 1990s took that idea one step further, and the componentoriented model was born. This model enabled objects to be encapsulated in a tightly coupled manner. It was only recently that the industry turned to a service-oriented architecture because developers and architects needed to take components and have them distributed to other points in an organization, to their partners, or to their customers. This distribution system needed to have the means to transfer messages between machines that were generally incompatible with one another. In addition, the messages had to include the ability to express the metadata about how a system should handle a message. If you ask 10 people what an SOA is, you’ll probably get 11 different answers, but some common principles are considered to be foundations of a service-oriented architecture: ➤➤

Boundaries are explicit: Any data store, logic, or entity uses an interface to expose its data or ­capabilities. The interface provides the means to hide the behaviors within the service, and the

www.it-ebooks.info

Windows Communication Foundation  ❘ 

1179

interface front-end enables you to change this behavior as required without affecting downstream consumers. ➤➤

Services are autonomous: All the services are updated or versioned independently of one another. Thus, you do not upgrade a system in its entirety; instead, each component of these systems is an ­i ndividual entity within itself and can move forward without waiting for other components to p ­ rogress forward. Note that with this type of model, after you publish an interface, that interface must remain unchanged. Interface changes require new interfaces (versioned, of course).

➤➤

Services are based upon contracts, schemas, and policies: All services developed require a contract regarding what is required to consume items from the interface (usually done through a WSDL document). Along with a contract, schemas are required to define the items passed in as parameters or delivered through the service (using XSD schemas). Finally, policies define any capabilities or requirements of the service.

➤➤

Service compatibility that is based upon policy: The final principle enables services to define policies (decided at runtime) that are required to consume the service. These policies are usually expressed through WS-Policy.

If your own organization is considering establishing an SOA, the WCF is a framework that works on these principles and makes implementing it relatively simple. The next section looks at what the WCF offers. Then you can dive into building your first WCF service.

WCF Overview As stated, the Windows Communication Foundation is a means to build distributed applications in a Microsoft environment. Although the distributed application is built upon that environment, this does not mean that consumers are required to be Microsoft clients or to take any Microsoft component or technology to accomplish the task of consumption. On the other hand, building WCF services means you are also building services that abide by the principles set forth in the aforementioned SOA discussion and that these services are vendor-agnostic — thus, they can be consumed by almost anyone. You can build WCF services using Visual Studio 2010. Note that because this is a .NET Framework 3.0 or greater component, you are actually limited to the operating systems in which you can run a WCF service. Whereas the other Microsoft distribution technologies mentioned in this chapter do not have too many limitations on running on Microsoft operating systems, an application built with WCF can only run on Windows XP SP2, Windows Vista, Windows 7, or Windows Server 2008. If you are already familiar with WCF, it is interesting to note that some improvements have been made to WCF within the .NET Framework 4 release. A lot of focus was put on increasing the productivity of developers and providing quick options for common tasks such as creating syndicated services, as well as better debugging and serialization options. You will find that the performance for WCF has increased, especially when hosted in IIS7. Other new features include new support for working with the ADO.NET Entity Framework, improvements to the configuration editor, and more.

Building a WCF Service Building a WCF service is not hard to accomplish. The assumption here is that you have installed the .NET Framework 4 for the purpose of these examples. If you are using Visual Studio 2010, the view of the project from the New Web Site dialog box is as shown in Figure 31-16.

www.it-ebooks.info

1180  ❘  Chapter 31   Working with Services

Figure 31-16

When you build a WCF project in this manner, the idea is that you build a traditional class library that is compiled down to a DLL that can then be added to another project. The separation of code and project is a powerful division on larger projects. That said, you can, however, just as easily build a WCF service directly in your .NET project, whether that is a console application or a Windows Forms application. The approach taken for the examples in this chapter shows you how to build a WCF service that is hosted in a console application. Keep in mind that for the services you actually build and deploy, building them directly as a WCF Service Library project and using the created DLL in your projects or in IIS itself is usually better. Before we jump into building a WCF service, first consider what makes up a service built upon the WCF framework.

What Makes a WCF Service When looking at a WCF service, you should understand that it is made up of three parts: the service, one or more endpoints, and an environment in which to host the service. A service is a class that is written in one of the .NET-compliant languages. The class can contain one or more methods that are exposed through the WCF service. A service can have one or more endpoints. An endpoint is used to communicate through the service to the client. Endpoints themselves are also made up of three parts. These parts are usually defined by Microsoft as the ABC of WCF. Each letter of WCF means something in particular in the WCF model, including the following: ➤➤

“A” is for address.

➤➤

“B” is for binding.

➤➤

“C” is for contract.

www.it-ebooks.info

Windows Communication Foundation  ❘ 

1181

Basically, you can think of this as follows: “A” is the where, “B” is the how, and “C” is the what. Finally, a hosting environment is where the service is contained. This constitutes an application domain and process. All three of these elements (the service, the endpoints, and the hosting environment) are put together to create a WCF service offering, as depicted in Figure 31-17.

Endpoint

Service method

Endpoint Endpoint Endpoint

Service method

Endpoint Endpoint

WCF Service Application Domain Process Figure 31-17

The next step is to create a basic service using the WCF framework.

Creating Your First WCF Service To build your service, prior to hosting it, you must perform two main steps. First, you must create a service contract. Second, you must create a data contract. The service contract is really a class with the methods that you want to expose from the WCF service. The data contract is a class that specifies the structure you want to expose from the interface. After you have a service class in place, you can host it almost anywhere. When running this service from Visual Studio 2010, you will be able to use the same built-in hosting mechanisms that are used by any standard ASP.NET application. To build your first WCF application, choose File ➪ New ➪ Web Site from the Visual Studio 2010 menu and call the project WCFService1. The example this chapter will run through here demonstrates how to build the WCF service by building the interface, followed by the service itself.

Creating the Service Framework The first step is to create the services framework in the project. To do this, right-click on the project and select Add New Item from the provided menu. From the Add New Item dialog box, select WCF Service, and name the service Calculator.svc, as illustrated in Figure 31-18.

www.it-ebooks.info

1182  ❘  Chapter 31   Working with Services

Figure 31-18

This step creates a Calculator.svc file, a Calculator.cs file, and an ICalculator.cs file. The Calculator.svc file is a simple file that includes only the page directive, whereas the Calculator.cs does all the heavy lifting. The Calculator.cs file is an implementation of the ICalculator.cs interface.

Working with the Interface To create your service, you need a service contract. The service contract is the interface of the service. This consists of all the methods exposed, as well as the input and output parameters that are required to invoke the methods. To accomplish this task, turn to the ICalculator.vb or ICalculator.cs (depending on the language you are using). Listing 31-23 presents the interface you need to create. Listing 31-23:  Creating the interface Imports System Imports System.ServiceModel

VB

_ Public Interface ICalculator _ Function Add(ByVal a As Integer, ByVal b As Integer) As Integer _ Function Subtract(ByVal a As Integer, ByVal b As Integer) As Integer _ Function Multiply(ByVal a As Integer, ByVal b As Integer) As Integer _ Function Divide(ByVal a As Integer, ByVal b As Integer) As Integer End Interface

www.it-ebooks.info

Windows Communication Foundation  ❘ 

C#

1183

using System.ServiceModel; [ServiceContract] public interface ICalculator { [OperationContract] int Add(int a, int b); [OperationContract] int Subtract(int a, int b); [OperationContract] int Multiply(int a, int b); [OperationContract] int Divide(int a, int b); }

This is pretty much the normal interface definition you would expect, but with a couple of new attributes included. To gain access to these required attributes, you must make a reference to the System.ServiceModel namespace. This gives you access to the and attributes. You use the attribute to define the class or interface as the service class, and it needs to precede the opening declaration of the class or interface. In this case, the example in the preceding code is based upon an interface: _ Public Interface ICalculator ' Code removed for clarity End Interface

Within the interface, four methods are defined. Each method is going to be exposed through the WCF service as part of the service contract. For this reason, each method is required to have the attribute applied. _ Function Add(ByVal a As Integer, ByVal b As Integer) As Integer

Utilizing the Interface The next step is to create a class that implements the interface. Not only is the new class implementing the defined interface, but it is also implementing the service contract. For this example, add this class to the same Calculator.vb or .cs file. The following code, illustrated in Listing 31-24, shows the implementation of this interface. Listing 31-24:  Implementing the interface Public Class Calculator Implements ICalculator

VB

Public Function Add(ByVal a As Integer, ByVal b As Integer) As Integer Implements ICalculator.Add Return (a + b) End Function Public Function Subtract(ByVal a As Integer, ByVal b As Integer) As Integer

continues

www.it-ebooks.info

1184  ❘  Chapter 31   Working with Services

Listing 31-24  (continued) Implements ICalculator.Subtract Return (a - b) End Function Public Function Multiply(ByVal a As Integer, ByVal b As Integer) As Integer Implements ICalculator.Multiply Return (a * b) End Function Public Function Divide(ByVal a As Integer, ByVal b As Integer) As Integer Implements ICalculator.Divide Return (a / b) End Function End Class

C#

public class Calculator : ICalculator { public int Add(int a, int b) { return (a + b); } public int Subtract(int a, int b) { return (a - b); } public int Multiply(int a, int b) { return (a * b); } public int Divide(int a, int b) { return (a / b); } }

From these new additions, you can see that you don’t have to do anything different to the Calculator class. It is a simple class that implements the ICalculator interface and provides implementations of the Add(), Subtract(), Multiply(), and Divide() methods. With the interface and the class available, you now have your WCF service built and ready to go. The next step is to get the service hosted. Note that this is a simple service — it exposes only simple types, rather than a complex type. This enables you to build only a service contract and not have to deal with the construction of a data contract. You learn about the construction of data contracts later in this chapter.

Hosting the WCF Service in a Console Application The next step is to take the service just developed and host it in some type of application process. Many hosting options are available, including the following:

www.it-ebooks.info

Windows Communication Foundation  ❘ 

➤➤

Console applications

➤➤

Windows Forms applications

➤➤

Windows Presentation Foundation (WPF) applications

➤➤

Managed Windows Services

➤➤

Internet Information Services (IIS) 5.1

➤➤

Internet Information Services (IIS) 6.0

➤➤

Internet Information Services (IIS) 7.0 and the Windows Activation Service (WAS)

1185

As stated earlier, this example hosts the service in the developer Web server provided by Visual Studio 2010. You can activate hosting a couple of ways — either through the direct coding of the hosting behaviors or through declarative programming (usually done via the configuration file). Compiling and running this application produces the results illustrated in Figure 31-19.

Figure 31-19

You will notice that the resulting page is quite similar to how it appears when you build an ASP.NET Web service.

Reviewing the WSDL Document The page presented in Figure 31-19 is the information page about the service. In the image, notice the link to the WSDL file of the service. As with ASP.NET Web services, a WCF service can also auto-generate the WSDL file. Clicking the WSDL link shows the WSDL in the browser, as illustrated in Figure 31-20.

www.it-ebooks.info

1186  ❘  Chapter 31   Working with Services

Figure 31-20

With this WSDL file, you can consume the service it defines through an HTTP binding. Note the following element at the bottom of the document, as shown in Listing 31-25. Listing 31-25:  The part of the WSDL file showing the service’s endpoint http://localhost:51715/WCFService1/Calculator.svc Lipper-STL-LAP\Bill

This element in the XML document indicates that in order to consume the service, the end user must use SOAP 1.2 over HTTP. This is indicated through the use of the element in the document. The is a WS-Addressing endpoint definition. Using this simple WSDL document, you can now build a consumer that makes use of this interface.

Building the WCF Consumer Now that an HTTP service is out there, which you built using the WCF framework, the next step is to build a consumer application in ASP.NET that uses the simple Calculator service. The consumer sends its request via HTTP using SOAP. This section describes how to consume this service. Open Visual Studio

www.it-ebooks.info

Building the WCF Consumer  ❘ 

1187

2010 and create a new ASP.NET application. Although this example uses an ASP.NET application, you can make this consumer call through any other application type within .NET as well. Call the new ASP.NET application WCFConsumer. This application consumes the Calculator service, so it should be laid out with two text boxes and a button to initiate the service call. For this example, you will only use the Add() method of the service.

Adding a Service Reference After you have laid out your ASP.NET page, make a reference to the new WCF service. You do this in a manner quite similar to how you do it with XML Web service references. Right-click on the solution name from the Visual Studio Solution Explorer and select Add Service Reference from the dialog box that appears. This capability to add a service reference is included in Visual Studio 2010 — previously (prior to Visual Studio 2008), you had only the Add Reference and Add Web Reference options. After you have selected Add Service Reference, the Add Service Reference dialog box, shown in Figure 31-21, appears. The Add Service Reference dialog box asks Figure 31-21 you for two things: the Service URI or Address (basically a pointer to the WSDL file) and the name you want to give to the reference. The name you provide the reference is the name that will be used for the instantiated object that enables you to interact with the service. Referring to Figure 31-21, you can see that the name provided to the Service Address setting is what is used for the running service from earlier in this chapter. Click OK in the Add Service Reference dialog box. This adds to your project a Service Reference folder containing some proxy files, as shown in Figure 31-22. Indeed, the Service Reference folder is added and a series of files are contained within this folder. The other important addition to note is the System.ServiceModel reference, made for you in the References folder. This reference was not there before you made reference to the service through the Add Service Reference dialog.

Figure 31-22

Configuration File Changes Looking at the web.config file, you can see that Visual Studio has placed information about the service inside the document, as illustrated in Listing 31-26. Listing 31-26:  Additions made to the web.config file by Visual Studio

continues

www.it-ebooks.info

1188  ❘  Chapter 31   Working with Services

Listing 31-26  (continued)


The important part of this configuration document is the element. This element contains a child element called that defines the where and how of the service consumption process. The element provides the address of the service — http://localhost:51715/WCFService1/ Calculator.svc — and it specifies which binding of the available WCF bindings should be used. In this case, the wsHttpBinding is the required binding. Even though you are using an established binding from the WCF framework, from the client side, you can customize how this binding behaves. The settings that define the behavior of the binding are specified using the bindingConfiguration attribute of the element. In this case, the value provided to the bindingConfiguration attribute is WSHttpBinding_ICalculator, which is a reference to the element contained within the element. As demonstrated, Visual Studio 2010 makes the consumption of these services fairly trivial. The next step is to code the consumption of the service interface into the GUI that you created as one of the first steps earlier in this chapter.

Writing the Consumption Code The code to consume the interface is quite minimal. The end user places a number in each of the two text boxes provided and clicks the button to call the service to perform the designated operation on the provided numbers. Listing 31-27 is the code from the button click event.

www.it-ebooks.info

Building the WCF Consumer  ❘ 

1189

Listing 31-27:  The button click event to call the service Protected Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button1.Click

VB

Dim ws As New ServiceReference.CalculatorClient() Dim result As Integer result = ws.Add(TextBox1.Text, TextBox2.Text) Response.Write(result.ToString()) ws.Close() End Sub

C#

protected void Button1_Click(object sender, EventArgs e) { ServiceReference.CalculatorClient ws = new ServiceReference.CalculatorClient(); int result = ws.Add(int.Parse(TextBox1.Text), int.Parse(TextBox2.Text)); Response.Write(result); ws.Close(); }

This code is quite similar to what is done when working with Web references from the XML Web services world. First is an instantiation of the proxy class, as shown with the creation of the svc object: Dim ws As New ServiceReference.CalculatorClient()

Working with the ws object now, the IntelliSense options provide you with the appropriate Add(), Subtract(), Multiply(), and Divide() methods. As before, the requests and responses are sent over HTTP as SOAP 1.2. This concludes the short tutorial demonstrating how to build your own WCF service using the HTTP protocol and consume this service directly into an ASP.NET application.

Working with Data Contracts Thus far, when building the WCF services, the defined data contract has relied upon simple types or primitive datatypes. In the case of the earlier WCF service, a .NET type of Integer was exposed, which in turn was mapped to an XSD type of int. Although you may not be able to see the input and output types defined in the WSDL document provided via the WCF-generated one, they are there. These types are actually exposed through an imported .xsd document (Calculator.xsd and Calculator1.xsd). Listing 31-28 presents this bit of the WSDL document. Listing 31-28:  Imported types in the WSDL document

Typing in the XSD location of http://localhost:51715/WCFService1/Calculator.svc?xsd=xsd0 gives you the input and output parameters of the service. For instance, looking at the definition of the Add() method, you will see the following bit of code, as shown in Listing 31-29.

www.it-ebooks.info

1190  ❘  Chapter 31   Working with Services

Listing 31-29:  Defining the required types in the XSD

This bit of XML code indicates that two required input parameters (a and b) are of type int; in return, the consumer gets an element called , which contains a value of type int. As a builder of this WCF service, you did not have to build the data contract because this service used simple types. When using complex types, you have to create a data contract in addition to your service contract.

Building a Service with a Data Contract For an example of working with data contracts, create a new WCF service (another WCF service project) called WCF_WithDataContract. In this case, you still need an interface that defines your service contract, and then another class that implements that interface. In addition to these items, you also need another class that defines the data contract. As with the service contract, which makes use of the and the attributes, the data contract uses the and attributes. To gain access to these attributes, you must make a reference to the System.Runtime .Serialization namespace in your project and import this namespace into the file. The custom type in this case is a Customer type, as presented in Listing 31-30. Listing 31-30:  Building the Customer type

VB

Imports System Imports System.ServiceModel Imports System.Runtime.Serialization Public Class Customer Public FirstName As String Public LastName As String End Class Public Interface IHelloCustomer Function HelloFirstName(ByVal cust As Customer) As String Function HelloFullName(ByVal cust As Customer) As String

www.it-ebooks.info

Building the WCF Consumer  ❘ 

1191

End Interface

C#

using System.Runtime.Serialization; using System.ServiceModel; [DataContract] public class Customer { [DataMember] public string Firstname; [DataMember] public string Lastname; } [ServiceContract] public interface IHelloCustomer { [OperationContract] string HelloFirstName(Customer cust); [OperationContract] string HelloFullName(Customer cust); }

Here, you can see that the System.Runtime.Serialization namespace is also imported, and the first class in the file is the data contract of the service. This class, the Customer class, has two members: FirstName and LastName. Both of these properties are of type String. You specify a class as a data contract through the use of the attribute: Public Class Customer ' Code removed for clarity End Class

Now, any of the properties contained in the class are also part of the data contract through the use of the attribute: Public Class Customer Public FirstName As String Public LastName As String End Class

Finally, the Customer object is used in the interface, as well as the class that implements the IHelloCustomer interface, as shown in Listing 31-31. Listing 31-31:  Implementing the interface Public Class HelloCustomer Implements IHelloCustomer

VB

Public Function HelloFirstName(ByVal cust As Customer) As String Implements IHelloCustomer.HelloFirstName Return "Hello " & cust.FirstName End Function

continues

www.it-ebooks.info

1192  ❘  Chapter 31   Working with Services

Listing 31-31  (continued) Public Function HelloFullName(ByVal cust As Customer) As String Implements IHelloCustomer.HelloFullName Return "Hello " & cust.FirstName & " " & cust.LastName End Function End Class

C#

public class HelloCustomer: IHelloCustomer { public string HelloFirstName(Customer cust) { return "Hello " + cust.Firstname; } public string HelloFullName(Customer cust) { return "Hello " + cust.Firstname + " " + cust.Lastname; } }

Building the Consumer Now that the service is running and in place, the next step is to build the consumer. To begin, build a new ASP.NET application from Visual Studio 2010 and call the project HelloWorldConsumer. Again, right-click on the solution and select Add Service Reference from the options provided in the menu. From the Add Service Reference dialog box, add the location of the WSDL file for the service and click OK. This adds the changes to the references and the web.config file just as before, enabling you to consume the service. The code presented in Listing 31-32 shows what is required to consume the service if you are using a Button control to initiate the call. Listing 31-32:  Consuming a custom type through a WCF service Protected Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button1.Click

VB

Dim ws As New ServiceReference.HelloCustomerClient() Dim myCustomer As New ServiceReference.Customer() myCustomer.Firstname = "Bill" myCustomer.Lastname = "Evjen" Response.Write(ws.HelloFullName(myCustomer)) ws.Close() End Sub

C#

protected void Button1_Click(object sender, EventArgs e) { ServiceReference.HelloCustomerClient ws = new ServiceReference.HelloCustomerClient(); ServiceReference.Customer myCustomer = new ServiceReference.Customer(); myCustomer.Firstname = "Bill"; myCustomer.Lastname = "Evjen"; Response.Write(ws.HelloFullName(myCustomer)); ws.Close(); }

www.it-ebooks.info

Building the WCF Consumer  ❘ 

1193

As a consumer, after you make the reference, you will notice that the service reference provides both a HelloCustomerClient object and the Customer object, which was defined through the service’s data contract. Therefore, the preceding code block just instantiates both of these objects and builds the Customer object before it is passed into the HelloFullName() method provided by the service.

Looking at WSDL and the Schema for HelloCustomerService When you make a reference to the HelloCustomer service, you will find the following XSD imports in the WSDL:

http://localhost:51715/WCFService1/HelloCustomer.svc?xsd=xsd2 provides the details on your Customer object. Here is the code from this file:

This code is an XSD description of the Customer object. Making a reference to the WSDL includes the XSD description of the Customer object and gives you the ability to create local instances of this object. Using this model, you can easily build your services with your own defined types.

Defining Namespaces Note that the services built in the chapter have no defined namespaces. If you looked at the WSDL files that were produced, you would see that the namespace provided is http://tempuri.org. Obviously, you do not want to go live with this default namespace. Instead, you must define your own namespace. To accomplish this task, the interface’s attribute enables you to set the namespace, as shown here: Public Interface IHelloCustomer Function HelloFirstName(ByVal cust As Customer) As String

www.it-ebooks.info

1194  ❘  Chapter 31   Working with Services

Function HelloFullName(ByVal cust As Customer) As String End Interface

Here, the attribute uses the Namespace property to provide a namespace.

Using WCF Data Services Prior to the release of the .NET Framework 4, the release of the .NET Framework 3.5 SP1 provided tremendous focus on the concept of the Entity Data Model (EDM) and what these models bring to your application’s reusability. Again, an EDM is an abstract conceptual model of data as you want to represent it in your code. Chapter 29 of this book provides more information on EDMs and how to work with them.

WCF Data Services works to create a services layer to your back-end data source. Doing so yourself, especially if you are working with a full CRUD model, means a lot of work. WCF Data Services allow you to get a service layer that is URI-driven. Figure 31-23 shows the general architecture when you’re working with WCF Data Services.

Entity Data Model

WCF Data Services

Response

When you are working with ASP.NET, there is now a lot more focus on putting work down on the client as well as the server. In the past, much of the development focused on performing as much on the server as possible and delivering completed operations and UI back down to the client simply for viewing. With the release of “smart clients” and Ajax-enabled applications, much of the work is now being offloaded to the client. WCF Data Services makes exposing back-end capabilities over the wire to enable more work to be performed on the client even easier than before.

Data Source (SQL Server, Oracle, XML file)

Request

Another outstanding feature found in this release of the .NET Framework is the WCF Data Services framework. This feature enables you to easily create a cloud interface to your client applications that provides everything from simple read capabilities to a full CRUD model (create, read, update, and delete functions). This feature was previously referred to as ADO.NET Data Services.

Internet

Client Application

Figure 31-23 As you can see from this figure, the WCF Data Services layer is not the layer that interacts with the database. Instead, you are working with an EDM layer that is the mapping layer between the data store and the cloudbased interface. When working with your EDM, you are able to use LINQ to SQL and LINQ to Entities (something that you have been introduced to in the last few chapters of this book).

WCF Data Services allow you to quickly expose interactions with the application’s underlying data source as RESTful-based services. The current version of WCF Data Services allows you to work with the datastores using JSON or Atom-based XML. Again, JSON is what ASP.NET AJAX uses for doing out-of-bounds calls to get around doing a complete page refresh.

Creating Your First Service Figuring out how to build a complete services layer to your database for all create, read, update, and delete functions would take some serious time and thought. However, WCF Data Services makes this task much more feasible, as you will see as you work through this first example.

www.it-ebooks.info

Creating Your First Service  ❘ 

1195

To build a services layer, first create a standard ASP.NET Web Application called Web_ADONETDS in either Visual Basic or C#. Note, you need to use the .NET Framework 3.5 SP1 along with Visual Studio 2008 SP1 or the .NET Framework 4 with Visual Studio 2010 for this example to work. This, of course, creates a standard Web application that is not different from normal. Because WCF Data Services works from an underlying database, you will need to add one. For this example, add the NORTHWND.mdf database as you previously used in other chapters of this book. Place this database within the App_Data folder of your project.

Adding Your Entity Data Model After you have the database in place, you next create an Entity Data Model that WCF Data Services will work with. To do this, right-click on your project and select Add ➪ New Item from the list of options in the provided menu. The Add New Item dialog appears. As illustrated in Figure 31-24, add an ADO.NET Entity Data Model to your project.

Figure 31-24

As shown in the figure, name your ADO.NET Entity Data Model file Northwind.edmx. Chapter 29 of this book covers the ADO.NET Entity Framework and its capabilities of building your EDM using Visual Studio. When you create the Northwind.edmx file by clicking Add, the Entity Data Model Wizard appears, offering you the option of creating an empty EDM or creating one from a pre-existing database. For this example, choose the option to create one from the pre-existing (Northwind) database. Then click Next in the wizard. The next screen of the wizard will find your NORTHWND.mdf database and pre-define the connection settings and how they will be stored within your application. Figure 31-25 presents this second screen of the wizard.

www.it-ebooks.info

1196  ❘  Chapter 31   Working with Services

Figure 31-25

In the screenshot in Figure 31-25, notice that the connection string and the locations of the mapping details are going to be stored within the web.config file. You can also see on this screen that you are naming the instance of the model NORTHWNDEntities in the text box at the bottom of the wizard. This name is important to note because you will use it later in this example. The next screen allows you to select the tables, views, or stored procedures that will be part of the model. For this example, select the check box next to the Table item in the tree view to select all the tables in the database, as shown in Figure 31-26. After selecting the Table check box, click the Finish button to have Visual Studio create the EDM for you. You will notice that Visual Studio will create a visual representation of the model for you in the O/R Designer.

Figure 31-26

If you look at the Northwind.designer.vb or the Northwind.designer.cs file in your solution, you will see all the generated code for your EDM in place. This class file is named NORTHWNDEntities.

Creating the Service Now that the EDM is in place along with the database, the next step is to add your WCF Data Service. To accomplish this, right-click on your project within the Visual Studio Solution Explorer and select Add ➪ New Item from the provided menu. The Add New Item dialog appears again; select WCF Data Service from the middle section of the provided dialog (see Figure 31-27).

www.it-ebooks.info

Creating Your First Service  ❘ 

1197

Figure 31-27

As shown in the figure, name your WCF Data Service NorthwindDataService.svc. When done, click the Add button and Visual Studio will then generate a WCF service for you on your behalf. Listing 31-33 shows the code of the default service file. Listing 31-33:  The default .svc file for a WCF Data Service

VB

Imports System.Data.Services Imports System.Linq Imports System.ServiceModel.Web Public Class NorthwindDataService ' TODO: replace [[class name]] with your data class name Inherits DataService(Of [[class name]]) ' This method is called only once to initialize service-wide policies. Public Shared Sub InitializeService(ByVal config As IDataServiceConfiguration) ' TODO: set rules to indicate which entity sets and service operations are visible, updatable, etc. ' Examples: ' config.SetEntitySetAccessRule("MyEntityset", EntitySetRights.AllRead) ' config.SetServiceOperationAccessRule("MyServiceOperation", ServiceOperationRights.All) End Sub End Class

continues

www.it-ebooks.info

1198  ❘  Chapter 31   Working with Services

Listing 31-33  (continued)

C#

using using using using using

System; System.Collections.Generic; System.Data.Services; System.Linq; System.ServiceModel.Web;

namespace Web_ADONETDS { public class NorthwindDataService : DataService< /* TODO: put your data source class name here */ > { // This method is called only once to initialize service-wide policies. public static void InitializeService(IDataServiceConfiguration config) { // TODO: set rules to indicate which entity sets and service operations are visible, updatable, etc. // Examples: // config.SetEntitySetAccessRule("MyEntityset", EntitySetRights.AllRead); // config.SetServiceOperationAccessRule ("MyServiceOperation", ServiceOperationRights.All); } } }

The code generated here is the base framework for what you are going to expose through WCF Data Services. It will not work, however, until you accomplish the big TODO that the code specifies. The first step is to put in the name of the EDM instance using the code presented in Listing 31-34. Listing 31-34:  Changing the WCF Data Service to work with your EDM

VB

Public Class NorthwindDataService Inherits DataService(Of NORTHWNDEntities) ' Code removed for clarity End Class

C#

public class NorthwindDataService : DataService { // Code removed for clarity }

Now your application is at a state in which the database, the EDM, and the service to work with the EDM are in place. Upon compiling and pulling up the NorthwindDataService.svc file in the browser, you are presented with the following bit of XML: Default

www.it-ebooks.info

Creating Your First Service  ❘ 

1199

If you don’t see this XML, then you need to turn off the feed reading capabilities of your IE browser by selecting Tools ➪ Internet Options. From the provided dialog, select the Content tab and, within the Feeds section, click on the Select button. From there, you will be able to uncheck the “Turn on feed reading” check box. The result of the earlier XML is supposed to be a list of all the available sets that are present in the model, but by default, WCF Data Services locks everything down. To unlock these sets from the model, go back to the InitializeService() function and add the following bolded code, as illustrated in Listing 31-35. Listing 31-35:  Opening up the service for reading from the available tables

VB

Imports System.Data.Services Imports System.Linq Imports System.ServiceModel.Web Public Class NorthwindDataService Inherits DataService(Of NORTHWNDEntities) Public Shared Sub InitializeService(ByVal config _ As IDataServiceConfiguration) config.SetEntitySetAccessRule(“*”, EntitySetRights.AllRead) End Sub End Class

C#

using using using using using using

System; System.Collections.Generic; System.Data.Services; System.Linq; System.ServiceModel.Web; System.Web;

namespace Web_ADONETDS { public class NorthwindDataService : DataService { public static void InitializeService(IDataServiceConfiguration config) { config.SetEntitySetAccessRule(“*”, EntitySetRights.AllRead); } } }

In this case, every table is opened up to access. Everyone who accesses the tables has the ability to read from them but no writing or deleting abilities. All tables are specified through the use of the asterisk (*), and the right to the underlying data is set to read-only through the EntitySetRights enum being set to AllRead. Now, when you compile and run this service in the browser, you see the following bit of XML:

www.it-ebooks.info

1200  ❘  Chapter 31   Working with Services

Default Categories CustomerDemographics Customers Employees Order_Details Orders Products Region Shippers Suppliers Territories


The output of this XML is in the AtomPub format, one of the two available formats of XML that are made available from WCF Data Services. The other format is JSON, which is used in the AJAX world. The AtomPub example was retrieved due to the following header being in place: GET /NorthwindDataService.svc/ HTTP/1.1 Accept: application/atom+xml Accept-Language: en-us User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; .NET CLR 1.1.4322) UA-CPU: x86 Accept-Encoding: gzip, deflate Host: localhost.:4113 Connection: Keep-Alive GET /NorthwindDataService.svc/ HTTP/1.1 Accept: */* Accept-Language: en-US,fi-FI;q=0.7,ru-RU;q=0.3 User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; .NET CLR 1.1.4322) UA-CPU: x86 Accept-Encoding: gzip, deflate Host: localhost:50122 Connection: Keep-Alive

www.it-ebooks.info

Querying the Interface  ❘ 

1201

Cache-Control: no-cache Unsupported media type requested.

Here, application/atom+xml is used, and therefore, the AtomPub format is used. Changing the Accept header to read application/json will instead give you the following response: { "d" : { "EntitySets": [ "Categories", "CustomerDemographics", "Customers", "Employees", "Order_Details", "Orders", "Products", "Region", "Shippers", "Suppliers", "Territories" ] } }

The format of the preceding code is what you would need just for an ASP.NET AJAX page. Next, this chapter explores how to query the interface.

Querying the Interface You query the interface using three components: the URI, the action of the HTTP header, and the HTTP verb that you are using in the query. One of the more common ways to query the interface is to perform a read operation against the datastore. Looking back on an example header from earlier in this chapter, you can see something like the code shown in Listing 31-36. Listing 31-36:  An example request HTTP header GET /NorthwindDataService.svc/Products HTTP/1.1 Accept: application/atom+xml Accept-Language: en-us User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; .NET CLR 1.1.4322) UA-CPU: x86 Accept-Encoding: gzip, deflate Host: localhost.:4113 Connection: Keep-Alive

The result that is returned in this case is based on what is returned within the Accept HTTP header. Shown here is the Accept value of application/atom+xml. You could have also had the header of */* for a wildcard, and WCF Data Services would simply default to AtomPub in this case. Again, if you wanted the result returned as JSON, then you would have the Accept value of application/json. The method that you are calling is determined by the URI used. In the example in Listing 31-36, the URI is /NorthwindDataService.svc/Products, meaning that the Products set from the EDM is called and the results of the Products table from the Northwind database will be returned. Listing 31-36 is also a read statement because the HTTP verb that is used is a GET statement. Table 31-1 details the list of HTTP verbs and how they map to the data access type.

www.it-ebooks.info

1202  ❘  Chapter 31   Working with Services

Table 31-1 HTTP Verb

Data Access Type

POST

Create

GET

Read

PUT

Update

DELETE

Delete

The next section looks at different ways to query the underlying interface provided by WCF Data Services for reading content out of the database.

Reading a Table of Data Reading out an entire table of contents is based on the URI that is passed in. You read the contents by specifying the particular entity set that you are interested in. For example, type the following query into the browser’s address bar: http://localhost/NorthwindDataService.svc/Products

In this case, you are requesting the entire contents of the Products table by providing only the entity set Products in the URI. Figure 31-28 shows the result of this request.

Figure 31-28

The following syntax is another example of this type of request: http://localhost/NorthwindDataService.svc/Customers

In this case, you will receive a complete list of the customers from the Customers table in the database. If you look at the table-level information that is available from the URI call, you will find the following (as an example):

www.it-ebooks.info

Querying the Interface  ❘ 

1203

Customers http://localhost:4113/NorthwindDataService.svc/Customers 2009-11-30T19:36:24Z

Here, you see that you receive a title (the entity name), as well as the full URI as the element. You also get the timestamp of when the query was run in the element. Finally, you get a link referencing the item itself (other links also exist to the entity’s relationships beyond this one) if you are going to programmatically construct URIs.

Reading a Specific Item from the Table The previous example showed you how to pull an entire collection from the table or entity set using a URI such as: http://localhost/NorthwindDataService.svc/Products

If you look at one of the product items contained within the result collection, you will see something like what’s illustrated in Listing 31-37. Listing 31-37:  Reviewing one of the items from the Products query http://localhost:4113/NorthwindDataService.svc/Products(4) 2009-11-30T19:36:24Z false 4 Chef Anton's Cajun Seasoning 48 - 6 oz jars 0 22.0000 53 0

If you look at the value of this product, you will find the following: http://localhost:4113/NorthwindDataService.svc/Products(4)

www.it-ebooks.info

1204  ❘  Chapter 31   Working with Services

You can see that the reference to this particular product is Products(4), which means that you are interested in the fourth item in the Product collection. Typing that URI into the browser’s address bar will give you only that particular product. If you review the XML, you will find that the element contains all the data from the specific product that you are looking at. This is constructed as a properties collection. Although you see a list of properties for this customer, you are also able to get at individual properties themselves through URI declarations such as: http://localhost:4113/NorthwindDataService.svc/Products(4)/ProductName

Using a construct like the preceding will return only the ProductName property of this product. Here is the result: Chef Anton's Cajun Seasoning

It is important to realize that this number reference from Products(4) is not an index reference but the ID reference. For an example, type in the following URI (obviously, your port number will be different from mine, which is set to 4113): http://localhost:4113/NorthwindDataService.svc/Customers(1)

You might think that you would get the first customer in the list from the Customers table, but the number one (1) is not an identifier for any customer in the table. If you provided this statement, you wouldn’t get anything in return. If you look at the entire list of customers, you will find one of the customers as an example, as shown in Listing 31-38. Listing 31-38:  Reviewing one of the individual customers http://localhost:4113/NorthwindDataService.svc /Customers('ALFKI') 2009-11-30T20:20:28Z Obere Str. 57 Berlin Alfreds Futterkiste Maria Anders Sales Representative Germany ALFKI 030-0076545

www.it-ebooks.info

Querying the Interface  ❘ 

1205

030-0074321 12209


If you look at the element, you will see that the way to pull up a specific customer (such as this one) is not by specifying Customers(1), but instead by using Customers('ALFKI'). This means that you would use a URI such as the following: http://localhost:4113/NorthwindDataService.svc/Customers('ALFKI')

Using this construct will return the information on only the customer with the ID of ALFKI.

Working with Relationships Working with WCF Data Services obviously makes getting at your database and working with it through a RESTful interface in the cloud easy. One great advantage to working with the ADO.NET Entity Framework and WCF Data Services is that these items make working with object relationships just as easy. Going back to the Entity Data Model that was designed earlier in this chapter, you will notice that the objects have a built-in relationship that is visually shown in the O/R Designer. Figure 31-29 presents this view.

Figure 31-29

From this figure, you can see that the Customers object has a relationship with the Orders object (among others). You can also work down this object chain using WCF Data Services because it is represented in your EDM. To understand how these relationships are represented, take another look a customer’s output from WCF Data Services (shown in Listing 31-39).

www.it-ebooks.info

1206  ❘  Chapter 31   Working with Services

Listing 31-39:  Reviewing the customer relationships http://localhost:4113/NorthwindDataService.svc /Customers(‘ALFKI’) 2009-11-30T20:20:28Z Obere Str. 57 Berlin Alfreds Futterkiste Maria Anders Sales Representative Germany ALFKI 030-0076545 030-0074321 12209

The bolded XML code from this customer representation shows the two relationships that are in place for this customer. The first is a reference to the Orders relationship. You can see this statement through the rel attribute as well as the title attribute that is in place within this particular element. In addition to just a statement that there is this relationship in place, you will find a link to the relationship itself through the href attribute of the element. The stated reference is Customers('ALFKI')/Orders. This means that you can now type the following URI in the browser: http://localhost:4113/NorthwindDataService.svc/ Customers('ALFKI')/Orders

Typing this URI means that you are interested in drilling down to the customer with the ID of ALFKI and his orders in the system. In response, you get what is presented in Figure 31-30.

www.it-ebooks.info

Querying the Interface  ❘ 

1207

Figure 31-30

This figure shows all the orders from the ALFKI customer. Another interesting aspect is that you have all the nested relationships even at this level. You really will find all the available relationships at every level. Here are the relationships found for one of the orders of this customer:

The preceding code shows four relationships — one refers back to the customer of the particular order that you are looking at (Order ID 10643); the others are for the Employee objects associated with this order, the order details (through the Order_Details object), and the connection to the Shippers object.

Expanding on Associations So far, you have seen the entity associations mentioned through these elements and the ability to re-query with a new URI to dig into these associated details. You are also able to pull these associations out in the same query if you want.

www.it-ebooks.info

1208  ❘  Chapter 31   Working with Services

Getting these associations is possible through the use of the some querystring parameters that have been made available through WCF Data Services. For example, suppose you are making the following query: http://localhost:4113/NorthwindDataService.svc/Products(1)

This query gives you the output presented in Listing 31-40. Listing 31-40:  Calling for a single product http://localhost:4113/NorthwindDataService.svc/Products(1) 2009-11-30T23:22:55Z false 1 Chai 10 boxes x 20 bags 10 18.0000 39 0

The bolded portion of this code shows the link to the Categories entity set. In this mode, if you want the category of this product, then you must make an additional call to get this item using http://localhost:4113/NorthwindDataService.svc/Products(1)/Categories. Using this will give you a completely separate result. However, if you want to get this related set of data points for the product in a single call, you can use the expand keyword in your URI query: http://localhost:4113/NorthwindDataService.svc/ Products(1)?$expand=Categories

www.it-ebooks.info

Querying the Interface  ❘ 

1209

For this query to work, you can use one of the available keywords in your querystring, in this case expand. You simply use a familiar format of ?$expand= followed by the name of the associated entity sets. This query gives a result set similar to what is presented in Listing 31-41. Listing 31-41:  Adding additional entity sets http://localhost:4113/NorthwindDataService.svc/Products(1) 2009-12-03T01:04:12Z http://localhost:4113/NorthwindDataService.svc/ Categories(1) 2009-12-03T01:04:12Z 1 Beverages Soft drinks, coffees, teas, beers, and ales FRwvAAIAAAhAPAAADHrQX+
continues

www.it-ebooks.info

1210  ❘  Chapter 31   Working with Services

Listing 31-41  (continued) href=”Products(1)/Suppliers” /> false 1 Chai 10 boxes x 20 bags 10 18.0000 39 0


From this code, you can see that the element that was specific for the category is now expanded to include what was once a separate call inline. In addition to expanding a single associated entity set, you can expand multiple items: http://localhost:4113/NorthwindDataService.svc/ Products(1)?$expand=Categories,Suppliers

In this case, both the Categories and the Suppliers sections are expanded within the Product entity set call. You can also keep digging into the nested associated entity sets. For example, look at this: http://localhost:4113/NorthwindDataService.svc/ Products(1)?$expand=Suppliers/Products

Using this construct, you are looking at the first product from the product list and expanding the section for the internal suppliers of this product. Then, within the suppliers, the associated products that these suppliers sell are also included in the result set. As you can see, there is a lot of power in the ease with which you can drill down into nested relationships.

Ordering in Result Sets Another way to manipulate the result set that comes from the URI query is to get the results of the collection placed in a specific order as you define it. You can do so using the orderby keyword as a querystring command: http://localhost:4113/NorthwindDataService.svc/ Products?$orderby=ProductName

In this case, you get back a complete list of products that are in alphabetical order according to the entity’s ProductName field value. You are also able to assign an ascending or descending value to the order provided. By default, an ascending order is assigned. This means that the preceding query is the same as the following: http://localhost:4113/NorthwindDataService.svc/ Products?$orderby=ProductName asc

Notice that there is an actual space between the ProductName and asc items in the URI. If you want these in the reverse order, or descending order, then use the following construct: http://localhost:4113/NorthwindDataService.svc/ Products?$orderby=ProductName desc

You can also perform nested sorting using WCF Data Services: http://localhost:4113/NorthwindDataService.svc/ Products?$orderby=Discontinued asc, ProductName asc

www.it-ebooks.info

Querying the Interface  ❘ 

1211

Moving Around Result Sets As an end user of this interface, you can probably see that you might be working with fairly large result sets, depending on what is in the database. If you need only a portion of the database table and you are requesting all of your customers (which might be 100,000 or more), what then? In this case, WCF Data Services provides the capability to grab just smaller subsets of the content as pages and to navigate through the page that you need. This is done through the combination of two querystring commands: top and skip. They are also quite powerful in their own right. For instance, with the top command, you are able to pull the top n-number of items based on the sort that is being used. For example, consider this command: http://localhost:4113/NorthwindDataService.svc/Customers?$top=5

Here, the top five entities, in this case based on the CustomerID value, are pulled from the database and returned. If you want the top entities based on a different value, then you can use something like the following: http://localhost:4113/NorthwindDataService.svc/ Products?$orderby=UnitsOnOrder desc&$top=5

Using this example, the top five products, according to the number of units that are on order, are returned in the result set. You are able to use the skip command to basically skip the first set of defined items. For instance, you can do something similar to the following: http://localhost:4113/NorthwindDataService.svc/ Customers?$skip=5

In this case, the customers are returned minus the first five that would normally be returned. There is some question as to the value of this command, but its power is evident when used in combination with the top keyword. http://localhost:4113/NorthwindDataService.svc/ Customers?$skip=10&$top=10

In this case, you are skipping the first ten entities and then grabbing the following ten entities from that point onward. This means that you are really grabbing page two of sets that consist of ten items each. This would make performing a type of database-pagination process quite easy by using this process to use URI commands to get at the page of data you require.

Filtering Content The final command is one of the more powerful commands at your disposal. It is a type of screening that allows you to filter the content that you are truly interested in receiving from the database. This is all done through the use of the filter command: http://localhost:4113/NorthwindDataService.svc/ Customers?$filter=Country eq 'Germany'

Using the filter command preceded by a dollar sign ($), the value of this command is Country eq ‘Germany’. With this filtering command, you are requesting a list of customers located in the country of Germany. The database property in this case is Country, and within the URI it is important that you are specifying this property in its proper case. This means that if you used country instead of Country, you would not get any items in the result set. The Germany value is put in single quotes and the operator is specified as a set of characters, rather than a true equals sign (=). When using the filter command, you specify the equal operator with the eq string. Table 31-2 lists the logical operators that you are able to use.

www.it-ebooks.info

1212  ❘  Chapter 31   Working with Services

Table 31-2 Operator

Description

Ex ample

Eq

Equal

Country eq ‘Germany’

Ne

Not equal

Country ne ‘Germany’

Gt

Greater than

$filter = UnitsOnOrder gt 20

Ge

Greater than or equal

$filter = UnitsOnOrder ge 20

Lt

Less than

$filter = UnitsOnOrder lt 20

Le

Less than or equal

$filter = UnitsOnOrder le 20

And

Logical and

$filter = UnitsOnOrder gt 0 and UnitsInStock gt 0

Or

Logical or

$filter = UnitsOnOrder gt 0 or UnitsOnOrder lt 100

Not

Logical not

$filter = UnitsOnOrder gt 0 not ProductName eq ‘Chang’

In addition to logical operators, you can use a number of arithmetic operators, as shown in Table 31-3. Table 31-3 Operator

Description

Ex ample

Add

Add

$filter = UnitsOnOrder add 5 gt 20

Sub

Subtract

$filter = UnitsOnOrder sub 5 gt 20

Mul

Multiply

$filter = UnitsOnOrder mul 5 gt 20

Div

Divide

$filter = UnitsOnOrder div 5 gt 20

Mod

Modulo

$filter = UnitsOnOrder mod 100 gt 20

A long list of string, date, and math functions is also available: substringof

➤➤

endswith

➤➤

startswith

➤➤

length

➤➤

indexof

➤➤

insert

➤➤

remove

➤➤

replace

➤➤

substring

➤➤

tolower

➤➤

toupper

➤➤

trim

➤➤

concat

➤➤

day

➤➤

hour

➤➤

minute

➤➤

month

➤➤

second

➤➤

year

➤➤

round

➤➤

floor

➤➤

ceiling

➤➤

You would use these functions like this: http://localhost:4113/NorthwindDataService.svc/ Products?$filter = endswith(QuantityPerUnit, 'bottles')

In this case, you are retrieving all the products from the database that have a property QuantityPerUnit that ends with the string bottles.

www.it-ebooks.info

Consuming WCF Data Services in ASP.NET  ❘ 

1213

Consuming WCF Data Services in ASP.NET Now that you understand how to build a WCF Data Service, the next step is to consume this service in an ASP.NET application. Keep in mind that consuming a WCF Data Service in all types of .NET applications is obviously possible, but this chapter focuses on using this technology within ASP.NET itself. For an example of consuming a data service, create a standard ASP.NET application within Visual Studio. On the Default.aspx page, create a simple page that contains only a styled GridView server control. Listing 31-42 presents an example of this page. Listing 31-42:  A standard ASP.NET page with a GridView control <%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %> Working with Data Services


Now that you have a basic page ready, right-click on your project within the Visual Studio Solution Explorer and select Add Service Reference from the provided menu. The Add Service Reference dialog appears. Because a WCF Data Service is a standard .svc file, you can make reference to your Northwind .svc file within the text box provided and click the Go button. Figure 31-31 shows something similar to what appears. As you can see from the figure, all the underlying objects are represented in the dialog. Within the Namespace text box, you can name the reference Northwind and then click OK to accept this configuration. The next step is to work with this reference from the code-behind page. This work is shown in Listing 31-43.

Figure 31-31

www.it-ebooks.info

1214  ❘  Chapter 31   Working with Services

Listing 31-43:  Working with a WCF Data Service Imports Northwind

VB

Partial Class _Default Inherits System.Web.UI.Page Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load Dim svc As New NORTHWNDEntities(New _ Uri("http://localhost:4113/NorthwindDataService.svc").ToString()) GridView1.DataSource = svc.Customers GridView1.DataBind() End Sub End Class

C#

using System; using Northwind; public partial class _Default : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { NORTHWNDEntities svc = new NORTHWNDEntities(new Uri("http://localhost:4113/NorthwindDataService.svc").ToString()); GridView1.DataSource = svc.Customers; GridView1.DataBind(); } }

In the preceding code, it is simply a matter of making a reference to the Entity Data Model that the service exposes through the URI of the data service. This EDM instantiation will include a list of all the capabilities that interact with the underlying service. In the case of Listing 31-43, the entire Customers table is returned through the use of svc.Customers. This result set is then bound to the GridView control. Figure 31-32 shows the results.

Figure 31-32

www.it-ebooks.info

Summary  ❘ 

1215

In addition to a query as simple as the one in the preceding code, you can also start using some of the command logic presented earlier in this chapter when using LINQ within your code. Listing 31-44 shows a query against the Customer table in which you are interested only in seeing the customers that have a Country value of Germany. Listing 31-44:  Using LINQ Dim svc As New NORTHWNDEntities(New _ Uri("http://localhost:4113/NorthwindDataService.svc").ToString())

VB

Dim query = From c In svc.Customers Where c.Country.Contains("Germany") Select c GridView1.DataSource = query GridView1.DataBind()

C#

NORTHWNDEntities svc = new NORTHWNDEntities(new Uri("http://localhost:4113/NorthwindDataService.svc").ToString()); var query = from c in svc.Customers where c.Country.Contains("Germany") select c; GridView1.DataSource = query; GridView1.DataBind();

In this code, a LINQ query is performed and this object is then bound as the DataSource value of the GridView control. This will produce another list of items in the grid.

Summary This chapter was a whirlwind tour of XML Web services in the .NET platform. It is definitely a topic that merits an entire book of its own. The chapter showed you the power of exposing your data and logic as SOAP and also how to consume these SOAP messages directly in the ASP.NET applications you build. In addition to pointing out the power you have for building and consuming basic Web services, the chapter spent some time helping you understand caching, performance, the use of SOAP headers, and more. A lot of power is built into this model; every day, the Web services model is starting to make stronger inroads into various enterprise organizations. It is becoming more likely that to get at some data or logic you need for your application, you will employ the tactics presented in this chapter. While not exhaustive, this chapter broadly outlined the basics of the framework. As you start to dig deeper in the technology, you will find capabilities that are strong and extensible. WCF Data Services is a powerful and new way to expose out your database content. This chapter examined working with commands against the interface to filter out specific content. A number of commands can be used as part of the URI to get at the specific result sets you are interested in. However, using code, and more specifically LINQ, is also just as possible to get at the results from the interface that you are looking for when working with your ASP.NET pages. If you want to keep these queries hidden from end users and encapsulated within your code, then this is the best approach. Remember that querystrings also have a limit to the number of characters that they can hold, so sometimes working in the code using LINQ might be your only option.

www.it-ebooks.info

www.it-ebooks.info

32

Building Global applications whaT’s in This chaPTer? ➤

Globalizing your applications



Defining culture, both server-side and client-side



Working with local and global resources

Developers usually build Web applications in their native language, and then, as the audience for the application expands, they realize the need to globalize the application. Of course, the ideal is to build the Web application to handle an international audience right from the start — but, in many cases, this may not be possible because of the extra work it requires. It is good to note that with the ASP.NET Framework, a considerable effort has been made to address the internationalization of Web applications. You quickly realize that changes to the API, the addition of capabilities to the server controls, and even Visual Studio itself equip you to do the extra work required more easily to bring your application to an international audience. This chapter looks at some of the important items to consider when building your Web applications for the world.

culTures and regions The ASP.NET page that is pulled up in an end user’s browser runs under a specific culture and region setting. When building an ASP.NET application or page, the defi ned culture in which it runs is dependent upon both a culture and region setting coming from the server in which the application is run or from a setting applied by the client (the end user). By default, ASP.NET runs under a culture setting defi ned by the server. The world is made up of a multitude of cultures, each of which has a language and a set of defi ned ways in which it views and consumes numbers, uses currencies, sorts alphabetically, and so on. The .NET Framework defi nes cultures and regions using the Request for Comments 1766 standard defi nition (tags for identification of languages) that specifies a language and region using two -letter codes separated by a dash. Table 32-1 provides examples of some culture defi nitions.

www.it-ebooks.info

1218  ❘  Chapter 32   Building Global Applications

Table 32-1 Culture Code

Description

en-US

English language; United States

en-GB

English language; United Kingdom (Great Britain)

en-AU

English language; Australia

en-CA

English language; Canada

Looking at the examples in this table, you can see that four distinct cultures are defined. These four cultures have some similarities and some differences. All four cultures speak the same language (English). For this reason, the language code of en is used in each culture setting. After the language setting comes the region setting. Even though these cultures speak the same language, distinguishing them further by setting their region (such as US for the United States, GB for the United Kingdom, AU for Australia, and CA for Canada) is important. Invariant As you are probably well aware, the English language in the Culture United States is slightly different from the English language that is used in the United Kingdom, and so forth. Beyond language, differences exist in how dates and numerical values are represented. This is why a culture’s language and EN region are presented together. (Neutral Culture)

The differences do not break down by the country only. Many times, countries contain more than a single language, and each area has its own preference for notation of dates and other items. For example, en-CA specifies English speakers in Canada. Because Canada is not only an English-speaking country, it also includes the culture setting of fr-CA for French-speaking Canadians.

en-US en-GB en-AU

Understanding Culture Types The culture definition you have just seen is called a specific culture definition. This definition is as detailed as you can possibly get — defining both the language and the region. The other type of culture definition is a neutral culture definition. Each specific culture has a specified neutral culture that it is associated with. For example, the English language cultures shown in the previous table are separate, but they also all belong to one neutral culture EN (English). The diagram presented in Figure 32-1 displays how these culture types relate to one another. From this diagram, you can see that many specific cultures belong to a neutral culture. Higher in the hierarchy than the neutral culture is an invariant culture, which is an agnostic culture setting that should be utilized when passing items (such as dates and numbers) around a network. When performing these kinds of operations, make your backend data flows devoid of user-specific culture settings. Instead, apply these settings in the business and presentation layers of your applications.

en-CA

ES (Neutral Culture)

es-ES es-MX es-AR Figure 32-1

Also, pay attention to the neutral culture when working with your applications. Invariably, you are going to build applications with views that are more dependent on a neutral culture than on a specific culture.

www.it-ebooks.info

Cultures and Regions  ❘ 

1219

For example, if you have a Spanish version of your application, you probably make this version available to all Spanish speakers regardless of their regions. In many applications, it will not matter if the Spanish speaker is from Spain, Mexico, or Argentina. In a case where it does make a difference, use the specific culture settings.

The ASP.NET Threads When the end user requests an ASP.NET page, this Web page is executed on a thread from the thread pool. The thread has a culture associated with it. You can get information about the culture of the thread programmatically and then check for particular details about that culture, as shown in Listing 32-1. Listing 32-1:  Checking the culture of the ASP.NET thread

VB

C#

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load Dim ci As CultureInfo = System.Threading.Thread.CurrentThread.CurrentCulture Response.Write("CURRENT CULTURE'S INFO") Response.Write("

Culture's Name: " & ci.Name.ToString() & "
") Response.Write("Culture's Parent Name: " & ci.Parent.Name.ToString() & _ "
") Response.Write("Culture's Display Name: " & ci.DisplayName.ToString() & _ "
") Response.Write("Culture's English Name: " & ci.EnglishName.ToString() & _ "
") Response.Write("Culture's Native Name: " & ci.NativeName.ToString() & _ "
") Response.Write("Culture's Three Letter ISO Name: " & ci.Parent.ThreeLetterISOLanguageName.ToString() & "
") Response.Write("Calendar Type: " & ci.Calendar.ToString() & "

") End Sub protected void Page_Load(object sender, EventArgs e) { CultureInfo ci = System.Threading.Thread.CurrentThread.CurrentCulture; Response.Write("CURRENT CULTURE'S INFO"); Response.Write("

Culture's Name: " + ci.Name.ToString() + "
"); Response.Write("Culture's Parent Name: " + ci.Parent.Name.ToString() + "
"); Response.Write("Culture's Display Name: " + ci.DisplayName.ToString() + "
"); Response.Write("Culture's English Name: " + ci.EnglishName.ToString() + "
"); Response.Write("Culture's Native Name: " + ci.NativeName.ToString() + "
"); Response.Write("Culture's Three Letter ISO Name: " + ci.Parent.ThreeLetterISOLanguageName.ToString() + "
"); Response.Write("Calendar Type: " + ci.Calendar.ToString() + "

"); }

This bit of code in the Page_Load event checks the CurrentCulture property. You can place the result of this value in a CultureInfo object. To get at this object, you import the System.Globalization namespace into your Web page. The CultureInfo object contains a number of properties that provide you with specific culture information. The following items, which are displayed in a series of simple Response .Write statements, are only a small sampling of what is actually available. Running this page produces results similar to what is shown in Figure 32-2. From this figure, you can see that the en-US culture is the default setting in which the ASP.NET thread executes. In addition to this information, you can use the CultureInfo object to get at a lot of other descriptive information about the culture.

www.it-ebooks.info

1220  ❘  Chapter 32   Building Global Applications

Figure 32-2

You can always change a thread’s culture on the overloads provided via a new instantiation of the CultureInfo object, as presented in Listing 32-2. Listing 32-2:  Changing the culture of the thread using the CultureInfo object Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load

VB

System.Threading.Thread.CurrentThread.CurrentCulture = New CultureInfo("th-TH") Dim ci As CultureInfo = System.Threading.Thread.CurrentThread.CurrentCulture Response.Write("CURRENT CULTURE'S INFO") Response.Write("

Culture's Name: " & ci.Name.ToString() & "
") Response.Write("Culture's Parent Name: " & ci.Parent.Name.ToString() & _ "
") Response.Write("Culture's Display Name: " & ci.DisplayName.ToString() & _ "
") Response.Write("Culture's English Name: " & ci.EnglishName.ToString() & _ "
") Response.Write("Culture's Native Name: " & ci.NativeName.ToString() & _ "
") Response.Write("Culture's Three Letter ISO Name: " & _ ci.Parent.ThreeLetterISOLanguageName.ToString() & "
") Response.Write("Calendar Type: " & ci.Calendar.ToString() & "

") End Sub

C#

protected void Page_Load(object sender, EventArgs e) { System.Threading.Thread.CurrentThread.CurrentCulture = new CultureInfo("th-TH"); CultureInfo ci = System.Threading.Thread.CurrentThread.CurrentCulture; Response.Write("CURRENT CULTURE'S INFO"); Response.Write("

Culture's Name: " + ci.Name.ToString() + "
"); Response.Write("Culture's Parent Name: " + ci.Parent.Name.ToString() + "
"); Response.Write("Culture's Display Name: " + ci.DisplayName.ToString() + "
"); Response.Write("Culture's English Name: " + ci.EnglishName.ToString() + "
"); Response.Write("Culture's Native Name: " + ci.NativeName.ToString() + "
"); Response.Write("Culture's Three Letter ISO Name: " +

www.it-ebooks.info

Cultures and Regions  ❘ 

1221

ci.Parent.ThreeLetterISOLanguageName.ToString() + "
"); Response.Write("Calendar Type: " + ci.Calendar.ToString() + "

"); }

In this example, only a single line of code is added to assign a new instance of the CultureInfo object to the CurrentCulture property of the thread being executed by ASP.NET. The culture setting enables the CultureInfo object to define the culture you want to utilize. In this case, the Thai language of Thailand is assigned, producing the results shown in Figure 32-3.

Figure 32-3

From this figure, you can see that the .NET Framework goes so far as to provide the native name of the language used even if it is not a Latin-based letter style. In this case, the results are presented for the Thai language in Thailand, as well as some of the properties that are associated with this culture (such as an entirely different calendar from the one used in Western Europe and the United States). Remember that you reference System.Globalization to get at the CultureInfo object.

Server-Side Culture Declarations ASP.NET enables you to easily define the culture that is used either by your entire ASP.NET application or by a specific page within your application. You can specify the culture for any of your ASP.NET applications by means of the appropriate configuration files. In the default install of ASP.NET, no culture is specified, as is evident when you look at the global web.config.comments file (meant for documentation purposes) found in the ASP.NET 4 CONFIG folder (C:\WINDOWS\Microsoft.NET\Framework\v4.0.21006\ CONFIG). (If you are using ASP.NET 3.5, remember that ASP.NET 3.5 is built on top of ASP.NET 2.0 and uses the same configuration files.) In the web.config.comments file, you find a section of the configuration document, shown in Listing 32-3. Listing 32-3:  The section in the web.config.comments file

Note the two attributes represented in bold — culture and uiCulture. The culture attribute enables you to define the culture to use for processing incoming requests, whereas the uiCulture attribute enables you define the default culture needed to process any resource files in the application. (The use of these attributes is covered later in the chapter.)

www.it-ebooks.info

1222  ❘  Chapter 32   Building Global Applications

As you look at the configuration declaration in Listing 32-3, you can see that nothing is specified for the culture settings. One option you have when specifying a culture on the server is to define this culture in the server version of the web.config file found in the CONFIG folder. This causes every ASP.NET 4 application on this server to adopt this particular culture setting. The other option is to specify these settings in the web.config file of the application, as shown in Listing 32-4. Listing 32-4:  Defining the section in the web.config file

In this case, the culture established for just this ASP.NET application is the Russian language in the country of Russia. In addition to setting the culture at either the server-wide or the application-wide level, another option is to set the culture at the page level, as shown in Listing 32-5. Listing 32-5:  Defining the culture at the page level using the @Page directive <%@ Page Language="VB" UICulture="ru-RU" Culture="ru-RU" %>

This example determines that the Russian language and culture settings are used for everything on the page. You can see this in action by using this @Page directive and a simple calendar control on the page. Figure 32-4 shows the output.

Client-Side Culture Declarations In addition to using server-side settings to define the culture for your ASP.NET pages, you also have the option of defining the culture with what the client has set as his preference in a browser instance. When end users install Microsoft’s Internet Explorer and some of the other browsers, they have the option to select their preferred cultures in a particular order (if they have selected more than a single culture preference). To see this in action in IE, select Tools ➪ Internet Options from the IE menu. On the first tab provided (General), you see a Languages button at the bottom of the dialog. Select this button and the Language Preference dialog appears, as shown in Figure 32-5. In this figure, you can see that two cultures are selected from the list of available cultures. To add any additional cultures to the list, click the Add button in the dialog and select the appropriate culture from the list. After you have selected cultures that are present in the list, you can select the order in which you prefer to use them. In the case of Figure 32-5, the Finnish culture is established as the most preferred culture, whereas the U.S. version of English is selected as the second preference. A user with this setting gets the Finnish language version of the application before anything else; if a Finnish version is not available, a U.S. English version is presented.

www.it-ebooks.info

Figure 32-5

Figure 32-4

Cultures and Regions  ❘ 

1223

After the end user selects a culture, you can use the auto feature provided in ASP.NET 4. Instead of specifying a distinct culture in any of the configuration files or from the @Page directive, you can also state that ASP.NET should automatically select the culture provided by the end user requesting the page. This is done using the auto keyword, as shown in Listing 32-6. Listing 32-6:  Changing the culture to the end user’s selection <%@ Page Language="VB" UICulture="auto" Culture="auto" %>

With this construction in your page, the dates, calendars, and numbers now appear in the preferred culture of the requestor. What happens, however, if you have translated resources in resource files (shown later in the chapter) that depend on a culture specification? What if you only have specific translations, and so cannot handle every possible culture that might be returned to your ASP.NET page? In this case, you can specify the auto option with an additional fallback option if ASP.NET cannot find the culture settings of the user (such as culture-specific resource files). Listing 32-7 shows this usage. Listing 32-7:  Providing a fallback culture from the auto option <%@ Page Language="VB" UICulture="auto:en-US" Culture="auto:en-US" %>

In this case, the automatic detection is utilized, but if the culture the end user prefers is not present, then en-US is used.

Translating Values and Behaviors In the process of globalizing your ASP.NET application, you may notice a number of items that are done differently from building an application that is devoid of globalization, including how dates are represented and currencies are shown. This next section touches upon some of these topics.

Understanding Differences in Dates Different cultures specify dates and time very differently. For instance, take the following date as an example: 08/11/2010

What is this date exactly? Is it August 11, 2010 or is it November 8, 2010? I repeat: When storing values such as date/time stamps in a database or other some type of backend system, you should always use the same culture (or invariant culture) for these items so that you avoid any mistakes. Converting these items for use by the end user should be the job of the business logic layer or the presentation layer. Setting the culture at the server level or in the @Page directive (as discussed earlier) enables ASP.NET to make these conversions for you. You can also simply assign a new culture to the thread in which ASP.NET is running. For example, look at the code listing presented in Listing 32-8. Listing 32-8:  Working with date/time values in different cultures

VB

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load Dim dt As DateTime = New DateTime(2010, 8, 11, 11, 12, 10, 10) System.Threading.Thread.CurrentThread.CurrentCulture = New CultureInfo("en-US") Response.Write("en-US
") Response.Write(dt.ToString() & "
") System.Threading.Thread.CurrentThread.CurrentCulture = New CultureInfo("ru-RU") Response.Write("ru-RU
") Response.Write(dt.ToString() & "
") System.Threading.Thread.CurrentThread.CurrentCulture = New CultureInfo("fi-FI")

continues

www.it-ebooks.info

1224  ❘  Chapter 32   Building Global Applications

Listing 32-8  (continued) Response.Write("fi-FI
") Response.Write(dt.ToString() & "
") System.Threading.Thread.CurrentThread.CurrentCulture = new CultureInfo("th-TH") Response.Write("th-TH
") Response.Write(dt.ToString()) End Sub

C#

protected void Page_Load(object sender, EventArgs e) { DateTime dt = new DateTime(2010, 8, 11, 11, 12, 10, 10); System.Threading.Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US"); Response.Write("en-US
"); Response.Write(dt.ToString() + "
"); System.Threading.Thread.CurrentThread.CurrentCulture = new CultureInfo("ru-RU"); Response.Write("ru-RU
"); Response.Write(dt.ToString() + "
"); System.Threading.Thread.CurrentThread.CurrentCulture = new CultureInfo("fi-FI"); Response.Write("fi-FI
"); Response.Write(dt.ToString() + "
"); System.Threading.Thread.CurrentThread.CurrentCulture = new CultureInfo("th-TH"); Response.Write("th-TH
"); Response.Write(dt.ToString()); }

In this case, four different cultures are utilized, and the date/time construction used by each culture is written to the browser screen using a Response.Write command. Figure 32-6 shows the result from this code operation.

Figure 32-6

As you can see, the formats used to represent a date/time value are dramatically different from one another — and one of the cultures, the Thai culture (th-TH), even uses an entirely different calendar that labels this year as 2553.

www.it-ebooks.info

Cultures and Regions  ❘ 

1225

Understanding Differences in Numbers and Currencies In addition to date/time values, numbers are constructed quite differently from one culture to the next. How can a number be represented differently in different cultures? Well, it has less to do with the actual number (although certain cultures use different number symbols) and more to do with how the number separators are used for decimals or for showing amounts such as thousands, millions, and more. For example, in the English culture of the United States (en-US), you see numbers represented in the following fashion: 5,123,456.00

From this example, you can see that the en-US culture uses a comma as a separator for thousands and a period for signifying the start of any decimals that might appear after the number is presented. This number appears quite differently when working with other cultures. Listing 32-9 shows you an example of representing numbers in other cultures. Listing 32-9:  Working with numbers in different cultures

VB

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load Dim myNumber As Double = 5123456.00 System.Threading.Thread.CurrentThread.CurrentCulture = New CultureInfo("en-US") Response.Write("en-US
") Response.Write(myNumber.ToString("n") & "
") System.Threading.Thread.CurrentThread.CurrentCulture = new CultureInfo("vi-VN") Response.Write("vi-VN
") Response.Write(myNumber.ToString("n") & "
") System.Threading.Thread.CurrentThread.CurrentCulture = new CultureInfo("fi-FI") Response.Write("fi-FI
") Response.Write(myNumber.ToString("n") & "
") System.Threading.Thread.CurrentThread.CurrentCulture = new CultureInfo("fr-CH") Response.Write("fr-CH
") Response.Write(myNumber.ToString("n")) End Sub

C#

protected void Page_Load(object sender, EventArgs e) { double myNumber = 5123456.00; System.Threading.Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US"); Response.Write("en-US
"); Response.Write(myNumber.ToString("n") + "
"); System.Threading.Thread.CurrentThread.CurrentCulture = new CultureInfo("vi-VN"); Response.Write("vi-VN
"); Response.Write(myNumber.ToString("n") + "
"); System.Threading.Thread.CurrentThread.CurrentCulture = new CultureInfo("fi-FI"); Response.Write("fi-FI
"); Response.Write(myNumber.ToString("n") + "
"); System.Threading.Thread.CurrentThread.CurrentCulture = new CultureInfo("fr-CH"); Response.Write("fr-CH
"); Response.Write(myNumber.ToString("n")); }

Running this short example produces the results presented in Figure 32-7.

www.it-ebooks.info

1226  ❘  Chapter 32   Building Global Applications

Figure 32-7

From this example, you can see that the other cultures represented here show numbers in quite a different format than that of the en-US culture. The second culture listed in the figure, vi-VN (Vietnamese in Vietnam), constructs a number exactly the opposite from the way it is constructed in en-US. The Vietnamese culture uses periods for the thousand separators and a comma for signifying decimals. Finnish, on the other hand, uses spaces for the thousand separators and a comma for the decimal separator, whereas the French-speaking Swiss use a high comma for separating thousands and a period for the decimal separator. As you can see, “translating” numbers to the proper construction so that users of your application can properly understand the numbers represented is important. You also represent numbers when working with currencies. Converting currencies so that end users understand the proper value of an item is one thing, but translating the construction of the currency just as you would a basic number is another. Each culture has a distinct currency symbol used to signify that a number represented is an actual currency value. For example, the en-US culture represents a currency in the following format: $5,123,456.00

The en-US culture uses a U.S. Dollar symbol ($), and the location of this symbol is just as important as the symbol itself. For en-US, the $ symbol directly precedes the currency value (with no space in between the symbol and the first character of the number). Other cultures use different symbols to represent a currency and often place those currency symbols in different locations. Change the previous Listing 32-9 so that it now represents the number as a currency. Listing 32-10 presents the necessary changes. Listing 32-10:  Working with currencies in different cultures

VB

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load Dim myNumber As Double = 5123456.00 System.Threading.Thread.CurrentThread.CurrentCulture = New CultureInfo("en-US") Response.Write("en-US
") Response.Write(myNumber.ToString("c") & "
") System.Threading.Thread.CurrentThread.CurrentCulture = new CultureInfo("vi-VN") Response.Write("vi-VN
") Response.Write(myNumber.ToString("c") & "
") System.Threading.Thread.CurrentThread.CurrentCulture = new CultureInfo("fi-FI") Response.Write("fi-FI
") Response.Write(myNumber.ToString("c") & "
")

www.it-ebooks.info

Cultures and Regions  ❘ 

1227

System.Threading.Thread.CurrentThread.CurrentCulture = new CultureInfo("fr-CH") Response.Write("fr-CH
") Response.Write(myNumber.ToString("c")) End Sub

C#

protected void Page_Load(object sender, EventArgs e) { double myNumber = 5123456.00; System.Threading.Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US"); Response.Write("en-US
"); Response.Write(myNumber.ToString("c") + "
"); System.Threading.Thread.CurrentThread.CurrentCulture = new CultureInfo("vi-VN"); Response.Write("vi-VN
"); Response.Write(myNumber.ToString("c") + "
"); System.Threading.Thread.CurrentThread.CurrentCulture = new CultureInfo("fi-FI"); Response.Write("fi-FI
"); Response.Write(myNumber.ToString("c") + "
"); System.Threading.Thread.CurrentThread.CurrentCulture = new CultureInfo("fr-CH"); Response.Write("fr-CH
"); Response.Write(myNumber.ToString("c")); }

Run this example to see how these cultures represent currency values, as illustrated in Figure 32-8.

Figure 32-8

From this figure, you can see that not only are the numbers constructed quite differently from one another, but the currency symbol and the location of the symbol in regard to the number are quite different as well. When working with currencies, note that when you are using currencies on an ASP.NET page, you have provided an automatic culture setting for the page as a whole (such as setting the culture in the @Page directive). You must specify a specific culture for the currency that is the same in all cases unless you are actually doing a currency conversion. For instance, if you are specifying a U.S. Dollar currency value on your ASP.NET page, you do not want to specify that the culture of the currency is something else (for example, the Euro). An exception would be if you actually performed a currency conversion and showed

www.it-ebooks.info

1228  ❘  Chapter 32   Building Global Applications

the appropriate Euro value along with the culture specification of the currency. Therefore, if you are using an automatic culture setting on your ASP.NET page and you are not converting the currency, you perform something similar to what appears in Listing 32-11 for currency values. Listing 32-11:  Reverting to a specific culture when displaying currencies

VB

Dim myNumber As Double = 5123456.00 Dim usCurr As CultureInfo = New CultureInfo("en-US") Response.Write(myNumber.ToString("c", usCurr))

C#

double myNumber = 5123456.00; CultureInfo usCurr = new CultureInfo("en-US"); Response.Write(myNumber.ToString("c", usCurr));

Understanding Differences in Sorting Strings You have learned to translate textual values and alter the construction of the numbers, date/time values, currencies, and more when you are globalizing an application. You should also take note when applying culture settings to some of the programmatic behaviors that you establish for values in your applications. One operation that can change based upon the culture setting applied is how .NET sorts strings. You might think that all cultures sort strings in the same way (and generally they do), but sometimes differences exist in how sorting occurs. To give you an example, Listing 32-12 shows you a sorting operation occurring in the en-US culture. Listing 32-12:  Working with sorting in different cultures Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) System.Threading.Thread.CurrentThread.CurrentCulture = New CultureInfo("en-US")

VB

Dim myList As List(Of String) = New List(Of String) myList.Add("Washington D.C.") myList.Add("Helsinki") myList.Add("Moscow") myList.Add("Warsaw") myList.Add("Vienna") myList.Add("Tokyo") myList.Sort() For Each item As String In myList Response.Write(item.ToString() + "
") Next End Sub

C#

protected void Page_Load(object sender, EventArgs e) { System.Threading.Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US"); List myList = new List(); myList.Add("Washington D.C."); myList.Add("Helsinki"); myList.Add("Moscow"); myList.Add("Warsaw"); myList.Add("Vienna"); myList.Add("Tokyo"); myList.Sort(); foreach (string item in myList)

www.it-ebooks.info

Cultures and Regions  ❘ 

1229

{ Response.Write(item.ToString() + "
"); } }

For this example to work, you have to import the System.Collections and the System.Collections .Generic namespaces, because this example makes use of the List(Of String) object. In this example, a generic list of capitals from various countries of the world is created in random order. Then the Sort() method of the generic List(Of String) object is invoked. This sorting operation sorts the strings based upon how sorting is done for the defined culture in which the ASP.NET thread is running. Listing 32-12 shows the sorting as it is done for the en-US culture. Figure 32-9 shows the result of this operation.

Figure 32-9

This result is much what you would expect. Now, however, change the previous example from Listing 32-12 so that the culture is set to Finnish, as shown in Listing 32-13. Listing 32-13:  Changing the culture to Finnish

VB

System.Threading.Thread.CurrentThread.CurrentCulture = New CultureInfo("fi-FI")

C#

System.Threading.Thread.CurrentThread.CurrentCulture = new CultureInfo("fi-FI");

If you run the same bit of code under the Finnish culture setting, you get the results presented in Figure 32-10.

Figure 32-10

www.it-ebooks.info

1230  ❘  Chapter 32   Building Global Applications

If you examine the difference between the Finnish culture sorting done in Figure 32-10 and the U.S. English culture sorting done in Figure 32-9, you see that the city of Vienna is in a different place in the Finnish version. This is because, in the Finnish language, no difference exists between the letter V and the letter W. Because no difference exists, if you are sorting using the Finnish culture setting, then Vi comes after Wa and, thus, Vienna comes last in the list of strings in the sorting operation.

ASP.NET 4 Resource Files When you work with ASP.NET, all resources are handled by a resource file. A resource file is an XML-based file that has a .resx extension. You can have Visual Studio 2010 help you construct this file. Resource files provide a set of items that are utilized by a specified culture. In your ASP.NET applications, you store resource files as either local resources or global resources. The following sections look at how to use each type of resource.

Making Use of Local Resources You would be surprised how easily you can build an ASP.NET page so that it can be localized into other languages. Really, the only thing you must do is build the ASP.NET page as you normally would, and then use some built-in capabilities from Visual Studio 2010 to convert the page to a format that allows you to plug in other languages easily. To see this in action, build a simple ASP.NET page as presented in Listing 32-14. Listing 32-14:  Building the basic ASP.NET page to localize <%@ Page Language="VB" %> Sample Page






As you can see, there is not much to this page. It is composed of a couple of Label controls, as well as TextBox and Button controls. The end user enters her name into the text box, and then the Label2 server control is populated with the inputted name and a simple greeting. The next step is what makes Visual Studio so great. To change the construction of this page so that it can be localized easily from resource files, open the page in Visual Studio and select Tools ➪ Generate Local

www.it-ebooks.info

ASP.NET 4 Resource Files  ❘ 

1231

Resource from the Visual Studio menu. Note that you can select this tool only when you are in the Design view of your page. It will not work in the split view or the code view of the page. Selecting the Generate Local Resource from the Tool menu option causes Visual Studio to create an App_ LocalResources folder in your project if you do not have one already. A .resx file based upon this ASP.NET page is then placed in the folder. For instance, if you are working with the Default.aspx page, the resource file is named Default.aspx.resx. Figure 32-11 shows these changes. If you right-click on the .resx file and view the code, notice that the .resx file is nothing more than an XML file with an associated schema at the beginning of the document. The resource file that is generated for you takes every possible property of every translatable control on the page and gives each item a key value that can be referenced in your ASP.NET page. If you look at the code of the page, notice that all the text values that you placed in the page have been left in the page, but they have also been placed inside the resource file. You can see how Visual Studio changed the code of the Default.aspx page in Listing 32-15.

Figure 32-11

Listing 32-15:  Looking at how Visual Studio altered the page code <%@ Page Language="VB" Culture="auto" meta:resourcekey="PageResource1" UICulture="auto" %> Sample Page


 



From this bit of code, you can see that the Culture and UICulture attributes have been added to the @Page directive with a value of auto, thus enabling this application to be localized. Also, the attribute meta:resourcekey has been added to each of the controls along with an associated value. This is the key from the .resx file that was created on your behalf. Double-clicking on the Default.aspx.resx file opens the resource file in the Resource Editor, which you will find is built into Visual Studio. Figure 32-12 shows this editor.

www.it-ebooks.info

1232  ❘  Chapter 32   Building Global Applications

In the figure, note that a couple of properties from each of the server controls have been defined in the resource file. For instance, the Button server control has its Text and ToolTip properties exposed in this resource file, and the Visual Studio localization tool has pulled the default Text property value from the control based on what you placed there. Looking more closely at the Button server control constructions in this file, you can see that both the Text and ToolTip properties have a defining Button1Resource1 value preceding the property name. This key is used in the Button server control you saw earlier:

Figure 32-12

You can see that a meta:resourcekey attribute has been added and, in this case, it references Button1Resource1. All the properties using this key in the resource file (for example, the Text and ToolTip properties) are applied to this Button server control at runtime.

Adding Another Language Resource File Now that the Default.aspx.resx file is in place, this is a file for an invariant culture. No culture is assigned to this resource file. If no culture can be determined, this resource file is then utilized. To add another resource file for the Default.aspx page that handles another language altogether, you copy and paste the Default.aspx.resx file into the same App_LocalResources folder and rename the newly copied file. If you use Default.aspx.fi-FI.resx, you give the following keys the following values to make a Finnish language resource file: Button1Resource1.Text Label1Resource1.Text PageResource1.Title

Lähetä Nimi Mikä sinun nimi on? Näytesivu

You want to create a custom resource in both resource files using the key Label2Answer. The Default .aspx.resx file should have the following new key: Label2Answer

Hello

www.it-ebooks.info

ASP.NET 4 Resource Files  ❘ 

1233

Now you can add the key Label2Answer to the Default.aspx.fi-FI.resx file, as shown here: Label2Answer

Hei

You now have resources for specific controls and a resource that you can access later programmatically.

Finalizing the Building of the Default.aspx Page Finalizing the Default.aspx page, you want to add a Button1_Click event so that when the end user enters a name into the text box and clicks the Submit button, the Label2 server control provides a greeting to him or her that is pulled from the local resource files. When all is said and done, you should have a Default.aspx page that resembles the one in Listing 32-16. Listing 32-16:  The final Default.aspx page <%@ Page Language=”VB” Culture=”auto” meta:resourcekey=”PageResource1” UICulture=”auto” %>

VB

Sample Page


 



C#

<%@ Page Language=”C#” Culture=”auto” meta:resourcekey=”PageResource1” UICulture=”auto” %>

www.it-ebooks.info

1234  ❘  Chapter 32   Building Global Applications

In addition to pulling local resources using the meta:resourcekey attribute in the server controls on the page to get at the exposed attributes, you can also get at any property value contained in the local resource file by using the GetLocalResourceObject. When using GetLocalResourceObject, you simply use the name of the key as a parameter, as shown here: GetLocalResourceObject("Label2Answer")

You could just as easily get at any of the control’s property values from the resource file programmatically using the same construct: GetLocalResourceObject("Button1Resource1.Text")

With the code from Listing 32-16 in place and the resource files completed, you can run the page, entering a name in the text box and then clicking the button to get a response, as illustrated in Figure 32-13.

Figure 32-13

What happened behind the scenes that caused this page to be constructed in this manner? First, only two resource files, Default.aspx.resx and Default.aspx.fi-FI.resx, are available. The Default.aspx .resx resource file is the invariant culture resource file, whereas the Default.aspx.fi-FI.resx resource file is for a specific culture (fi-FI). Because I requested the Default.aspx page, and my browser is set to en-US as my preferred culture, ASP.NET found the local resources for the Default.aspx page. From there, ASP.NET made a check for an en-US–specific version of the Default.aspx page. Because there is not a specific page for the en-US culture, ASP.NET made a check for an EN (neutral culture)–specific page. Not finding a page for the EN neutral culture, ASP.NET was then forced to use the invariant culture resource file of Default.aspx.resx, producing the page presented in Figure 32-13. Now, if you set your IE language preference as fi-FI and rerun the Default.aspx page, you see a Finnish version of the page, as illustrated in Figure 32-14.

Figure 32-14

www.it-ebooks.info

ASP.NET 4 Resource Files  ❘ 

1235

In this case, setting the IE language preference to fi-FI results in this culture’s page instead of the invariant culture page that was presented earlier. ASP.NET found this specific culture through use of the Default .aspx.fi-FI.resx resource file. You can see that all the control properties that were translated and placed within the resource file are utilized automatically by ASP.NET, including the page title presented in the title bar of IE.

Neutral Cultures Are Generally More Preferred When you are working with the resource files from this example, note that one of the resources is for a specific culture. The Default.aspx.fi-FI.resx file is for a specific culture — the Finnish language as spoken in Finland. Another option would be to make this file work not for a specific culture but, instead, for a neutral culture. To accomplish this task, you simply name the file Default.aspx.FI.resx instead. In this example, having a specific culture declared really does not make that much difference because no other countries speak Finnish. It would make sense for languages such as German, Spanish, or French. These languages are spoken in multiple countries. For instance, if you are going to have a Spanish version of the Default.aspx page, you could definitely build it for a specific culture, such as Default.aspx.es-MX.resx. This construction is for the Spanish language as spoken in Mexico. With this in place, if someone requests the Default.aspx page with the language setting of es-MX, that user is provided with the contents of this resource file. However, what if the requestor has a setting of es-ES? He will not get the Default.aspx .es-MX.resx resource file but, instead, the invariant culture resource file of Default.aspx.resx. If you are going to make only a single translation into German, Spanish, or another language for your site or any of your pages, you want to construct the resource files to be for neutral cultures rather than for specific cultures. If you have the resource file Default.aspx.ES.resx, then it won’t matter if the end user’s preferred setting is set to es-MX, es-ES, or even es-AR — the user gets the appropriate ES neutral culture version of the page.

Making Use of Global Resources Besides using only local resources that specifically deal with a particular page in your ASP.NET application, you also have the option of creating global resources that can be used across multiple pages. To create a resource file that can be utilized across the entire application, right-click on the solution in the Solution Explorer of Visual Studio and select Add New Item. From the Add New Item dialog, select Resource file. Selecting this option provides you with a Resource.resx file. Visual Studio places this file in a new folder called App_GlobalResources. Again, this first file is the invariant culture resource file. Add a single string resource giving it the key of PrivacyStatement and a value of some kind (a long string). After you have the invariant culture resource file completed, the next step is to add another resource file, but this time name it Resource.fi-FI.resx. Again, for this resource file, give a string key of PrivacyStatement and a different value altogether from the one you used in the other resource file. The idea of a global resource file is that you have access to these resources across your entire application. You can gain access to the values that you place in these files in several ways. One way is to work the value directly into any of your server control declarations. For instance, you can place this privacy statement in a Label server control, as presented in Listing 32-17. Listing 32-17:  Using a global resource directly in a server control

With this construction in place, you can now grab the appropriate value of the PrivacyStatement global resource, depending upon the language preference of the end user requesting the page. To make this construction work, you use the keyword Resources followed by a colon. Next, you specify the name of the resource file class. In this case, the name of the resource file is Resource because this statement goes to

www.it-ebooks.info

1236  ❘  Chapter 32   Building Global Applications

the Resource.resx and Resource.fi-FI.resx files in order to find what it needs. After specifying the particular resource file to use, the next item in the statement is the key — in this case, PrivacyStatement. Another way of achieving the same result is to use some built-in dialogs within Visual Studio. To do so, highlight the server control you want in Visual Studio from Design view so that the control appears within the Properties window. For this example, highlight a Label server control. From the Properties window, you click the button within the Expressions property. This launches the Expressions dialog and enables you to bind the PrivacyStatement value to the Text property of the control, as illustrated in Figure 32-15.

Figure 32-15

To make what you see in the above figure work, highlight the Text property in the Bindable properties list. You then select an expression type from a drop-down list on the right side of the dialog. Your options include AppSettings, ConnectionStrings, and Resources. Select the Resources option and you are then asked for the ClassKey and ResourceKey property values. The ClassKey is the name of the file that should be utilized. In this example, the name of the file is Resource.resx. Therefore, use the Resource keyword as a value. You are provided with a drop-down list in the ResourceKey property section with all the keys available in this file. Because only a single key exists at this point, you find only the PrivacyStatement key in this list. Make this selection and click the OK button. The Label server control changes and now appears as it was presented earlier in Listing 32-17. One nice feature is that the resources provided via global resources are available in a strongly typed manner. For instance, you can programmatically get at a global resource value by using the construction presented in Listing 32-18.

www.it-ebooks.info

Looking at the Resource Editor  ❘ 

1237

Listing 32-18:  Programmatically getting at global resources

VB C#

Label1.Text = Resources.Resource.PrivacyStatement Label1.Text = Resources.Resource.PrivacyStatement;

In Figure 32-16, you can see that you have full IntelliSense for these resource values.

Figure 32-16

Looking at the Resource Editor Visual Studio 2010 provides an editor for working with resource files. You have already seen some of the views available from this editor. Resources are categorized visually by the data type of the resource. So far, this chapter has dealt only with the handling of strings, but other categories exist (such as images, icons, audio files, miscellaneous files, and other items). These options are illustrated in Figure 32-17.

Figure 32-17

www.it-ebooks.info

1238  ❘  Chapter 32   Building Global Applications

Summary We hope you see the value in globalizing your ASP.NET applications so that they can handle multiple cultures. This chapter looked at some of the issues you face when globalizing your applications and some of the built-in tools provided via both Visual Studio and the .NET Framework to make this process easier for you. Globalizing your applications is really almost as easy as taking ASP.NET pages that have been already created and running the appropriate Visual Studio tool over the files to rebuild the pages to handle translations.

www.it-ebooks.info

33

Configuration whaT’s in This chaPTer? ➤

Introduction to the ASP�NET configuration file



An overview of the ASP�NET configuration settings



Encrypting portions of your configuration files



An examination of the ASP�NET configuration APIs



Storing and retrieving sensitive information

Those of you who remember the “Classic” ASP days know that ASP ’s configuration information was stored in a binary repository called the Internet Information Services (IIS) metabase. To configure a classic ASP application, you had to modify the metabase, either through script or, more commonly, through the IIS Microsoft Management Console (MMC) snap -in. Unlike classic ASP, all the available versions of ASP.NET do not require extensive use of the IIS metabase. Instead, ASP.NET uses an XML fi le-based configuration system that is much more flexible, accessible, and easier to use. When building ASP.NET, the ASP.NET team wanted to improve the manageability of the product. Although the release of ASP.NET 1.0 was a huge leap forward in Web application development, it really targeted the developer. What was missing was the focus on the administrator — the person who takes care of Web applications after they are built and deployed. ASP.NET today makes configuring an ASP.NET application quite easy for you by working either directly with the various configuration fi les or by using GUI tools that, in turn, interact with configuration fi les. Before examining the various GUI-based tools in detail in Chapter 36, you fi rst take an in-depth look at how to work directly with the XML configuration fi les to change the behavior of your ASP.NET applications. The journey into these configuration enhancements starts with an overview of configuration in ASP.NET.

configuraTion overview ASP.NET configuration is stored in two primary XML -based fi les in a hierarchal fashion. XML is used to describe the properties and behaviors of various aspects of ASP.NET applications. The ASP.NET configuration system supports two kinds of configuration fi les: ➤

Server or machine-wide configuration fi les such as the machine.config fi le



Application configuration fi les such as the web.config fi le

www.it-ebooks.info

1240  ❘  Chapter 33   Configuration

Because the configuration files are based upon XML, the elements that describe the configuration are, therefore, case-sensitive. Moreover, the ASP.NET configuration system follows camel-casing naming conventions. If you look at the session state configuration example shown in Listing 33-1, for example, you can see that the XML element that deals with session state is presented as . Listing 33-1:  Session state configuration

The benefits of having an XML configuration file instead of a binary metabase include the following: ➤➤

The configuration information is human-readable and can be modified using a plain text editor such as Notepad, although using Visual Studio 2010 or another XML-aware editor is recommended. Unlike a binary metabase, the XML-based configuration file can be easily copied from one server to another, as with any simple file. This feature is extremely helpful when working in a Web farm scenario.

➤➤

When some settings are changed in the configuration file, ASP.NET automatically detects the changes and applies them to the running ASP.NET application. ASP.NET accomplishes this by creating a new instance of the ASP.NET application and directing end users to this new application.

➤➤

The configuration changes are applied to the ASP.NET application without the need for the administrator to stop and start the Web server. Changes are completely transparent to the end user.

➤➤

The ASP.NET configuration system is extensible.

➤➤

Application-specific information can be stored and retrieved very easily.

➤➤

The sensitive information stored in the ASP.NET configuration system can optionally be encrypted to keep it from prying eyes.

Server Configuration Files Every ASP.NET server installation includes a series of configuration files, such as the machine.config file. This file is installed as a part of the default .NET Framework installation. You can find machine.config and the other server-specific configuration files in C:\Windows\Microsoft.NET\Framework\v4.0.21006\ CONFIG. They represent the default settings used by all ASP.NET Web applications installed on the server. Some of the server-wide configuration files include the following: ➤➤

machine.config

➤➤

machine.config.comments

➤➤

machine.config.default

➤➤

web.config

➤➤

web.config.comments

➤➤

web.config.default

➤➤

web_hightrust.config

➤➤

web_hightrust.config.default

➤➤

web_lowtrust.config

➤➤

web_lowtrust.config.default

www.it-ebooks.info

Configuration overview



web_mediumtrust.config



web_mediumtrust.config.default



web_minimaltrust.config



web_minimaltrust.config.default

❘ 1241

The system-wide configuration fi le, machine.config, is used to configure common .NET Framework settings for all applications on the machine. As a rule, editing or manipulating the machine.config fi le is not a good idea unless you know what you are doing. Changes to this fi le can affect all applications on your computer (Windows, Web, and so on). Because the .NET Framework supports side-by-side execution mode, you might fi nd more than one installation of the machine.config fi le if you have multiple versions of the .NET Framework installed on the server. If you have .NET Framework versions 1.0, 1.1, 2.0, and 4 running on the server, for example, each .NET Framework installation has its own machine.config fi le. This means that you will fi nd four machine.config fi le installations on that particular server. It is interesting to note that the .NET Framework 3.5 is really just a bolt-on to the .NET Framework 2.0. (It includes extra DLLs, which are sometimes referred to as extensions.) Thus, the .NET Framework 3.5 uses the same machine.config fi le as the .NET Framework 2.0. It is important to note that the .NET Framework 4 is a completely new CLR and doesn’t have the same 2.0 dependency that the .NET Framework 3.5 does. The .NET 4 version of the framework includes its own machine.config fi le. In addition to the machine.config fi le, the .NET Framework installer also installs two more fi les called machine.config.default and machine.config.comments. The machine.config.default fi le acts as a backup for the machine.config fi le. If you want to revert to the factory setting for machine.config, simply copy the settings from the machine.config.default to the machine.config fi le. The machine.config.comments fi le contains a description for each configuration section and explicit settings for the most commonly used values. machine.config.default and machine.config.comment fi les are not used by the .NET Framework runtime; they’re installed in case you want to revert to default factory settings and default values. You will also fi nd a root-level web.config fi le in place within the same CONFIG folder as the machine .config. When making changes to settings on a server-wide basis, you should always attempt to make these changes in the root web.config fi le rather than in the machine.config fi le. You will fi nd that fi les like the machine.config.comments and the machine.config.default fi les also exist for the web.config fi les (web.config.comments and web.config.default). By default, your ASP.NET Web applications run under a full trust setting. You can see this setting by looking at the and sections in the root-level web.config fi le. Listing 33 -2 presents these sections. lisTing 33-2: The root web.config showing the trust level

continues

www.it-ebooks.info

1242  ❘  Chapter 33   Configuration

Listing 33-2  (continued)


The other policy files are defined at specific trust levels. These levels determine the code-access security (CAS) allowed for ASP.NET. To change the trust level in which ASP.NET applications can run on the server, you simply change the element within the document or within your application’s instance of the web.config file. For example, you can change to a medium trust level using the code shown in Listing 33-3. Listing 33-3:  Changing the trust level to medium trust

In this case, not only does this code mandate use of the web_mediumtrust.config file, but also (by setting the allowOverride attribute to false) it forces this trust level upon every ASP.NET application on the server. Individual application instances are unable to change this setting by overriding it in their local web.config files because this setting is in the root-level web.config file. If you look through the various trust level configuration files (such as the web_mediumtrust.config file), notice that they define what kinds of actions you can perform through your code operations. For example, the web_hightrust.config file allows for open FileIO access to any point on the server as illustrated in Listing 33-4. Listing 33-4:  The web_hightrust.config file’s definition of FileIO CAS

If, however, you look at the medium trust web.config file (web_mediumtrust.config), you see that this configuration file restricts ASP.NET to only those FileIO operations within the application directory. Listing 33-5 presents this definition. Listing 33-5:  FileIO restrictions in the web_mediumtrust.config file
www.it-ebooks.info

Configuration Overview  ❘ 

1243

Read="\$AppDir\$" Write="\$AppDir\$" Append="\$AppDir\$" PathDiscovery="\$AppDir\$" />

Seeing in which trust level you can run your ASP.NET applications and changing the section to enable the appropriate level of CAS is always a good idea.

Application Configuration File Unlike the machine.config file, each and every ASP.NET application has its own copy of configuration settings stored in a file called web.config. If the Web application spans multiple subfolders, each subfolder can have its own web.config file that inherits or overrides the parent’s file settings. To update servers in your farm with these new settings, you simply copy this web.config file to the appropriate application directory. ASP.NET takes care of the rest — no server restarts and no local server access is required — and your application continues to function normally, except that it now uses the new settings applied in the configuration file.

Applying Configuration Settings When the ASP.NET runtime applies configuration settings for a given Web request, machine.config (as well as any of the web.config files configuration information) is merged into a single unit, and that information is then applied to the given application. Configuration settings are inherited from any parent web.config file or machine.config, which is the root configuration file or the ultimate parent. Figure 33-1 presents an example of this. machine.config C:\WINDOWS\Microsoft.NET\ Framework\v4.0.21006\CONFIG

web.config C:\WINDOWS\Microsoft.NET\ Framework\v4.0.21006\CONFIG These settings supercede its parent’s settings.

web.config $ AppDir $ These settings supercede its parent’s settings.

web.config $ AppDir $\subdirectory These settings supercede its parent’s settings.

Figure 33-1

www.it-ebooks.info

1244



chaPTer 33 conFigurAtion

The configuration for each Web application is unique; however, settings are inherited from the parent. For example, if the web.config fi le in the root of your Web site defi nes a session timeout of 10 minutes, then that particular setting overrides the default ASP.NET setting inherited from the machine.config or the root web.config fi le. The web.config fi les in the subdirectories or subfolders can override these settings or inherit the settings (such as the 10 -minute session timeout). The configuration settings for virtual directories are independent of the physical directory structure. Unless the manner in which the virtual directories are organized is exclusively specifi ed, configuration problems can result. Note that these inheritance/override rules can be blocked in most cases by using the allowOverride = “false” mechanism shown earlier in Listing 33 -3.

detecting configuration file changes ASP.NET automatically detects when configuration fi les, such as machine.config or web.config, are changed. This logic is implemented based on listening for fi le- change notification events provided by the operating system. When an ASP.NET application is started, the configuration settings are read and stored in the ASP.NET cache. A fi le dependency is then placed on the entry within the cache in the machine.config and/or web.config configuration fi le. When the configuration fi le update is detected in the machine.config, ASP.NET creates a new application domain to service new requests. The old application domain is destroyed as soon as it completes servicing all its outstanding requests.

configuration file format The main difference between machine.config and web.config is the fi lename. Other than that, their schemas are the same. Configuration fi les are divided into multiple groups. The root-level XML element in a configuration fi le is named . This pseudo web.config fi le has a section to control ASP.NET, as shown in Listing 33 - 6. lisTing 33- 6: a pseudo web.config file


Values within brackets [ ] have unique values within the real configuration file.

The root element in the XML configuration fi le is always . Each of the section handlers and settings are optionally wrapped in a . A provides an organizational function within the configuration fi le. It allows you to organize configuration into unique groups — for instance, the section group is used to identify areas within the configuration fi le specific to ASP.NET.

www.it-ebooks.info

Common Configuration Settings  ❘ 

1245

The section is the mechanism to group the configuration section handlers associated with each configuration section. When you want to create your own section handlers, you must declare them in the section. The section has a configuration handler that is set to System.Web.Caching.HttpModulesSection, and the section has a configuration handler that is set to System.Web.SessionState.SessionStateSection classes, as shown in Listing 33-7. Listing 33-7:  HTTP module configuration setting from the machine.config file


Common Configuration Settings The ASP.NET applications depend on a few common configuration settings. These settings are common to both the web.config and machine.config files. In this section, you look at some of these common configuration settings.

Connection Strings In ASP.NET 1.0 and 1.1, all the connection string information was stored in the section. However, ever since ASP.NET 2.0, a section called was included that stores all kinds of connection-string information. Even though storing connection strings in the element works fine, it poses the following challenges: ➤➤

When connection strings are stored in appSettings section, it is impossible for a data-aware control such as SqlCacheDependency or MembershipProvider to discover the information.

➤➤

Securing connection strings using cryptographic algorithms is a challenge.

➤➤

Last, but not least, this feature does not apply to ASP.NET only; rather, it applies to all the .NET application types including Windows Forms, Web Services, and so on.

Because the connection-string information is stored independently of the appSettings section, it can be retrieved using the strongly typed collection method ConnectionStrings. Listing 33-8 gives an example of how to store connection strings. Listing 33-8:  Storing a connection string

Listing 33-9 shows how to retrieve the connection string (ExampleConnection) in your code.

www.it-ebooks.info

1246  ❘  Chapter 33   Configuration

Listing 33-9:  Retrieving a connection string

VB

Public Sub Page_Load (sender As Object, e As EventArgs) ... Dim dbConnection as New SqlConnection(ConfigurationManager.ConnectionStrings("ExampleConnection") .ConnectionString) ... End Sub

C#

public void Page_Load (Object sender, EventArgs e) { ... SqlConnection dbConnection = new SqlConnection(ConfigurationManager.ConnectionStrings["ExampleConnection"] .ConnectionString); ... }

This type of construction has a lot of power. Instead of hard-coding your connection strings into each and every page within your ASP.NET application, you can store one instance of the connection string centrally (in the web.config file, for example). Now, if you have to make a change to this connection string, you can make this change in only one place rather than in multiple places.

Configuring Session State Because Web-based applications utilize the stateless HTTP protocol, you must store the application-specific state or user-specific state where it can persist. The Session object is the common store where user-specific information is persisted. Session store is implemented as a Hashtable and stores data based on key/value pair combinations. ASP.NET 1.0 and 1.1 had the capability to persist the session store data in InProc, StateServer, and SqlServer. Since ASP.NET 2.0, you’ve been able to add one more capability called Custom. The Custom setting gives the developer a lot more control regarding how the session state is persisted in a permanent store. For example, out of the box ASP.NET does not support storing session data on non-Microsoft databases such as Oracle, DB2, or Sybase. If you want to store the session data in any of these databases or in a custom store such as an XML file, you can implement that by writing a custom provider class. (See the section “Custom State Store” later in this chapter and Chapter 21 to learn more about the session state features in ASP.NET 4.) You can configure the session information using the element as presented in Listing 33-10. Listing 33-10:  Configuring session state

The following list describes some of the attributes for the element shown in the preceding code: ➤➤

mode: Specifies whether the session information should be persisted. The mode setting supports five options: Off, InProc, StateServer, SQLServer, and Custom. The default option is InProc.

➤➤

cookieless: Specifies whether HTTP cookieless Session key management is supported.

www.it-ebooks.info

Common Configuration Settings  ❘ 

1247

➤➤

timeout: Specifies the Session lifecycle time. The timeout value is a sliding value; at each request, the timeout period is reset to the current time plus the timeout value. For example, if the timeout value is 20 minutes and a request is received at 10:10 am, the timeout occurs at 10:30 am.

➤➤

stateConnectionString: When mode is set to StateServer, this setting is used to identify the TCP/IP address and port to communicate with the Windows Service providing state management.

➤➤

stateNetworkTimeout: Specifies the timeout value (in seconds) while attempting to store state in an out-of-process session store such as StateServer.

➤➤

sqlConnectionString: When mode is set to SQLServer, this setting is used to connect to the SQL Server database to store and retrieve session data.

Web Farm Support Multiple Web servers working as a group are called a Web farm. If you would like to scale out your ASP.NET application into multiple servers inside a Web farm, ASP.NET supports this kind of deployment out of the box. However, the session data needs to be persisted in an out-of-process session state such as StateServer or SQLServer.

State Server Both StateServer and SQLServer support the out-of-process session state. However, the StateServer stores all the session information in a Windows Service, which stores the session data in memory. Using this option, if the server that hosts the session state service goes down in the Web farm, all the ASP.NET clients that are accessing the Web site fail; there is no way to recover the session data. You can configure the session state service using the Services dialog available by choosing Start ➪ Settings ➪ Control Panel ➪ Administrative Tools ➪ Computer Management if you are using Windows XP, and Start ➪ Control Panel ➪ System and Security ➪ Administrative Tools ➪ Services if you are using Windows 7 (as shown in Figure 33-2).

Figure 33-2

www.it-ebooks.info

1248



chaPTer 33 conFigurAtion

Alternatively, you can start the session state service by using the command prompt and entering the net start command, like this: C:\Windows\Microsoft.NET\Framework\v4.0.21006\> net start aspnet_state The ASP.NET State Service service is starting. The ASP.NET State Service service was started successfully.

All compatible versions of ASP.NET share a single state service instance, which is the service installed with the highest version of ASP.NET. For example, if you have installed ASP.NET 4 on a server where ASP.NET 2.0 and 1.1 are already running, the ASP.NET 4 installation replaces the ASP.NET 2.0’s state server instance. The ASP.NET 4 service is guaranteed to work for all previous compatible versions of ASP.NET.

SQL Server When you choose the SQLServer option, session data is stored in a Microsoft SQL Server database. Even if SQL Server goes down, the built-in SQL Server recovery features enable you to recover all the session data. Configuring ASP.NET to support SQL Server for session state is just as simple as configuring the Windows Service. The only difference is that you run a T- SQL script that ships with ASP.NET, InstallSqlState .sql. The T- SQL script that uninstalls ASP.NET SQL Server support, called UninstallSqlState.sql, is also included. The install and uninstall scripts are available in the Framework folder. Listing 33 -11 shows an example of using the SQL Server option. lisTing 33-11: Using the sQlserver option for session state

ASP.NET accesses the session data stored in SQL Server via stored procedures. By default, all the session data is stored in the Temp DB database. However, you can modify the stored procedures so they are stored in tables in a full-fledged database other than Temp DB. Even though the SQL Server–based session state provides a scalable use of session state, it could become the single point of failure. This is because SQL Server session state uses the same SQL Server database for all applications in the same ASP.NET process. This problem has been fi xed ever since ASP.NET 2.0, and you can configure different databases for each application. Now you can use the aspnet_regsql.exe utility to configure SQL Server-based session state for all your applications. However, if you are looking for a solution for older .NET Frameworks, a fi x is available at http://support.microsoft.com/kb/836680.

www.it-ebooks.info

Common Configuration Settings  ❘ 

1249

Because the connection strings are stored in the strongly typed mode, the connection string information can be referenced in other parts of the configuration file. For example, when configuring session state to be stored in SQL Server, you can specify the connection string in the section, and then you can specify the name of the connection string in the element, as shown in Listing 33-12. Listing 33-12:  Configuring session state with a connection string

Custom State Store The session state in ASP.NET 4 is based on a pluggable architecture with different providers that inherit the SessionStateStoreProviderBase class. If you want to create your own custom provider or use a third-party provider, you must set the mode to Custom. You specify the custom provider assembly that inherits the SessionStateStoreProviderBase class, as shown in Listing 33-13. Listing 33-13:  Working with your own session state provider

In the previous example, you have configured the session state mode as Custom because you have specified the provider name as CustomStateProvider. From there, you add the provider element and include the type of the provider with namespace and class name.

www.it-ebooks.info

1250



chaPTer 33 conFigurAtion

You can read more about the provider model and custom providers in Chapters 11 and 12.

compilation configuration ASP.NET supports the dynamic compilation of ASP.NET pages, Web services, HttpHandlers, ASP.NET application fi les (such as the Global.asax fi le), source fi les, and so on. These fi les are automatically compiled on demand when they are fi rst required by an ASP.NET application. Any changes to a dynamically compiled fi le causes all affected resources to become automatically invalidated and recompiled. This system enables developers to quickly develop applications with a minimum of process overhead because they can just click Save to immediately cause code changes to take effect within their applications. The ASP.NET 1.0 and 1.1 features are extended in ASP.NET 2.0, 3.5, and 4 to account for other fi le types, including class fi les. You can configure the ASP.NET compilation settings using the section in the web.config or machine.config files. The ASP.NET engine compiles the page when necessary and saves the generated code in code cache. This cached code is used when executing the ASP.NET pages. Listing 33 -14 shows the syntax for the section. lisTing 33-14: The section

www.it-ebooks.info

Common Configuration settings

❘ 1251

Now take a more detailed look at these attributes: ➤

batch: Specifies whether the batch compilation is supported. The default value is true.



maxBatchSize: Specifies the maximum number of pages/classes that can be compiled into a single batch. The default value is 1000.



maxBatchGeneratedFileSize: Specifies the maximum output size of a batch assembly compilation.

The default value is 1000KB. ➤

batchTimeout: Specifies the amount of time (minutes) granted for batch compilation to occur. If this timeout elapses without compilation being completed, an exception is thrown. The default value is 15 minutes.



optimizeCompilations: Specifies whether dynamic compilation compiles the entire site or only the items that have changed. When set to False (the default), the entire site will recompile when top -level fi les are changed; a setting of True will recompile only the changed fi les.



debug: Specifies whether to compile production assemblies or debug assemblies. The default is false.



defaultLanguage: Specifies the default programming language, such as VB or C#, to use in dynamic compilation files. Language names are defined using the child element. The default value is VB.



explicit: Specifies whether the Microsoft Visual Basic code compile option is explicit. The default

is true. ➤

numRecompilesBeforeAppRestart: Specifies the number of dynamic recompiles of resources that can occur before the application restarts.



strict: Specifies the setting of the Visual Basic strict compile option.



urlLinePragmas: Instructs the compiler if it should use URLs rather than physical paths (which is the default behavior).



tempDirectory: Specifies the directory to use for temporary fi le storage during compilation. By default, ASP.NET creates the temp fi le in the [WinNT\Windows]\Microsoft.NET\Framework\ [version]\Temporary ASP.NET Files folder.



assemblies: Specifies assemblies that are used during the compilation process.



codeSubDirectories: Specifies an ordered collection of subdirectories containing fi les compiled at runtime. Adding the codeSubDirectories section creates separate assemblies.



buildproviders: Specifies a collection of build providers used to compile custom resource fi les.



folderLevelBuildProviders: Specifies a collection of build providers used to compile custom

resource fi les in specific folders. ➤

expressionBuilders: Specifies a collection of resource strings to be utilized during the compilation

process.

Browser capabilities Identifying and using the browser’s capabilities is essential for Web applications. The browser capabilities component was designed for the variety of desktop and device browsers, such as Microsoft’s Internet Explorer, Google’s Chrome, Safari, Netscape, Opera, Blackberry, iPhone, and so on. The element enables you to specify the configuration settings for the browser capabilities component. The element can be declared at the machine, site, application, and subdirectory level. The HttpBrowserCapabilities class contains all the browser properties. The properties can be set and retrieved in this section. The element has been deprecated since ASP.NET 2.0 and now you should instead focus on using .browser files.

www.it-ebooks.info

1252



chaPTer 33 conFigurAtion

When a request is received from a browser, the browser capabilities component identifies the browser’s capabilities from the request headers. For each browser, compile a collection of settings relevant to applications. These settings may either be statically configured or gathered from request headers. Allow the application to extend or modify the capabilities settings associated with browsers and to access values through a strongly typed object model. The ASP.NET mobile capabilities depend on the browser capabilities component. In ASP.NET 4, all the browser capability information is represented in browser defi nition fi les. The browser defi nitions are stored in *.browser fi le types and specified in XML format. A single fi le may contain one or more browser defi nitions. The *.browser fi les are stored in the Config\Browsers subdirectory of the Framework installation directory (for example, [WinNT\Windows]\Microsoft.NET\Framework\ v4.0.21006\CONFIG\Browsers), as shown in Figure 33 -3. Application-specific browser defi nition fi les are stored in the /Browsers subdirectory of the application. In ASP.NET 1.0 and 1.1, the browser cap information was stored in the machine .config and web.config files themselves.

figure 33-3

The browser defi nition fi le format defi nes each browser as an entity, self- contained in a XML element. Each browser has its own ID that describes a class of browser and its parent class. The root node of a browser defi nition fi le is the element and multiple browser entries identified using the id attribute of the element. Listing 33 -15 shows a section of the ie.browser fi le.

www.it-ebooks.info

Common Configuration settings

❘ 1253

lisTing 33-15: Content of ie.browser file

name="browser" value="IE" /> name="layoutEngine" value="Trident" /> name="layoutEngineVersion value="${layoutVersion}" /> name="extra" value="${extra}" /> name="isColor" value="true" /> name="letters" value="${letters}" /> name="majorversion" value="${major}" /> name="minorversion" value="${minor}" /> name="screenBitDepth" value="8" /> name="type" value="IE${major}" /> name="version" value="${version}" />

...

The id attribute of the element uniquely identifies the class of browser. The parentID attribute of the element specifies the unique ID of the parent browser class. Both the id and the parentID are required values. Before running an ASP.NET application, the framework compiles all the browser definitions into an assembly and installs the compilation in GAC. When the browser definition files at the system level are modified, they do not automatically reflect the change in each and every ASP.NET application. Therefore, updating this information becomes the responsibility of the developer or the installation tool. You can send the updated browser information to all the ASP.NET applications by running the aspnet_regbrowsers.exe utility provided by the framework. When the aspnet_regbrowsers.exe utility is called, the browser information is recompiled and the new assembly is stored in the GAC; this assembly is reused by all the ASP.NET applications. Nevertheless, browser definitions at the application level are automatically parsed and compiled on demand when the application is started. If any changes are made to the application’s /Browsers directory, the application is automatically recycled.

custom errors When the ASP.NET application fails, the ASP.NET page can show the default error page with the source code and line number of the error. However, this approach has a few problems: ➤

The source code and error message might not make any sense to a less- experienced end user.



If the same source code and the error messages are displayed to a hacker, subsequent damage could result.

www.it-ebooks.info

1254  ❘  Chapter 33   Configuration

Displaying too much error information could provide important implementation details that in most cases you want to keep from the public. Figure 33-4 shows an example.

Figure 33-4

However, ASP.NET provides excellent infrastructure to prevent this kind of error information. The section provides a means for defining custom error messages in an ASP.NET application. The syntax is as follows:

➤➤

defaultRedirect: Specifies the default URL to which the client browser should be redirected if an

error occurs. This setting is optional. ➤➤

mode: Specifies whether the status of the custom errors is enabled, disabled, or shown only to remote machines. The possible values are On, Off, and RemoteOnly. On indicates that the custom errors are enabled. Off indicates that the custom errors are disabled. RemoteOnly indicates that the custom errors are shown only to remote clients.

➤➤

customErrors: The section supports multiple sub-elements that are used to define custom errors. Each sub-element can include a statusCode attribute and a URL.

Authentication In Chapter 20, you see the authentication process in detail. In this section, you can review configurationspecific information. Authentication is a process that verifies the identity of the user and establishes the identity between the server and a request. Because HTTP is a stateless protocol, the authentication information is persisted somewhere in the client or the server; ASP.NET supports both of these. You can store the server-side information in Session objects. When it comes to client side, you have many options:

www.it-ebooks.info

Common Configuration settings



Cookies



ViewState



URL



Hidden fields

❘ 1255

ASP.NET supports following authentication methods out of the box: ➤

Windows authentication



Passport authentication



Forms Authentication

If you want to disable authentication, you can use the setting mode = “None”:

Windows authentication ASP.NET relies on IIS’s infrastructure to implement Windows authentication, and Windows authentication enables you to authenticate requests using Windows Challenge/Response semantics. When the Web server receives a request, it initially denies access to the request (which is a challenge). This triggers the browser to pop up a window to collect the credentials; the request responds with a hashed value of the Windows credentials, which the server can then choose to authenticate. To implement Windows authentication, you configure the appropriate Web site or virtual directory using IIS. You can then use the element to mark the Web application or virtual directory with Windows authentication. Listing 33 -16 illustrates this process. lisTing 33-16: setting authentication to Windows authentication

You can declare the element only at the machine, site, or application level. Any attempt to declare it in a configuration file at the subdirectory or page level results in a parser error message.

Passport authentication ASP.NET relies on the Passport SDK to implement Passport authentication. It is important to note that even though you can apply these settings in ASP.NET, this technology has been deprecated by Microsoft. Passport is a subscription-based authentication mechanism that allows end users to remember a single username/password pair across multiple Web applications that implement Passport authentication. ASP.NET authenticates users based on the credentials presented by users. The Passport service sends a token back to authenticate. The token is stored in a site-specific cookie after it has been authenticated with login .passport.com. Using the redirectUrl attribute of the authentication option, you can control how non-authenticated Passport users are directed, as in the following example:

www.it-ebooks.info

1256  ❘  Chapter 33   Configuration

Forms Authentication Forms authentication is the widely used authentication mechanism. To configure forms authentication you use the section along with the subsection. Listing 33-17 shows the structure of an section that deals with forms authentication in the configuration file. Listing 33-17:  The section working with forms authentication

Each attribute is shown in detail in the following list: ➤➤

name: Specifies the name of the HTTP authentication ticket. The default value is .ASPXAUTH.

➤➤

loginUrl: Specifies the URL to which the request is redirected if the current request doesn’t have a valid authentication ticket.

➤➤

protection: Specifies the method used to protect cookie data. Valid values are All, None, Encryption, and Validation. ➤➤

Encryption: Specifies that content of the cookie is encrypted using TripleDES or DES cryptography algorithms in the configuration file. However, the data validation is not done on the cookie.

➤➤

Validation: Specifies that content of the cookie is not encrypted, but validates that the cookie data has not been altered in transit.

➤➤

All: Specifies that content of the cookie is protected using both data validation and encryption. The configured data validation algorithm is used based on the element, and Triple DES is used for encryption. The default value is All, and it indicates the

➤➤

None: Specifies no protection mechanism is applied on the cookie. Web applications that do

highest protection available. not store any sensitive information and potentially use cookies for personalization can look at this option. When None is specified, both encryption and validation are disabled.

www.it-ebooks.info

Common Configuration settings

❘ 1257



timeout: Specifies cookie expiration time in terms of minutes. The timeout attribute is a sliding value, which expires n minutes from the time the last request was received. The default value is 30 minutes.



path: Specifies the path to use for the issued cookie. The default value is / to avoid difficulties with mismatched case in paths because browsers are strictly case-sensitive when returning cookies.



requireSSL: Specifies whether Forms authentication should happen in a secure HTTPS connection.



slidingExpiration: Specifies whether valid cookies should be updated periodically when used. When this option is set to False, a ticket is good for only the duration of the period for which it is issued, and a user must re-authenticate even during an active session.



cookieless: Specifies whether cookieless authentication is supported. Supported values are UseCookies, UseUri, Auto, and UseDeviceProfile. The default value is UseDeviceProfile.



defaultUrl: Specifies the default URL used by the login control to control redirection after authentication.



enableCrossAppRedirects: When set to true, this allows for redirection to URLs that are not in

the current application. ➤

ticketCompatibilityMode: By default, the Framework20 setting uses local time for the ticket expiration date, while setting it to Framework40 will use UTC.



domain: Specifies the domain name string to be attached in the authentication cookie. This attribute is particularly useful when the same authentication cookie is shared among multiple sites across the domain.

Having the loginUrl be an SSL URL ( https://) is strongly recommended to keep secure credentials secure from prying eyes.

anonymous identity Many application types require the capability to work with anonymous users, although this is especially true for e- commerce Web applications. In these cases, your site must support both anonymous and authenticated users. When anonymous users are browsing the site and adding items to a shopping cart, the Web application needs a way to uniquely identify these users. For example, if you look at busy e- commerce Web sites such as Amazon.com or BN.com, they do not have a concept called anonymous users. Rather these sites assign a unique identity to each user. In ASP.NET 1.0 and 1.1, no out- of-the box feature existed to enable a developer to achieve this identification of users. Most developers used SessionID to identify users uniquely. They experienced a few pitfalls inherent in this method. Since the introduction of ASP.NET 2.0, ASP.NET has had anonymous identity support using the section in the configuration fi le. Listing 33 -18 shows the configuration section settings. lisTing 33-18: Working with anonymous identification in the configuration file
continues

www.it-ebooks.info

1258  ❘  Chapter 33   Configuration

Listing 33-18  (continued) cookieSlidingExpiration = "true" [true|false] cookieProtection = "Validation" [None|Validation|Encryption|All] cookieless="UseCookies" [UseUri|UseCookies|AutoDetect|UseDeviceProfile] domain="" [String] />


The enabled attribute within the section specifies whether the anonymous access capabilities of ASP.NET are enabled. The other attributes are comparable to those in the section from Listing 33-17. When you are working with anonymous identification, the possibility exists that the end user will have cookies disabled in their environments. When cookies are not enabled by the end user, the identity of the user is then stored in the URL string within the end user’s browser.

Authorization The authorization process verifies whether a user has the privilege to access the resource he is trying to request. ASP.NET supports both file and URL authorization. The authorization process dictated by an application can be controlled by using the section within the configuration file. The section, as presented in Listing 33-19, can contain subsections that either allow or deny permission to a user, a group of users contained within a specific role in the system, or a request that is coming to the server in a particular fashion (such as an HTTP GET request). Optionally, you can also use the section to grant special authorization permission to only a particular folder or file within the application. Listing 33-19:  Authorization capabilities from the configuration file

URL Authorization The URL authorization is a service provided by URLAuthorizationModule (inherited from HttpModule) to control the access to resources such as .aspx files. The URL authorization is very useful if you want to allow or deny certain parts of your ASP.NET application to certain people or roles. For example, you might want to restrict the administration part of your ASP.NET application only to administrators and deny access to others. You can achieve this task very easily with URL authorization. URL Authorization can be configurable based on the user, the role, or HTTP verbs such as HTTP GET request or HTTP POST request. You can configure URL authorization in the web.config file with and attributes. For example, the code in Listing 33-20 shows how you can allow the user Bubbles and deny the groups Sales and Marketing access to the application. Listing 33-20:  Allowing and denying entities from the section

www.it-ebooks.info

Common Configuration settings

❘ 1259



The and elements support users, roles, and verbs values. As you can see from the previous code example, you can add multiple users and groups by separating them with commas. Two special characters, an asterisk (*) and a question mark (?), are supported by URLAuthorizationModule. The asterisk symbol represents all users (anonymous and registered) and the question mark represents only anonymous users. The following code example in Listing 33 -21 denies access to all anonymous users and grants access to anyone contained within the Admin role. lisTing 33-21: denying anonymous users

You can also grant or deny users or groups access to certain HTTP methods. In the example in Listing 33 -22, access to the HTTP GET method is denied to the users contained within the Admin role, whereas access to the HTTP POST method is denied to all users. lisTing 33-22: denying users and roles by verb

file authorization Constructing the authorization section within the configuration fi le so that what is specified can be applied to a specific fi le or directory using the element is possible. For example, suppose you have a root directory called Home within your application and nested within that root directory you have a subdirectory called Documents. Suppose you want to allow access to the Documents subdirectory only to those users contained within the Admin role. Listing 33 -23 illustrates this scenario. lisTing 33-23: Granting access to the documents subdirectory for the admin role

The ASP.NET application does not verify the path specifi ed in the path attribute. If the given path is invalid, ASP.NET does not apply the security setting.

www.it-ebooks.info

1260  ❘  Chapter 33   Configuration

You can also set the security for a single file as presented in Listing 33-24. Listing 33-24:  Granting access to a specific file for the Admin role

Locking-Down Configuration Settings ASP.NET’s configuration system is quite flexible in terms of applying configuration information to a specific application or folder. Even though the configuration system is flexible, in some cases you may want to limit the configuration options that a particular application on the server can control. For example, you could decide to change the way in which the ASP.NET session information is stored. This lock-down process can be achieved using the attributes allowOverride and allowDefinition, as well as the path attribute. Listing 33-25 illustrates this approach. A section in this machine.config file identifies the path “Default Web Site/ExampleApplication” and allows any application to override the setting through the use of the allowOverride attribute. Listing 33-25:  Allowing a section to be overridden in a lower configuration file

The trace attribute can be overridden because the allowOverride attribute is set to true. You are able to override the tracing setting in the ExampleApplication’s web.config file and enable the local element, thereby overriding the settings presented in Listing 33-25. However, if you had written the attribute as allowOverride = “false” in the section of the machine.config file, the web.config file for ExampleApplication is unable to override that specific setting.

ASP.NET Page Configuration When an ASP.NET application has been deployed, the section of the configuration file enables you to control some of the default behaviors for each and every ASP.NET page. These behaviors include options such as whether you should buffer the output before sending it or whether session state should be enabled for the entire application. Listing 33-26 shows an example of using the section. Listing 33-26:  Configuring the section
www.it-ebooks.info

Common Configuration Settings  ❘ 

1261

enableViewState="true" [true|false] enableViewStateMac="false" [true|false] autoEventWireup="true" [true|false] smartNavigation="false" [true|false] maintainScrollPositionOnPostBack="false" [true|false] masterPageFile="" [String] theme="" [String] styleSheetTheme="" [String] maxPageStateFieldLength="-1" [number] pageBaseType="System.Web.UI.Page" [String] userControlBaseType="System.Web.UI.UserControl" [String] pageParserFilterType="" [String] compilationMode="Always" [Auto|Never|Always] viewStateEncyptionMode="Auto" [Auto|Always|Never] asyncTimeout="45" [in Seconds][number] clientIDMode="AutoID" [Inherit|AutoID|Predictable|Static] controlRenderingCompatibilityVersion="4.0" [Version] validateRequest="true" [true|false] >


The following list gives you some of the ASP.NET page configuration information elements in detail: ➤➤

buffer: Specifies whether the requests must be buffered on the server before they are sent to the client.

➤➤

enableSessionState: Specifies whether the session state for the current ASP.NET application should be enabled. The possible values are true, false, or readonly. The readonly value means that the application can read the session values but cannot modify them.

➤➤

enableViewState: Specifies whether the ViewState is enabled for all the controls. If the application does not use ViewState, you can set the value to false in the application’s web.config file.

➤➤

autoEventWireup: Specifies whether ASP.NET can automatically wire-up common page events such

as Load or Error. ➤➤

smartNavigation: Smart navigation is a feature that takes advantage of IE as a client’s browser to

prevent the redrawing that occurs when a page is posted back to itself. Using smart navigation, the request is sent through an IFRAME on the client, and IE redraws only the sections of the page that have changed. By default, this option is set to false. When it is enabled, it is available only to Internet Explorer browsers; all other browsers get the standard behavior. ➤➤

maintainScrollPositionOnPostback: Specifies whether or not to return the user to the exact same position on the page after the postback occurs. If set to False (the default), the user is returned to the top of the page.

➤➤

masterPageFile: Identifies the master page for the current ASP.NET application. If you want to apply the master page template to only a specific subset of pages (such as pages contained within a specific folder of your application), you can use the element within the web.config file:

www.it-ebooks.info

1262  ❘  Chapter 33   Configuration

➤➤

theme: Specifies the name of the theme to use for the page. Themes are covered in Chapter 6.

➤➤

styleSheetTheme: Defines the theme to use after control declaration.

➤➤

maxPageStateFieldLength: If set to a positive number, ASP.NET will separate out the ViewState into chucks that are smaller than the defined size. The default value is -1, meaning all the ViewState comes down in one piece.

➤➤

pageBaseType: Specifies the base class for all the ASP.NET pages in the current ASP.NET application. By default, this option is set to System.Web.UI.Page. However, if you want all ASP.NET pages to inherit from some other base class, you can change the default via this setting.

➤➤

userControlBaseType: Specifies the base class for all the ASP.NET user controls in the current ASP.NET application. The default is System.Web.UI.UserControl. You can override the default option using this element.

➤➤

validateRequest: Specifies whether ASP.NET should validate all the incoming requests that

are potentially dangerous, such as the cross-site script attack and the script injection attack. This feature provides out-of-the-box protection against cross-site scripting and script injection attacks by automatically checking all parameters in the request, ensuring that their content does not include HTML elements. For more information about this setting, visit www.asp.net/faq/ RequestValidation.aspx. ➤➤

namespaces: Optionally, you can import a collection of assemblies that can be included in the precompilation process.

➤➤

compilationMode: Specifies how ASP.NET should compile the current Web application. Supported values are Never, Always, and Auto. A setting of compilationMode = “Never” means that

the pages should never be compiled. A part error occurs if the page has constructs that require compilation. Setting compilationMode = “Always” means that the pages are always compiled. When you set compilationMode = “Auto”, ASP.NET does not compile the pages if that is possible. ➤➤

viewStateEncryptionMode: Specifies whether or not to encrypt the ViewState.

➤➤

asyncTimeout: Specifies the number of seconds that the page should wait for an asynchronous handler to finish during an asynchronous operation.

➤➤

clientIDMode: Specifies the algorithm that should be used to create the ClientID values for server controls on your page. The default setting is AutoID while the default value for the server controls is Inherit.

➤➤

controlRenderingCompatibilityVersion: Specifies the version of ASP.NET to use when controls

render their HTML.

Include Files Unlike ASP.NET 1.0 and 1.1, ASP.NET 2.0 to 4 support include files in both the machine.config and the web.config files. When configuration content is to be included in multiple places or inside the location elements, an include file is an excellent way to encapsulate the content. Any section in a configuration file can include content from a different file using the configSource attribute in the section. The value of the attribute indicates a virtual relative filename to the include file. Listing 33-27 is an example of such a directive. Listing 33-27:  Adding additional content to the web.config file

www.it-ebooks.info

Common Configuration settings

❘ 1263

The configuration include fi les can contain information that applies to a single section, and a single include fi le cannot contain more than one configuration section or a portion of a section. If the configSource attribute is present, the section element in the source fi le should not contain any other attribute or any child element. Nevertheless, the include fi le is not a full configuration fi le. It should contain only the include section, as presented in Listing 33 -28. lisTing 33-28: The systemWeb.config file

The configSource attribute cannot be nested. An include fi le cannot nest another fi le inside it using the configSource attribute. When an ASP.NET configuration file is changed, the application is restarted at runtime. When an external include file is used within the configuration file, the configuration reload happens without restarting the application.

configuring asP.neT runtime settings The general configuration settings are those that specify how long a given ASP.NET resource, such as a page, is allowed to execute before being considered timed- out. The other settings specify the maximum size of a request (in kilobytes) or whether to use fully qualified URLs in redirects. To specify these settings you use the section within a configuration fi le. The element is applied at the ASP.NET application at the folder level. Listing 33 -29 shows the default values used in the section. lisTing 33-29: The section

enabling and disabling asP.neT applications The enable attribute specifies whether the current ASP.NET application is enabled. When set to false, the current ASP.NET application is disabled, and all the clients trying to connect to this site receive the HTTP 404 — File Not Found exception. This value should be set only at the machine or application level. If you set this value in any other level (such as subfolder level), it is ignored. This great feature enables the administrators to bring down the application for whatever reason without starting or stopping IIS. The default value is true.

www.it-ebooks.info

1264



chaPTer 33 conFigurAtion

Outside of this setting, you can also take applications offline quickly by simply placing an App_Offline.htm file in the root of your application. This .htm file does not need to actually contain anything (it will not make any difference). Just having the file in the root directory causes the application domain to come down, and all requests to the application get a Page Not Found error.

fully Qualified redirect Urls The useFullyQualifiedRedirectUrl attribute specifies whether the client-side redirects should include the fully qualified URL. When you are programming against the mobile devices, some devices require specifying fully qualified URLs. The default value is false.

request Time - out The executionTimeout setting specifies the timeout option for an ASP.NET request time- out. The value of this attribute is the amount of time in seconds during which a resource can execute before ASP.NET times out the request. The default setting is 110 seconds. If you have a particular ASP.NET page or Web service that takes longer than 110 seconds to execute, you can extend the time limit in the configuration.

Maximum request length The maxRequestLength attribute specifies the maximum fi le-size upload accepted by ASP.NET runtime. For example, if the ASP.NET application is required to process huge fi les, then this setting is the one you will want to change. The default is 4096. This number represents kilobytes (KB or around 4MB). Web applications are prone to attacks these days. The attacks range from a script injection attack to a denial of service (DoS) attack. The DoS is a typical attack that bombards the Web server with requests for large fi les. This huge number of requests ultimately brings down the Web server. The maxRequestLength attribute could save you from a DoS attack by setting a restriction on the size of requests.

Buffer Uploads In ASP.NET 1.0 or 1.1, when an HTTP post is made (either a normal ASP.NET form post, fi le upload, or an XMLHTTP client-side post), the entire content is buffered in memory. This works out fi ne for smaller posts. However, when memory-based recycling is enabled, a large post can cause the ASP.NET worker process to recycle before the upload is completed. To avoid the unnecessary worker process recycling, ASP.NET 4 includes a setting called requestLengthDiskThreshold. This setting enables an administrator to configure the fi le upload buffering behavior without affecting the programming model. Administrators can configure a threshold below which requests will be buffered into memory. After a request exceeds the limit, it is transparently buffered on disk and consumed from there by whatever mechanism is used to consume the data. The valid values for this setting are numbers between 1 and Int32.MaxSize in KB. When fi le buffering is enabled, the fi les are uploaded to the codegen folder. The default path for the codegen folder is the following: [WinNT\Windows]\Microsoft.NET\Framework\[version]\Temporary ASP.NET Files\ [ApplicationName]

The fi les are buffered using a random name in a subfolder within the codegen folder called Uploads. The location of the codegen folder can be configured on a per-application basis using the tempDirectory attribute of the section. This is not a change in ASP.NET; rather it is an internal change. When an ASP.NET 1.0 or 1.1 application is migrated to the .NET Framework 2.0, 3.5, or 4, the ASP.NET application automatically takes advantage of this feature.

www.it-ebooks.info

Common Configuration settings

❘ 1265

Thread Management ASP.NET runtime uses free threads available in its thread pool to fulfi ll requests. The minFreeThreads attribute indicates the number of threads that ASP.NET guarantees is available within the thread pool. The default number of threads is eight. For complex applications that require additional threads to complete processing, this attribute simply ensures that the threads are available and that the application will not be blocked while waiting for a free thread to schedule more work. The minLocalRequestFreeThreads attribute controls the number of free threads dedicated for local request processing; the default is four.

application Queue length The appRequestQueueLimit attribute specifies the maximum number of requests that ASP.NET queues for the current ASP.NET application. ASP.NET queues requests when it does not have enough free threads to process them. The minFreeThreads attribute specifies the number of free threads the ASP.NET application should maintain, and this setting affects the number of items stored in the queue. When the number of requests queued exceeds the limit set in the appRequestQueueLimit setting, all the incoming requests are rejected and an HTTP 503 - Server Too Busy error is thrown back to the browser.

output Caching The enableKernelOutputCache specifies whether the output caching is enabled at the IIS kernel level (Http.sys). At present, this setting applies only to Web servers IIS6 and higher.

configuring the asP.neT worker Process When a request for an ASP.NET page is received by IIS, it passes the request to an unmanaged DLL called aspnet_isapi.dll. The aspnet_isapi.dll further passes the request to a separate worker process, aspnet_wp.exe if you are working with IIS5, which runs all the ASP.NET applications. With IIS6 and higher, however, all the ASP.NET applications are run by the w3wp.exe process. The ASP.NET worker process can be configured using the section in the machine.config fi le. All the configuration sections talked about so far are read by managed code. On the other hand, the section is read by the aspnet_isapi.dll unmanaged DLL. Because the configuration information is read by an unmanaged DLL, the changed process model information is applied to all ASP.NET applications only after an IIS restart. The code example in Listing 33 -30 shows the default format for the section. lisTing 33-30: The structure of the element
continues

www.it-ebooks.info

1266



chaPTer 33 conFigurAtion

lisTing 33-30 (continued) memoryLimit="percent" cpuMask="num" webGarden="true|false" userName="username" password="password" logLevel="All|None|Errors" clientConnectedCheck="hrs:mins:secs|Infinite" responseDeadlockInterval="hrs:mins:secs|Infinite" responseRestartDeadlockInterval="hrs:mins:secs|Infinite" comAuthenticationLevel="Default|None|Connect|Call| Pkt|PktIntegrity|PktPrivacy" comImpersonationLevel="Default|Anonymous|Identify| Impersonate|Delegate" maxWorkerThreads="num" maxIoThreads="num" autoConfig="true|false" minWorkerThreads="num" minIoThreads="num" serverErrorMessageFile="" pingFrequency="hrs:mins:secs|Infinite" pingTimeout="hrs:mins:secs|Infinite" maxAppDomains="number" />

The following section looks at each of these attributes in more detail: ➤

enable: Specifies whether the process model is enabled. When set to false, the ASP.NET applications run under IIS’s process model.

When ASP.NET is running under IIS6 or higher in native mode, the IIS6 or higher process model is used and most of the section within the configuration file is simply ignored. The autoConfig and requestQueueLimit attributes are still applied in this case. ➤

timeout: Specifies how long the worker process lives before a new worker process is created to replace the current worker process. This value can be extremely useful if a scenario exists where the application’s performance starts to degrade slightly after running for several weeks, as in the case of a memory leak. Rather than your having to manually start and stop the process, ASP.NET can restart automatically. The default value is Infinite.



idleTimeout: Specifies how long the worker process should wait before it is shut down. You can shut down the ASP.NET worker process automatically using the idleTimeout option. The default value is Infinite. You can also set this value to a time using the format, HH:MM:SS:.



shutdownTimeout: Specifies how long the worker process is given to shut itself down gracefully before ASP.NET calls the Kill command on the process. Kill is a low-level command that forcefully

removes the process. The default value is 5 seconds. ➤

requestLimit: Specifies when the ASP.NET worker process should be recycled after a certain number of requests are served. The default value is Infinite.



requestQueueLimit: Instructs ASP.NET to recycle the worker process if the limit for queued requests is exceeded. The default setting is 5000.



memoryLimit: Specifies how much physical memory the worker process is allowed to consume before it is considered to be misbehaving or leaking memory. The default value is 60 percent of available physical memory.

www.it-ebooks.info

Common Configuration Settings  ❘ 

1267

➤➤

username and password: By default, all ASP.NET applications are executed using the ASPNET identity. If you want an ASP.NET application to run with a different account, you can provide the username and the password pair using these attributes.

➤➤

logLevel: Specifies how the ASP.NET worker process logs events. The default setting is to log errors only. However, you can also disable logging by specifying None or you can log everything using All. All the log items are written to the Windows Application Event Log.

➤➤

clientConnectedCheck: The clientConnectedCheck setting enables you to check whether the

client is still connected at timed intervals before performing work. The default setting is 5 seconds. ➤➤

responseDeadlockInterval: Specifies how frequently the deadlock check should occur. A deadlock is considered to exist when requests are queued and no responses have been sent during this interval. After a deadlock, the process is restarted. The default value is 3 minutes.

➤➤

responseRestartDeadlockInterval: Specifies, when a deadlock is detected by the runtime, how long the runtime should wait before restarting the process. The default value is 9 minutes.

➤➤

comAuthenticationLevel: Controls the level of authentication for DCOM security. The default is set to Connect. Other values are Default, None, Call, Pkt, PktIntegrity, and PktPrivacy.

➤➤

comImpersonationLevel: Controls the authentication level for COM security. The default is set to Impersonate. Other values are Default, Anonymous, Identify, and Delegate.

➤➤

webGarden: Specifies whether Web Garden mode is enabled. The default setting is false. A Web Garden lets you host multiple ASP.NET worker processes on a single server, thus providing the application with better hardware scalability. Web Garden mode is supported only on multiprocessor servers.

➤➤

cpuMask: Specifies which processors should be affinities to ASP.NET worker processes when webGarden = “true”. The cpuMask is a hexadecimal value. The default value is all processors, shown as 0xFFFFFFFF.

➤➤

maxWorkerThreads: Specifies the maximum number of threads that exist within the ASP.NET worker process thread pool. The default is 20.

➤➤

maxIoThreads: Specifies the maximum number of I/O threads that exist within the ASP.NET worker process. The default is 20.

➤➤

autoConfig: Specifies whether to configure the ASP.NET application’s performance settings.

➤➤

minWorkerThreads: Specifies the minimum number of threads that exist within the ASP.NET worker process thread pool. The default is 1.

➤➤

minIoThreads: Specifies the minimum number of I/O threads that exist within the ASP.NET worker process. The default is 1.

➤➤

serverErrorMessageFile: Specifies the page to use for content for the error message rather than the default “Server Unavailable” message.

➤➤

pingFrequency: Specifies the time interval at which the ISAPI extension pings the worker process to determine whether it is running.

➤➤

pingTimeout: Specifies the time interval at which a worker process is restarted after not responding.

➤➤

maxAppDomains: Sets the absolute maximum number of application domains for one process.

Running Multiple Web Sites with Multiple Versions of Framework In the same context as the ASP.NET worker process, multiple Web sites within the given Web server can host multiple Web sites, and each of these sites can be bound to a particular version of a .NET Framework. This is typically done using the aspnet_regiis.exe utility. The aspnet_regiis.exe utility is shipped with each version of the framework. This utility has multiple switches. Using the -s switch allows you to install the current version of the .NET Framework runtime on a given Web site. Listing 33-31 shows how to install .NET Framework version 1.1 on the ExampleApplication Web site.

www.it-ebooks.info

1268  ❘  Chapter 33   Configuration

Listing 33-31:  Installing .NET Framework version 1.1 on the ExampleApplication Web site C:\WINDOWS\Microsoft.NET\Framework\v1.1.4322> aspnet_regiis -s W3SVC/1ROOT/ExampleApplication

Storing Application-Specific Settings Every Web application must store some application-specific information for its runtime use. The section of the web.config file provides a way to define custom application settings for an ASP.NET application. The section can have multiple sub-elements. Its syntax is as follows:

The sub-element supports two attributes: ➤➤

key: Specifies the key value in an appSettings hash table

➤➤

value: Specifies the value in an appSettings hash table

Listing 33-32 shows how to store an application-specific connection string. The key value is set to ApplicationInstanceID, and the value is set to the ASP.NET application instance and the name of the server on which the application is running. Listing 33-32:  Application instance information

Programming Configuration Files In ASP.NET 1.0 and 1.1 versions of the Framework provided APIs that enabled you only to read information from the configuration file. You had no way to write information into the configuration file because no out-of-the-box support was available. However, some advanced developers wrote their own APIs to write the information back to the configuration files. Because the web.config file is an XML file, developers were able to open configuration file using the XmlDocument object, modify the settings, and write it back to the disk. Even though this approach worked fine, the way to access the configuration settings were not strongly typed. Therefore, validating the values was always a challenge. However, ASP.NET today includes APIs (ASP.NET Management Objects) to manipulate the configuration information settings in machine.config and web.config files. ASP.NET Management Objects provide a strongly typed programming model that addresses targeted administrative aspects of a .NET Web Application Server. They also govern the creation and maintenance of the ASP.NET Web configuration. Using the ASP.NET Management Objects, you can manipulate the configuration information stored in the configuration files in the local or remote computer. These can be used to script any common administrative tasks or the writing of installation scripts. All the ASP.NET Management Objects are stored in the System.Configuration and System.Web .Configuration namespaces. You can access the configuration using the WebConfigurationManager class. The System.Configuration.Configuration class represents a merged view of the configuration settings from the machine.config and hierarchical web.config files. The System.Configuration and System.Web.Configuration namespaces have multiple classes that enable you to access nearly all the settings available in the configuration file. The main difference between System.Configuration and System.Web.Configuration namespaces is that the System.Configuration namespace contains all the classes that apply to all the .NET applications. On the other hand, the System.Web.Configuration namespace contains the classes that are applicable only to ASP.NET Web applications. Table 33-1 shows the important classes in System.Configuration and their uses.

www.it-ebooks.info

Common Configuration Settings  ❘ 

1269

Table 33-1 Class Name

Purpose

Configuration

Enables you to manipulate the configuration stored in the local computer or a remote one.

ConfigurationElementCollection

Enables you to enumerate the child elements stored inside the configuration file.

AppSettingsSection

Enables you to manipulate the section of the configuration file.

ConnectionStringsSettings

Enables you to manipulate the section of the configuration file.

ProtectedConfigurationSection

Enables you to manipulate the section of the configuration file.

ProtectedDataSection

Enables you to manipulate the section of the configuration file.

Table 33-2 shows some of the classes from the System.Web.Configuration and their uses. Table 33-2 Class Name

Purpose

AuthenticationSection

Enables you to manipulate the section of the configuration file.

AuthorizationSection

Enables you to manipulate the section of the configuration file.

CompilationSection

Enables you to manipulate the section of the configuration file.

CustomErrorsSection

Enables you to manipulate the section of the configuration file.

FormsAuthenticationConfiguration

Enables you to manipulate the section of the ­configuration file.

GlobalizationSection

Enables you to manipulate the section of the configuration file.

HttpHandlersSection

Enables you to manipulate the section of the configuration file.

HttpModulesSection

Enables you to manipulate the section of the configuration file.

HttpRuntimeSection

Enables you to manipulate the section of the configuration file.

MachineKeySection

Enables you to manipulate the section of the configuration file.

MembershipSection

Enables you to manipulate the section of the configuration file.

PagesSection

Enables you to manipulate the section of the ­configuration file.

ProcessModelSection

Enables you to manipulate the section of the configuration file.

WebPartsSection

Enables you to manipulate the section of the ­configuration file.

www.it-ebooks.info

1270  ❘  Chapter 33   Configuration

All the configuration classes are implemented based on simple object-oriented based architecture that has an entity class that holds all the data and a collection class that has methods to add, remove, enumerate, and so on. Start your configuration file programming with a simple connection string enumeration, as shown in the following section.

Enumerating Connection Strings In a Web application, you can store multiple connection strings. Some of them are used by the system and the others may be application-specific. You can write a very simple ASP.NET application that enumerates all the connection strings stored in the web.config file, as shown in Listing 33-33. Listing 33-33:  The web.config file

As shown in Listing 33-33, one application setting points to the symbol server, and one connection string is stored in the web.config file. Use the ConnectionStrings collection of the System.Web.Configuration .WebConfigurationManager class to read the connection strings, as shown in Listing 33-34. Listing 33-34:  Enum.aspx

VB

C#

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) GridView1.DataSource = System.Web.Configuration.WebConfigurationManager.ConnectionStrings GridView1.DataBind() End Sub protected void Page_Load(object sender, EventArgs e) { GridView1.DataSource = System.Web.Configuration.WebConfigurationManager.ConnectionStrings; GridView1.DataBind(); }

As shown in Listing 33-34, you’ve bound the ConnectionStrings property collection of the WebConfigurationManager class into the GridView control. The WebConfigurationManager class returns an instance of the Configuration class and the ConnectionStrings property is a static (shared in Visual Basic) property. Therefore, you are just binding the property collection into the GridView control. Figure 33-5 shows the list of connection strings stored in the ASP.NET application.

www.it-ebooks.info

Common Configuration Settings  ❘ 

1271

Figure 33-5

Adding a connection string at runtime is also a very easy task. If you do it as shown in Listing 33-35, you get an instance of the configuration object. Then you create a new connectionStringSettings class. You add the new class to the collection and call the update method. Listing 33-35 shows examples of this in both VB and C#. Listing 33-35:  Adding a connection string

VB

Protected Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) ' Get the file path for the current web request Dim webPath As String = Request.ApplicationPath Try ' Get configuration object of the current web request Dim config As Configuration = System.Web.Configuration.WebConfigurationManager.OpenWebConfiguration (webPath) ' Create new connection setting from text boxes Dim newConnSetting As New ConnectionStringSettings(txtName.Text, txtValue.Text, txtProvider.Text) ' Add the connection string to the collection config.ConnectionStrings.ConnectionStrings.Add(newConnSetting) ' Save the changes config.Save() Catch cEx As ConfigurationErrorsException lblStatus.Text = "Status: " + cEx.ToString() Catch ex As System.UnauthorizedAccessException ' The ASP.NET process account must have read/write access to the directory lblStatus.Text = "Status: " + "The ASP.NET process account must have read/write access to the directory" Catch eEx As Exception lblStatus.Text = "Status: " + eEx.ToString() End Try ShowConnectionStrings()

continues

www.it-ebooks.info

1272  ❘  Chapter 33   Configuration

Listing 33-35  (continued) End Sub

C#

protected void Button1_Click(object sender, EventArgs e) { // Get the file path for the current web request string webPath = Request.ApplicationPath; // Get configuration object of the current web request Configuration config = System.Web.Configuration.WebConfigurationManager.OpenWebConfiguration(webPath); // Create new connection setting from text boxes ConnectionStringSettings newConnSetting = new ConnectionStringSettings(txtName.Text, txtValue.Text, txtProvider.Text); try { // Add the connection string to the collection config.ConnectionStrings.ConnectionStrings.Add(newConnSetting); // Save the changes config.Save(); } catch (ConfigurationErrorsException cEx) { lblStatus.Text = "Status: " + cEx.ToString(); } catch (System.UnauthorizedAccessException uEx) { // The ASP.NET process account must have read/write access to the directory lblStatus.Text = "Status: " + "The ASP.NET process account must have" + "read/write access to the directory"; } catch (Exception eEx) { lblStatus.Text = "Status: " + eEx.ToString(); } // Reload the connection strings in the list box ShowConnectionStrings(); }

Manipulating a machine.config File The OpenMachineConfiguration method of the System.Configuration.ConfigurationManager class provides a way to manipulate the machine.config file. The OpenMachineConfiguration method is a static method. Listing 33-36 shows a simple example that enumerates all the section groups stored in the machine .config file. As shown in this listing, you’re getting an instance of the configuration object using the OpenMachineConfiguration method. Then you are binding the SectionGroups collection with the GridView control. Listing 33-36:  Configuration groups from machine.config

VB

Protected Sub Button2_Click(ByVal sender As Object, ByVal e As System.EventArgs) ' List all the SectionGroups in machine.config file Dim configSetting As Configuration = System.Configuration.ConfigurationManager.OpenMachineConfiguration()

www.it-ebooks.info

Common Configuration Settings  ❘ 

1273

GridView1.DataSource = configSetting.SectionGroups GridView1.DataBind() End Sub

C#

protected void Button2_Click(object sender, EventArgs e) { // List all the SectionGroups in machine.config file Configuration configSetting = System.Configuration.ConfigurationManager.OpenMachineConfiguration(); GridView1.DataSource = configSetting.SectionGroups; GridView1.DataBind(); }

In the same way, you can list all the configuration sections using the Sections collections, as shown in Listing 33-37. Listing 33-37:  Configuration sections from machine.config

VB

C#

Protected Sub Button2_Click(ByVal sender As Object, ByVal e As System.EventArgs) ' List all the SectionGroups in machine.config file Dim configSetting As Configuration = _ System.Configuration.ConfigurationManager.OpenMachineConfiguration() GridView1.DataSource = configSetting.Sections GridView1.DataBind() End Sub protected void Button2_Click(object sender, EventArgs e) { // List all the SectionGroups in machine.config file Configuration configSetting = System.Configuration.ConfigurationManager.OpenMachineConfiguration(); GridView1.DataSource = configSetting.Sections; GridView1.DataBind(); }

Manipulating web.config from Remote Servers The ASP.NET Management Objects also provide a way to read configuration information from remote servers. For example, if you want to manipulate the Expense Web application’s configuration file located on the imaginary Optra.Microsoft.com site, you can do so as shown in Listing 33-38. Listing 33-38:  Manipulating a remote server’s web.config file

VB

' Connect to the web application Expense on Optra.Microsoft.com server Dim configSetting As Configuration = System.Web.Configuration.WebConfigurationManager.OpenWebConfiguration _ ("/Expense", "1", "Optra.Microsoft.com") Dim section As System.Configuration.ConfigurationSection = configSetting.GetSection("appSettings") Dim element As KeyValueConfigurationElement = _ CType(configSetting.AppSettings.Settings("keySection"), KeyValueConfigurationElement) If Not element Is Nothing Then Dim value As String = "New Value"

continues

www.it-ebooks.info

1274  ❘  Chapter 33   Configuration

Listing 33-38  (continued) element.Value = value Try config.Save() Catch ex As Exception Response.Write(ex.Message) End Try End If

C#

// Connect to the web application Expense on Optra.Microsoft.com server Configuration configSetting = System.Web.Configuration.WebConfigurationManager.OpenWebConfiguration ("/Expense", "1", "Optra.Microsoft.com"); ConfigurationSection section = configSetting.GetSection("appSettings"); KeyValueConfigurationElement element = configSetting.AppSettings.Settings["keySection"]; if (element != null) { string value = "New Value"; element.Value = value; try { configSetting.Save(); } catch (Exception ex) { Response.Write(ex.Message); } }

The code in Listing 33-38 demonstrates how to give the machine address in the constructor method to connect to the remote server. Then you change a particular appSettings section to a new value and save the changes.

Protecting Configuration Settings When ASP.NET 1.0 was introduced, all the configuration information was stored in human-readable, clear-text format. However since ASP.NET 1.1 a new way has been available to store the configuration information inside the registry using the Data Protection API (or DPAPI). For example, Listing 33-39 shows how you can store a process model section’s username and password information inside the registry. Listing 33-39:  Storing the username and password in the registry and then referencing these

settings in the machine.config file



www.it-ebooks.info

Common Configuration settings

❘ 1275

ASP.NET 1.0 also acquired this functionality as a fi x. Visit the following URL for more information: http://support.microsoft.com/kb/329290. ASP.NET 4 includes a system for protecting sensitive data stored in the configuration system. It uses industry-standard XML encryption to encrypt specified sections of configuration that contain any sensitive data. Developers often feel apprehensive about sticking sensitive items such as connection strings, passwords, and more in the web.config fi le. For this reason, ASP.NET makes possible the storing of these items in a format that is not readable by any human or machine process without intimate knowledge of the encryption techniques and keys used in the encryption process. One of the most encrypted items in the web.config fi le is the section. Listing 33 - 40 shows an example of a web.config fi le with an exposed connection string. lisTing 33-40: a standard connection string exposed in the web.config file

In this case, you might want to encrypt this connection string to the database. To accomplish this, the install of ASP.NET provides a tool called aspnet_regiis.exe. You fi nd this tool at C:\WINDOWS\Microsoft .NET\Framework\v4.0.21006. To use this tool to encrypt the section, open a command prompt and navigate to the specified folder using cd C:\WINDOWS\Microsoft.NET\Framework\ v4.0.21006. Another option is to just open the Visual Studio 2010 Command Prompt. After you are in one of these environments, you use the syntax presented in Listing 33 - 41 to encrypt the section. lisTing 33-41: encrypting the section aspnet_regiis-pe "connectionString"-app "/EncryptionExample"

Running this bit of script produces the results presented in Figure 33 - 6.

www.it-ebooks.info

1276  ❘  Chapter 33   Configuration

Figure 33-6

Looking over the script used in the encryption process, you can see that the –pe command specifies the section in the web.config file to encrypt, whereas the –app command specifies which application to actually work with. If you look back at the web.config file and examine the encryption that occurred, you see something similar to the code in Listing 33-42. Listing 33-42:  The encrypted section of the web.config file Rsa Key 0s99STuGx+CdDXmWaOVc0prBFA65 Yub0VxDS7nOSQ79AAYcxKG7Alq1o M2BqZGSmElc7c4w93qgZn0CNN VHGhDLE1OjHPV942HaYhcddK5 5XY5j7L3WSEJFj68E2Ng9+EjU o+oAGJVhCAuG8owQBaQ2Bri3+ tfUB/Q8LpOW4kP8= O3/PtxajkdVD/5TLGddc1/ C8cg8RFYl8MiRXh71h4ls=

www.it-ebooks.info

Common Configuration Settings  ❘ 

1277





Now when you work with a connection string in your ASP.NET application, ASP.NET itself automatically decrypts this section to utilize the values stored. Looking at the web.config file, you can see a subsection within the section that exposes a username and password as clear text. This is also something that you might want to encrypt in order to keep it away from prying eyes. Because it is a subsection, you use the script presented in Listing 33-43. Listing 33-43:  Encrypting the section aspnet_regiis-pe "system.web/authentication"-app "/EncryptionExample"

This code gives you the partial results presented in Listing 33-44. Listing 33-44:  The encrypted section of the web.config file Rsa Key GzTlMc89r3ees9EoMedFQrLo3FI5p3JJ9DMONWe ASIww89UADkihLpmzCUPa3YtiCfKXpodr3Xt3RI 4zpveulZs5gIZUoX8aCl48U89dajudJn7eoJqai m6wuXTGI5XrUWTgYdELCcFCloW1c+eGMRBZpNi9 cir4xkkh2SsHBDE= 3MpRm+Xs+x5YsndH20lZau8/t+3RuaGv5+nTFoRXaV tweKdgrAVeB+PXnTjydq/u4LBXKMKmHzaBtxrqEHRD mNZgigLWVtfIRQ6P8cgBwtdIFhFmjm3B4tg/rA8dpJ

continues

www.it-ebooks.info

1278  ❘  Chapter 33   Configuration

Listing 33-44  (continued) ivDav2kDPp+SZ6yZ9LJzhBIe9TdJvwBQ9gJTGNVRft QOvdvH8c4KwYfiwZa9WCqys9WOZmw6g1a5jdW3hM// jiMizY1MwCECVh+T+y+f/vpP0xCkoKT9GGgHRMMrQd PqHUd5s7rUYp1ijQgrh1oPIXr6mx/XtzdXV8bQiEsg CLhsqphoVVwxkvmUKEmDQdOzdrB4sqmKgoHR3wCPyB npH58g==


After you have sections of your web.config file encrypted, you need a process to decrypt these sections to their original unencrypted values. To accomplish this task, you use the aspnet_regiis tool illustrated in Listing 33-45. Listing 33-45:  Decrypting the section in the web.config file aspnet_regiis-pd "connectionString"-app "/EncryptionExample"

Running this script returns the encrypted values to original values.

Editing Configuration Files So far in this chapter, you have learned about configuration files and what each configuration entry means. Even though the configuration entries are in an easy, human-readable XML format, editing these entries can be cumbersome. To help with editing, Microsoft ships three tools: ➤➤

Visual Studio 2010 IDE

➤➤

Web Site Administration Tool

➤➤

ASP.NET Snap-In for IIS 6.0 or Windows 7’s Internet Information Services (IIS) Manager

One of the nice capabilities of the Visual Studio 2010 IDE is that it supports IntelliSense-based editing for configuration files, as shown in Figure 33-7.

Figure 33-7

www.it-ebooks.info

Creating Custom sections

❘ 1279

The Visual Studio 2010 IDE also supports XML element syntax checking, as shown in Figure 33 -8. XML element syntax checking and IntelliSense for XML elements are accomplished using the XSD-based XML validation feature available for all the XML files inside Visual Studio 2010. The configuration XSD file is located at :\Program Files\Microsoft Visual Studio 10.0\Xml\Schemas\DotNetConfig.xsd.

figure 33-8

The Visual Studio 2010 IDE also adds two new useful features via the XML toolbar options that can help you with formatting the configuration settings: ➤

Reformat Selection: This option reformats the current XML notes content.



Format the whole document: This option formats the entire XML document.

The Web Site Administration Tool and the ASP.NET Snap -In for IIS 6.0 or Window 7 ’s IIS Manager allow you to edit the configuration entries without knowing the XML element names and their corresponding values. Chapter 35 covers these tools in more detail.

creaTing cusTom secTions In addition to using the web.config fi le as discussed, you can also extend it and add your own custom sections to the fi le that you can make use of just as with the other sections. One way of creating custom sections is to use some built-in handlers that enable you to read key-value pairs from the .config fi le. All three of the following handlers are from the System.Configuration namespace: ➤

NameValueFileSectionHandler: This handler works with the current section of the web.config fi le. You are able to use this handler to create new sections of the configuration fi le that behave in the same manner as the section.



DictionarySectionHandler: This handler works with a dictionary collection of key-value pairs.

www.it-ebooks.info

1280  ❘  Chapter 33   Configuration

➤➤

SingleTagSectionHandler: This handler works from a single element in the configuration file and allows you to read key-value pairs that are contained as attributes and values.

Next, this chapter looks at each of these handlers and some programmatic ways to customize the configuration file.

Using the NameValueFileSectionHandler Object If you are looking to create a custom section that behaves like the section of the web .config file, then using this handler is the way to go. Above the section of the web.config file, make a reference to the NameValueFileSectionHandler object, along with the other default references you will find in an ASP.NET 4 application. Listing 33-46 shows this additional reference. Listing 33-46:  Creating your own custom section of key-value pairs in the web.config file


After you have made this reference to the System.Configuration.NameValueFileSectionHandler object and have given it a name (in this case, MyCompanyAppSettings), then you can create a section in your web.config file that makes use of this reference, as illustrated in Listing 33-47. Listing 33-47:  Creating your own custom key-value pair section in the web.config

After you have this code in place within your web.config file, you can then programmatically get access to this section, as illustrated in Listing 33-48. Listing 33-48:  Getting access to your custom section in the web.config file Dim nvc As NameValueCollection = New NameValueCollection() nvc = System.Configuration.ConfigurationManager.GetSection("MyCompanyAppSettings")

VB

C#

Response.Write(nvc("Key1") + "
") Response.Write(nvc("Key2")) NameValueCollection nvc = new NameValueCollection(); nvc = ConfigurationManager.GetSection("MyCompanyAppSettings") as NameValueCollection; Response.Write(nvc["Key1"] + "
"); Response.Write(nvc["Key2"]);

www.it-ebooks.info

Creating Custom Sections  ❘ 

1281

For this to work, you must import the System.Collections.Specialized namespace into the file, because this is where you will find the NameValueCollection object.

Using the DictionarySectionHandler Object The DictionarySectionHandler works nearly the same as the NameValueFileSectionHandler. The difference, however, is that the DictionarySectionHandler returns a HashTable object instead of returning an Object. Listing 33-49 presents this handler. Listing 33-49:  Making a reference to the DictionarySectionHandler object


With this configuration setting in place, you can then make the same MyCompanyAppSettings section in the web.config file, as shown in Listing 33-50. Listing 33-50:  Creating a custom key-value pair section in the web.config file

Now that the web.config file is ready, you can call the items from code using the Configuration API, as illustrated in Listing 33-51. Listing 33-51:  Getting access to your custom section in the web.config file Dim ht As Hashtable = New Hashtable() ht = System.Configuration.ConfigurationManager.GetSection("MyCompanyAppSettings")

VB

C#

Response.Write(ht("Key1") + "
") Response.Write(ht("Key2")) Hashtable ht = new Hashtable(); ht = ConfigurationManager.GetSection("MyCompanyAppSettings") as Hashtable; Response.Write(ht["Key1"] + "
"); Response.Write(ht["Key2"]);

Using the SingleTagSectionHandler Object The SingleTagSectionHandler works almost the same as the previous NameValueFileSectionHandler and DictionarySectionHandler. However, this object looks to work with a single element that contains the key-value pairs as attributes.

www.it-ebooks.info

1282  ❘  Chapter 33   Configuration

Listing 33-52 presents this handler. Listing 33-52:  Making a reference to the SingleTagSectionHandler object


With this configuration setting in place, you can make a different MyCompanyAppSettings section in the web.config file, as presented in Listing 33-53. Listing 33-53:  Creating a custom key-value pair section in the web.config file

Now that the web.config file is complete, you can call the items from code using the Configuration API, as illustrated in Listing 33-54. Listing 33-54:  Getting access to your custom section in the web.config file Dim ht As Hashtable = New Hashtable() ht = System.Configuration.ConfigurationManager.GetSection("MyCompanyAppSettings")

VB

C#

Response.Write(ht("Key1") + "
") Response.Write(ht("Key2")) Hashtable ht = new Hashtable(); ht = ConfigurationManager.GetSection("MyCompanyAppSettings") as Hashtable; Response.Write(ht["Key1"] + "
"); Response.Write(ht["Key2"]);

Using Your Own Custom Configuration Handler You can also create your own custom configuration handler. To do this, you first must create a class that represents your section in the web.config file. In your App_Code folder, create a class called MyCompanySettings. Listing 33-55 shows this class. Listing 33-55:  The MyCompanySettings class Public Class MyCompanySettings Inherits ConfigurationSection

VB

_ Public ReadOnly Property Key1() As String Get Return MyBase.Item("Key1").ToString()

www.it-ebooks.info

Creating Custom Sections  ❘ 

End Get End Property _ Public ReadOnly Property Key2() As String Get Return MyBase.Item("Key2").ToString() End Get End Property End Class

C#

using System.Configuration; public class MyCompanySettings : ConfigurationSection { [ConfigurationProperty("Key1", DefaultValue = "This is the value of Key 1", IsRequired = false)] public string Key1 { get { return this["Key1"] as string; } } [ConfigurationProperty("Key2", IsRequired = true)] public string Key2 { get { return this["Key2"] as string; } } }

You can see that this class inherits from the ConfigurationSection and the two properties that are created using the ConfigurationProperty attribute. You can use a couple of attributes here, such as the DefaultValue, IsRequired, IsKey, and IsDefaultCollection. After you have this class in place, you can configure your application to use this handler, as illustrated in Listing 33-56. Listing 33-56:  Making a reference to the MyCompanySettings object


You can now use this section in your web.config file, as illustrated in Listing 33-57. Listing 33-57:  Creating your own custom key-value pair section in the web.config file

www.it-ebooks.info

1283

1284  ❘  Chapter 33   Configuration

Using this configuration you can programmatically access this key-value pair from code, as illustrated in Listing 33-58. Listing 33-58:  Accessing a custom section in the web.config file Dim cs As MyCompanySettings = New MyCompanySettings() cs = ConfigurationManager.GetSection("MyCompanySettings")

VB

C#

Response.Write(cs.Key1 + "
") Response.Write(cs.Key2) MyCompanySettings cs = ConfigurationManager.GetSection("MyCompanySettings") as MyCompanySettings; Response.Write(cs.Key1 + "
"); Response.Write(cs.Key2);

Summary In this chapter, you have seen the ASP.NET configuration system and learned how it does not rely on the IIS metabase. Instead, ASP.NET uses an XML configuration system that is human-readable. You also looked at the two different ASP.NET XML configuration files: ➤➤

machine.config

➤➤

web.config

The machine.config file applies default settings to all Web applications on the server. However, if the server has multiple versions of the framework installed, the machine.config file applies to a particular framework version. On the other hand, a particular Web application can customize or override its own configuration information using web.config files. Using a web.config file, you can also configure the applications on an application-by-application or folder-by-folder basis. Next, you looked at some typical configuration settings that you can apply to an ASP.NET application, such as configuring connection strings, session state, browser capabilities, and so on. Then you looked at an overview of ASP.NET Admin Objects and learned how to program configuration files. Finally, you learned how to protect the configuration section using cryptographic algorithms.

www.it-ebooks.info

34

instrumentation whaT’s in This chaPTer? ➤

Monitoring your applications with the event log and performance counters



Utilizing health monitoring for your applications

Many ASP.NET developers do more than just build an application and walk away. They defi nitely think about how the application will behave after it is deployed. Instrumentation is the task that developers undertake to measure and monitor their ASP.NET applications. Depending on the situation, some instrumentation operations occur at design time, whereas others are ongoing processes that begin at runtime. ASP.NET 4 gives you greater capability to apply instrumentation techniques to your applications. You will fi nd that the ASP.NET Framework includes a large series of performance counters, the capability to work with the Windows Event Tracing system, possibilities for application tracing (covered in Chapter 23), and the most exciting part of this discussion — a health monitoring system that allows you to log a number of different events over an application’s lifetime. You can monitor a deployed application in several ways. First, you learn how to work with the Windows event log. Then the chapter moves on to a discussion of performance counters, application tracing, and health monitoring in turn. By the end of the chapter, you will have a better understanding of what is going on within your ASP.NET applications. Instrumentation is key to this task.

working wiTh The evenT log When working with Visual Studio 2010, you can get to the event log in the Server Explorer of the IDE in a few different ways. You can get to the event log section in the Server Explorer by expanding the view of the server you want to work with (by clicking the plus sign next to the server) until you see the Event Logs section. You also have the option to right- click the Event Logs node and select Launch Event Viewer from the list of available options. This selection displays the same Event Viewer you are familiar with. From the Event Viewer or from the Server Explorer you can work directly with events recorded to your system. Another option available from the Server Explorer is to expand the Event Logs node of the tree view in the Server Explorer so that you can see the additional nodes, such as Application, Security, and System. If you are on Windows 7, you will see a series of additional event categories. Expanding any of these

www.it-ebooks.info

1286  ❘  Chapter 34   Instrumentation

sub-nodes allows you to see all the events that have been registered in the event log. These events are arranged by type, which makes browsing for specific events rather easy, as illustrated in Figure 34-1.

Reading from the Event Log Reading and writing events to and from the event log from your .NET application is possible. If you are interested in reading events from the event log, you can do so rather simply by using the EventLog object that is provided by .NET in the System.Diagnostics namespace. To see an example of using this object, you can create an ASP.NET page that displays all the entries contained within a specified event log. To create it, you need a DropDownList control, a Button control, and a GridView control. In Visual Studio .NET 2002 and 2003, a Components tab was located within the Toolbox, and you could simply drag and drop the Event Log component onto your design surface in order to start working with it. Figure 34-1 Since Visual Studio 2005, you won’t find this tab within the Toolbox, but finding the objects you need still isn’t too hard. Listing 34-1 shows the simple ASP.NET page that enables you to easily display the contents of the event log. Listing 34-1:  Displaying the contents of the event logs within the browser <%@ Page Language="VB" %> <%@ Import Namespace="System.Diagnostics" %>

VB

Working with Event Logs
Application Security System



www.it-ebooks.info

Working with the event log

❘ 1287



C#

<%@ Page Language="C#" %> <%@ Import Namespace="System.Diagnostics" %>

Note that you must run this code sample with credentials that have administrative rights for it to work. For this code to work, you import the System.Diagnostics namespace if you are not interested in fully qualifying your declarations. After you do so, you can create an instance of the EventLog object to give you access to the Source property. In assigning a value to this property, you use the SelectedItem.Text property from the DropDownList control on the page. Next, you can provide all the EventLog entries as the data source value to the GridView control. Finally, you bind this data source to the GridView. In the end, you get something similar to the results illustrated in Figure 34 -2.

figure 34-2

www.it-ebooks.info

1288  ❘  Chapter 34   Instrumentation

As you can see, pulling all the events from the event log and writing them to the browser screen is simple. The next section looks at writing values back to the event log.

Writing to the Event Log Not only can you read from the event logs, but you can write to them as well. This capability can be quite handy if you want to record specific events in an event log. Table 34-1 provides a short description of the main event logs available to you. Windows 7 has other event categories, but these are the main event categories that you will use. Table 34-1 Event Log

Description

Application

Enables you to record application-specific events, including whether a certain event was fired, a page was loaded, or a customer made a specific purchase

Security

Enables you to track security-related issues such as security changes and breaches

System

Enables you to track system-specific items such as issues that arise with components or drivers

From your code, you can write to any of the event logs defined in the preceding table as well as to any custom event logs that you might create. To accomplish this task, create an example ASP.NET page that contains a multiline TextBox control and a Button control. On the Button1_Click event, you register the text you want placed in the text box directly into the Application event log. Your ASP.NET page should be similar to what is presented in Listing 34-2. Listing 34-2:  Writing to the Application event log <%@ Page Language="VB" %> <%@ Import Namespace="System.Diagnostics" %>

VB

Working with Event Logs




www.it-ebooks.info

Working with the Event Log  ❘ 

1289



 


C#

<%@ Page Language="C#" %> <%@ Import Namespace="System.Diagnostics" %>

Again, for this code to work, you must import the System.Diagnostics namespace. In the Page_Load event of the page, ASP.NET is checking whether the event source exists for My Application. If no such source exists in the Application event log, it is created using the CreateEventSource() method. el.CreateEventSource("My Application", "Application")

The first parameter of the method takes the name of the source that you are creating. The second parameter of this method call takes the name of the event log that you are targeting. After this source has been created, you can start working with the EventLog object to place an entry into the system. First, the EventLog object is assigned a source. In this case, it is the newly created My Application. Using the WriteEntry() method, you can write to the specified event log. You can also assign the source and the message within the WriteEntry() method in the following manner: el.WriteEntry("My Application", TextBox1.Text);

The ASP.NET page produces something similar to what is illustrated in Figure 34-3.

Figure 34-3

www.it-ebooks.info

1290  ❘  Chapter 34   Instrumentation

After you have all this code in place, you can look in the Event Viewer and see your entry listed in the Application event log. Figure 34-4 illustrates what happens when you double-click the entry. Later in this chapter, you see some of the automatic ways in which ASP.NET can record events for you in the event log and in some other data stores (such as Microsoft’s SQL Server). Next, it is time to turn your attention to working with performance counters.

Using Performance Counters Figure 34-4 Utilizing performance counters is important if you want to monitor your applications as they run. What exactly is monitored is up to you. A plethora of available performance counters are at your disposal in Windows and you will find that more than 60 counters are specific to ASP.NET.

Viewing Performance Counters Through an Administration Tool You can see these performance counters by opening the Performance dialog found in the Control Panel and then Administration Tools if you are using Windows XP. If you are using Windows 7, select Control Panel ➪ System and Security ➪ Administrative Tools ➪ Performance Monitor. Figure 34-5 shows the dialog opened in Windows 7.

Figure 34-5

www.it-ebooks.info

Using Performance Counters  ❘ 

1291

Clicking the plus sign in the menu enables you to add more performance counters to the list. A number of ASP.NET–specific counters appear in the list shown in Figure 34-6. The following list details some of the ASP.NET–specific performance counters that are at your disposal, along with a definition of the counter (also available by selecting the Show Description check box from within the dialog). ➤➤

Application Restarts. Number of times the application has been restarted during the Web server’s lifetime.

➤➤

Applications Running. Number of currently running Web applications.

➤➤

Audit Failure Events Raised. Number of audit failures in the application since it was started.

➤➤

Audit Success Events Raised. Number of audit successes in the application since it was started.

➤➤

Error Events Raised. Number of error events raised since the application was started.

➤➤

Infrastructure Error Events Raised. Number of HTTP error events raised since the application was started.

➤➤

Request Error Events Raised. Number of runtime error events raised since the application was started.

➤➤

Request Execution Time. The number of milliseconds it took to execute the most recent request.

➤➤

Request Wait Time. The number of milliseconds the most recent request was waiting in the queue.

➤➤

Requests Current. The current number of requests, including those that are queued, currently executing, or waiting to be written to the client. Under the ASP.NET process model, when this counter exceeds the requestQueueLimit defined in the processModel configuration section, ASP .NET begins rejecting requests.

➤➤

Requests Disconnected. The number of requests disconnected because of communication errors or user terminations.

➤➤

Requests Queued. The number of requests waiting to be processed.

➤➤

Requests Rejected. The number of requests rejected because the request queue was full.

➤➤

State Server Sessions Abandoned. The number of sessions that have been explicitly abandoned.

➤➤

State Server Sessions Active. The current number of sessions currently active.

➤➤

State Server Sessions Timed Out. The number of sessions timed out.

➤➤

State Server Sessions Total. The number of sessions total.

➤➤

Worker Process Restarts. The number of times a worker process has restarted on the machine.

➤➤

Worker Processes Running. The number of worker processes running on the machine.

Figure 34-6

These are some of the performance counters for just the ASP.NET category. Here you will find categories for other ASP.NET–specific items such as: ➤➤

ASP.NET

➤➤

ASP.NET Applications

➤➤

ASP.NET Apps v4.0.21006

➤➤

ASP.NET State Service

➤➤

ASP.NET v4.0.21006

www.it-ebooks.info

1292  ❘  Chapter 34   Instrumentation

Performance counters can give you a rather outstanding view of what is happening in your application. The data retrieved by a specific counter is not a continuous thing because the counter is really taking a snapshot of the specified counter every 400 milliseconds, so be sure to take that into account when analyzing the data produced.

Building a Browser-Based Administrative Tool In addition to viewing the performance counters available to you through the Performance dialog, you can also get at the performance counter values programmatically. This is possible by working with the System.Diagnostics namespace in the .NET Framework. This namespace gives you access to performance counter–specific objects such as the PerformanceCounterCategory and PerformanceCounter objects. To show you how to work with these objects, this next example creates an ASP.NET page that enables you to view any value from a performance counter directly in the browser. To accomplish this task, create a basic ASP.NET page that includes three DropDownList controls, a Button control, and a Label control. This gives you the results presented in Figure 34-7. Listing 34-3 shows the code required for Figure 34-7.

Figure 34-7 Listing 34-3:  Working with performance counters in ASP.NET

VB

<%@ Page Language="VB" %> <%@ Import Namespace="System.Diagnostics" %> <%@ Import Namespace="System.Collections.Generic" %> Working with Performance Counters
Performance Object:


Performance Counter:


Instances:






C#

<%@ Page Language="C#" %> <%@ Import Namespace="System.Diagnostics" %> <%@ Import Namespace="System.Collections.Generic" %>

To make this code work, you have to deal with only a couple of performance-counter objects such as the PerformanceCounterCategory and the PerformanceCounter objects. The first drop-down list is populated with all the categories available. These values are first placed in a List (Of String) object that enables you to call a Sort() method and allows you to remove any categories you are not interested in by using the Remove() method before binding to the DropDown List control. The category selected in the first drop-down list drives what appears in the second and third drop-down lists. The second drop-down list displays a list of counters that are available for a particular category. You might tend to think that the third drop-down list of instances is based upon the counter that is selected, but instances are set at the category level. When the button on the page is clicked, a new instance of the PerformanceCounter object is created and, as you can see from the example, it can be instantiated in several ways. The first constructor takes just a category name and a counter name, whereas the second constructor takes these two items plus the instance name utilized. After a PerformanceCounter is created, the NextValue() method pulls a sample from the specified item, thus producing the results that are illustrated in Figure 34-7.

Application Tracing ASP.NET does an excellent job of displaying trace information either directly on the requested pages of your ASP.NET application or in a trace log that you can find at http://[server]/ [application]/trace.axd. Sample screenshots of both of these scenarios appear in Figure 34-8.

www.it-ebooks.info

Understanding Health Monitoring  ❘ 

1297

Figure 34-8

Chapter 23 provides detailed information on working with tracing for instrumentation.

Understanding Health Monitoring One of the more exciting instrumentation capabilities provided by ASP.NET is the health monitoring system introduced in ASP.NET 2.0. ASP.NET health monitoring is built around various health monitoring events (which are referred to as Web events) occurring in your application. Using the health monitoring system enables you to use event logging for Web events such as failed logins, application starts and stops, or any unhandled exceptions. The event logging can occur in more than one place; therefore, you can log to the event log or even back to a database. In addition to this disk-based logging, you can also use the system to e-mail health monitoring information. By default, the health monitoring system is already enabled. All default errors and failure audits are logged into the event logs on your behalf. For example, throwing an error produces an entry in the event log, as illustrated in Figure 34-9.

Figure 34-9

www.it-ebooks.info

1298  ❘  Chapter 34   Instrumentation

By default, these errors are registered in the event logs, but you can also record these events in a couple of other places. You define where you record these event messages through the various providers available to the health monitoring system. These providers are briefly covered next.

The Health Monitoring Provider Model Chapter 11 offers quite a bit of information about what a provider model is and how the health monitoring system works with providers, but a short review of the providers available to the health monitoring system is warranted here, as well. The health monitoring system has the most built-in providers in ASP.NET. Figure 34-10 contains a diagram of the available providers.

Figure 34-10

Seven providers are available to the health monitoring system right out of the box: ➤➤

System.Web.Management.EventLogWebEventProvider: Enables you to use the ASP.NET health monitoring system to record security operation errors and all other errors into the Windows event log.

➤➤

System.Web.Management.SimpleMailWebEventProvider: Allows you to use the ASP.NET health

➤➤

System.Web.Management.TemplatedMailWebEventProvider: Similar to the SimpleMailWebEventProvider, the TemplatedMailWebEventProvider class lets you send error information in a templated e-mail. Templates are defined using a standard .aspx page.

➤➤

System.Web.Management.SqlWebEventProvider: Enables you to use the ASP.NET health monitoring system to store error information in SQL Server. Like the other SQL providers for the other systems in ASP.NET, the SqlWebEventProvider stores error information in SQL Server Express Edition by default.

➤➤

System.Web.Management.TraceWebEventProvider: Enables you to use the ASP.NET health monitoring system to send error information to the ASP.NET page tracing system.

➤➤

System.Web.Management.IisTraceWebEventProvider: Provides you with the capability to use the ASP.NET health monitoring system to send error information to the IIS tracing system.

➤➤

System.Web.Management.WmiWebEventProvider: Enables you to connect the ASP.NET health monitoring system to the Windows Management Instrumentation (WMI) event provider.

monitoring system to send error information in an e-mail.

www.it-ebooks.info

Understanding Health Monitoring  ❘ 

1299

Health Monitoring Configuration By default, the EventLogWebEventProvider object is what is utilized for all errors and failure audits. These rules are defined along with the providers and what Web events to trap in the root web.config file found at C:\WINDOWS\Microsoft.NET\Framework\v4.0.21006\CONFIG. All the information for the health monitoring section is defined within the section of this file. Even though these items are defined in the root web.config file, you can also override these settings and define them yourself in your application’s web.config file. To define the health monitoring capabilities and behaviors in your web.config file, place a section within the section of your configuration file:

The section can include a number of subsections, including the following: ➤➤



➤➤



➤➤



➤➤



➤➤



You will look next at some of the core sections and how you can use these sections to define health monitoring tasks in your application’s web.config file.

The section of the health monitoring system allows you to define friendly names for specific events that can be captured. You can declare a number of events in this section, but doing so doesn’t mean that they are utilized automatically. Remember that when placing events in this section, you are just simply defining them in this configuration file for reuse in other sections. Listing 34-4 shows an example of event mapping. Listing 34-4:  Using the section
continues

www.it-ebooks.info

1300  ❘  Chapter 34   Instrumentation

Listing 34-4  (continued) Version=4.0.0.0,Culture=neutral,PublicKeyToken=b03f5f7f11d50a3a" startEventCode="0" endEventCode="2147483647"/>


From this section, you can see that a number of different event types are defined within this section. Not all these definitions are required, just the ones you are interested in working with. Because these definitions are important, look at how the first one is listed:

The element takes a number of different attributes. The first, name, allows you to give a user-friendly name that can be used later in one of the other sections of the document. In this case, the friendly name provided is All Events. The next attribute is the type attribute. This enables you to define the .NET event object with which this event mapping is associated. In this case, it is the base event used by all the other event types as a base class. Although the object definition with the type attribute is shown on multiple lines in this book, you should define it on a single line for it to work. All the possible Web events it might associate with are found in the System.Web.Management namespace.

www.it-ebooks.info

Understanding Health Monitoring

❘ 1301

Each Web event that is recorded has an identifier (code) associated with it, and you can use the startEventCode attribute to defi ne a starting point for this code defi nition. In the preceding example, the event code starts at 0 and increments from there until it reaches the number defi ned in the endEventCode attribute. In this case, the ending event code is 2147483647. Table 34 -2 defi nes each event type you can fi nd in the System.Web.Management namespace. TaBle 34-2 weB evenT (sysTem.weB.

descriPTion

managemenT)

WebBaseEvent

The WebBaseEvent class is the base class for all Web event types� You can, therefore, use this instance within the section in order to create a definition to capture all events�

WebHeartbeatEvent

This event defines Web events that are recorded only at specific intervals instead of every time they occur�

WebApplicationLife timeEvent

This event defines Web events that occur on the scale of the application and its lifecycle� These types of events include application starts and stops as well as compilation starts and stops�

WebRequestEvent

This event defines Web events that occur during a request cycle and include items such as when a transaction for a request is committed or aborted�

WebBaseErrorEvent

The WebBaseErrorEvent class is the base class for all Web events that deal with error types� You can, therefore, use this instance within the section to create a definition to capture all error events�

WebErrorEvent

This event defines Web events that occur because of configuration errors or any compilation or parsing errors�

WebRequestErrorEvent

This event defines Web events that occur because requests are aborted or requests are larger than allowable as defined by the maxRequestLength attribute in the configuration file� It also includes any validation errors or any unhandled exceptions�

WebAuditEvent

The WebAuditEvent class is the base class for all Web events that deal with login attempts� These attempts can either fail or succeed� You can use this instance within the section to write a definition to capture all login attempts�

WebFailureAuditEvent

This event defines Web events that occur because of failed login attempts�

Now that all the error defi nitions are in place within the section, the next step is to further defi ne the structure your health monitoring system will take by detailing the section, which is also found within the section.

As you saw earlier in Figure 34 -10, seven different providers that you can use within the health monitoring system are available to you out of the box. By default, ASP.NET records these events to the event logs using the EventLogWebEventProvider, but you can also modify the provider model so that it uses any of the other providers available. You can also build your own custom providers so you can record these Web events to any data store you want. You can fi nd more information on building custom providers in Chapter 12.

Providers are declared within the section, which is nested within the section of the document. Within the root web.config fi le found at

www.it-ebooks.info

1302  ❘  Chapter 34   Instrumentation

C:\WINDOWS\Microsoft.NET\Framework\v4.0.21006\CONFIG, you find a short list of declared providers. You can also declare other providers in your application’s web.config file as presented in Listing 34-5. Listing 34-5:  Using the section

In this example, you see three separate health monitoring providers declared in the web.config file. A provider is defined for storing Web events in the event logs, another for storing in SQL Server, and finally another for the Windows Management Instrumentation (WMI) system. By declaring these providers within the section of the web.config file, you don’t ensure that these providers are actually utilized. Instead, they are simply defined and ready for you to use. You specify which providers to use within the section of the section, which is defined shortly. You can add defined providers by using the element within the section. Within the element, you find a series of available attributes. The first one is the name attribute. This allows you to provide a friendly name to the defined provider instance that you will use later when specifying the provider to actually use. The name can be anything you want. The type attribute allows you to define the class used for the provider. In some cases, this is all you need (for example, for the event log provider). If you are working with a database-linked provider, however, you must further define attributes such as the connectionString attribute, along with the buffer and bufferMode attributes. These are covered in more detail later in this chapter. After your providers are defined in the section of the document, the next step is to determine which Web events to work with and which provider(s) should be utilized in the monitoring of these events. Both these operations are accomplished from within the section.

The section allows you to define the Web events to monitor and the providers to tie them to when one of the monitored Web events is actually triggered. When you are using the health monitoring system, you can actually assign multiple providers to watch for the same Web events. This means that you can store

www.it-ebooks.info

Understanding Health Monitoring  ❘ 

1303

the same Web event in both the event logs and in SQL Server. That capability is powerful. Listing 34-6 provides an example of using the section within your application’s web.config file. Listing 34-6:  Using the section

In this example, two types of Web events are being recorded. You specify rules (the Web events to monitor) by using the element within the section. The name attribute allows you to provide a friendly name for the rule definition. The eventName is an important attribute because it takes a value of the Web event to monitor. These names are the friendly names that you defined earlier within the section of the section. The first element provides a definition of monitoring for All Errors via the eventName attribute. Looking back, you can see that this was, indeed, defined in the section.

After specifying which Web event to work with through the eventName attribute, the next step is to define which provider to use for this Web event. You do this using the provider attribute. In the case of the first element, the EventLogProvider is utilized. Again, this is the friendly name that was used for the provider definition in the section, as shown here:

The three attributes discussed so far are required attributes. The rest are considered optional attributes. The minInstances attribute defines the minimum number of times this Web event must occur before it is logged. The maxLimit attribute defines the maximum number of instances of the defined Web event that can be

www.it-ebooks.info

1304  ❘  Chapter 34   Instrumentation

recorded. If instances are likely to occur because of some continuous loop, you might want to add a value here. The minInterval attribute denotes the minimum time allowed between log entries. This means that if the minInterval is set to 00:01:00 and two Web events being monitored occur within 15 seconds of each other, the first one is recorded but the second instance is discarded because it falls within the 1-minute setting. Within the section, you can also see a profile attribute defined in the elements of the rules defined. The value of this attribute comes from a definition that is supplied from the section of the section. This section is discussed next.

The section enables you to define some of the behaviors for Web event monitoring. These definitions are utilized by any number of rule definitions within the section. Listing 34-7 presents an example use of the section. Listing 34-7:  Using the section

As with the other sections, you add a profile definition by using the element within the section. The name attribute allows you to provide a friendly name that is then utilized from the definitions placed within the section, as illustrated here:

The definitions in the section also use the attributes minInstances, maxLimit, and minInterval. These have the same meanings as if they were used directly in the section (see the explanation in the previous section). The idea here, however, is that you can more centrally define these values and use them across multiple rule definitions.

www.it-ebooks.info

Understanding Health Monitoring  ❘ 

1305

Writing Events via Configuration: Running the Example Using the sample section (illustrated in the previous listings in this chapter), you can now write all errors as well as audits that fail (failed logins) to the event log automatically. To test this construction in the section, create a simple ASP.NET page that allows you to divide two numbers and then show the result of the division on the screen. Figure 34-11 shows an example ASP.NET page.

Figure 34-11

As you can see from this page, you can enter a single number in the first text box and another in the second text box, and click the Calculate button. This allows the two numbers to be divided. The result appears in a Label control on the page. In this example, the code intentionally does not use any exception handling. Therefore, if the end user tried to divide 5 by 0 (zero), an exception is thrown. This event, in turn, causes the exception to be written to the event log. In fact, if you run a similar example and look in the event log, you find that the error has indeed been written as you have specified it should be within the web.config file. Figure 34-12 shows the report from the event log.

Figure 34-12

Routing Events to SQL Server Pushing Web events to the event log is something most Web applications do and is also something you should consider if you have overridden these built-in (and default) features. Even more powerful than writing them to the event log, however, is being able to write them to a database. Writing them to a database allows you to actually create a larger history, makes them more secure, and allows you to more easily retrieve the values and use them in any type of administration application that you might create. This capability was briefly introduced in the introduction of this book and is not that difficult to work with. If you work from the same section created earlier in this chapter, writing the events to SQL Server is as simple as adding some elements to the various sections. As shown in the previous section, a declaration actually exists for recording events into SQL Server. It is presented here again:
www.it-ebooks.info

1306



chaPTer 34 instrumentAtion

type="System.Web.Management.EventLogWebEventProvider,System.Web, Version=4.0.0.0,Culture=neutral,PublicKeyToken=b03f5f7f11d50a3a" />


If you look over this bit of code, you see the defi nition to record events into SQL Server is presented in the bold section within the section. The section in bold shows a connection to the SQL Server instance that is defi ned by the ApplicationServices connection string name. You can fi nd this connection string in the machine.config fi le, and you see that it points to an auto -generated SQL Server Express fi le that ASP.NET can work with.

To work with Microsoft ’s SQL Server 2008, you simply create a new connection string within your ASP.NET application’s web.config file and reference the friendly name provided to this connection from the element of the section. Although this SQL Server instance is declared and defi ned, it is not utilized by any of the rules that you have established. Looking at the previous sections, you can see that within the section of the health monitoring section of the web.config fi le, only two rules exist: a rule to write all errors to the event log and another rule to write failure audits to the event log. To add a new rule to write errors (all errors again) to SQL Server, you use the construction shown in Listing 34 -8. lisTing 34-8: Creating a rule to write events to sQl server
www.it-ebooks.info

Understanding Health Monitoring  ❘ 

1307

profile="Default" minInstances="1" maxLimit="Infinite" minInterval="00:01:00" custom="" />


To be able to write to SQL Server, you must create a new element within the section of the document. The friendly name provided via the name attribute must be unique in the list of defined rules or you encounter an error. In this case, the name of the rule is All Errors SQL Server and like the All Errors Default rule, it points to the event mapping of All Errors as shown via the eventName attribute. The provider attribute then points to the SQL Server definition contained within the section. In this case, it is SqlWebEventProvider. With all this in place, run the ASP.NET page (shown earlier) that allows you to throw an error by dividing by zero. If you run this page a couple of times, you see that ASP.NET has automatically created an ASPNETDB.MDF file in your project (if you didn’t already have one). You can then open the SQL Server Express Edition database and expand the Tables node in the Visual Studio Server Explorer. In this list of available tables, you find a new table called aspnet_WebEvent_ Events. Right-click this table and select Show Table Data from the menu. A dialog similar to what is illustrated in Figure 34-13 appears.

Figure 34-13

www.it-ebooks.info

1308  ❘  Chapter 34   Instrumentation

As you can see from this figure, the entire event is now stored within SQL Server (it is the first and only event at the moment). One of the nice things about this example is that not only is this event recorded to the database, but it is also written to the event log because you can use more than a single provider for each event. You can just as easily use a provider that comes from MailWebEventProvider and send the error out as an e-mail message.

Buffering Web Events When you start working with Web events that you want to write to a database (such as SQL Server) or send via an e-mail, you soon realize that doing so can lock up your database or result in a huge amount of e-mail (especially if your application is sent into some perpetual loop of errors). For this reason, you can see why these providers inherit from BufferedWebEventProvider. BufferedWebEventProvider does exactly what it says — it buffers (or queues) your collected Web events before performing the specified action upon them. The reason you might want to buffer is to eliminate the possibility of your database being pounded or your e-mail box being flooded.

Definitions of how the buffering should occur are located in the section within the section of the web.config file. Listing 34-9 presents an example of the section. Listing 34-9:  Using the section

www.it-ebooks.info

Understanding Health Monitoring  ❘ 

1309



In this code, you can see four buffer modes defined. Each mode has a friendly name defined using the name attribute. For each mode, a number of different attributes can be applied. To examine these attributes, take a closer look at the Logging buffer mode:

This buffer mode is called Logging and, based on the values assigned to its attributes, any provider using this buffer mode sends the messages to the database or via e-mail every 30 minutes. This is also referred to as flushing the Web events. The time period of 30 minutes is defined using the regularFlushInterval attribute. Therefore, every 30 minutes, ASP.NET sends 200 messages to the database (or via e-mail). It will not send more than 200 messages at a time because of what is specified in the maxFlushSize attribute. Well, what happens if more than 200 messages are waiting within that 30-minute time period? ASP.NET still sends only 200 messages every 30 minutes and holds additional messages when the number exceeds 200. The maximum number of messages held in the queue cannot exceed 1,000 messages. This number is set through the maxBufferSize attribute. However, after the total in the queue hits 800 messages, ASP.NET starts flushing the messages every 5 minutes instead of every 30 minutes. The change in frequency of messages is determined by the urgentFlushThreshold attribute, and the time interval used to send messages when the urgentFlushThreshold is hit is determined by the urgentFlushInterval attribute. After you have defined the buffering modes you want to use, the next step is to apply them. To analyze the process, look back on how the SqlWebEventProvider is declared:

Again, this event is shown in bold. The most important attribute is the buffer attribute. By default, buffering of messages is turned off, which you do by setting the buffer attribute to false. The bufferMode attribute

www.it-ebooks.info

1310  ❘  Chapter 34   Instrumentation

allows you to assign one of the defined buffer modes created within the section. In this case, the Notification buffer mode is referenced. Changing the buffer attribute to true enables the events to be sent only to SQL Server according to the time intervals defined by the Notification buffer mode.

E-mailing Web Events When monitoring a server for Web events, you are not always going to be networked into a server or actively monitoring it every second. This doesn’t mean that you won’t want to know immediately if something is going very wrong with your ASP.NET application. For this reason, you will find the SimpleMailWebEventProvider and the TemplatedMailWebEventProvider objects quite beneficial.

Using the SimpleMailProvider The SimpleMailWebEventProvider sends Web events as a simple text e-mail, as shown in Figure 34-14.

Figure 34-14

To set up this provider, you place an additional element within the section, as illustrated in Listing 34-10. Listing 34-10:  Adding a SimpleMailWebEventProvider instance
www.it-ebooks.info

Understanding Health Monitoring  ❘ 

1311

subjectPrefix="Action required." buffer="false" maxEventLength="4096" maxMessagesPerNotification="1" />

After the provider is added, you also add a rule that uses this provider in some fashion (as you do with all the other providers). You do this by referencing the SimpleMailWebEventProvider name within the provider attribute of the element contained in the section. For this code to work, be sure that you set up the SMTP capabilities correctly in your web.config file. Listing 34-11 presents an example. Listing 34-11:  Setting up SMTP in the web.config file

If you do not have a quick way of setting up SMTP and still want to test this code, you can also have ASP.NET create e-mail messages and store them to disk by setting up the section as illustrated in Listing 34-12. Listing 34-12:  Another option for setting up the SMTP section

In this scenario, the e-mails will be just placed within the C:\ root directory.

Using the TemplatedMailWebEventProvider Another option for e-mailing Web events is to use the TemplatedMailWebEventProvider object. This object works basically the same as the SimpleMailWebEventProvider, but the TemplatedMailWebEventProvider

www.it-ebooks.info

1312  ❘  Chapter 34   Instrumentation

allows you to create more handsome e-mails (they might get noticed more). As with the other providers, you use the element within the section to add this provider. Listing 34-13 shows this process. Listing 34-13:  Adding a TemplatedMailWebEventProvider

After adding the provider, you also must add a rule that uses this provider in some fashion (as you do with all the other providers). You add it by referencing the TemplatedMailWebEventProvider name within the provider attribute of the element contained in the section. Be sure to set up the section, just as you did with the SimpleMailWebEventProvider. After these items are in place, the next step is to create an ErrorNotification.aspx page. Listing 34-14 presents this page construction. Listing 34-14:  Creating the ErrorNotification.aspx page <%@ Page Language="VB" %> <%@ Import Namespace="System.Web.Management" %>

VB

My Page



www.it-ebooks.info

Understanding Health Monitoring  ❘ 


id="Label2" id="Label3" id="Label4" id="Label5" id="Label6" id="Label7"

runat="server">






/> /> /> /> /> />




C#

<%@ Page Language="C#" %> <%@ Import Namespace="System.Web.Management" %>

To work with the TemplatedMailWebEventProvider, you first import the System.Web .Management namespace. This is done so you can work with the MailEventNotificationInfo and TemplatedMailWebEventProvider objects. You then create an instance of the MailEventNotificationInfo object and assign it a value of the TemplatedMailWebEventProvider .CurrentNotification property. Now, you have access to an entire series of values from the Web event that was monitored. Figure 34-15 shows this e-mail message.

www.it-ebooks.info

1313

1314  ❘  Chapter 34   Instrumentation

Figure 34-15

As you can see in this figure, the e-mail message is more readable in this format.

Summary Whereas ASP.NET 1.x was really focused on the developer, ASP.NET 2.0 through 4 have made tremendous inroads into making life easier for the administrator of the deployed ASP.NET application. In addition to a number of GUI-based management and administration tools (covered in the next chapter), you can now record and send notifications about the health (good or bad) of your ASP.NET applications using the ASP.NET health monitoring capabilities.

www.it-ebooks.info

35

administration and Management whaT’s in This chaPTer? ➤

Managing applications settings with the ASP�NET Web Site Administration Tool



Using the IIS Manager to manage ASP�NET applications

You have almost reached the end of this book; you have been introduced to ASP.NET 4 with its wonderful features designed to help you become a better and more efficient programmer. However, with all advancement comes complexity, as is the case in the areas of ASP.NET configuration and management. The good news is that the ASP.NET development team realized this and provided tools and APIs that enable developers to configure and manage ASP.NET–based applications with reliability and comfort. This chapter covers these tools in great detail in an effort to educate you about some of the options available to you. This chapter explores two powerful configuration tools: the ASP.NET Web Site Administration Tool, a Web -based application, and the IIS Manager, which is used to configure your ASP.NET applications.

The asP.neT weB siTe adminisTraTion Tool When ASP.NET was fi rst released, it introduced the concept of an XML -based configuration fi le for its Web applications. This web.config fi le is located in the same directory as the application itself. It is used to store a number of configuration settings, some of which can override configuration settings defi ned in machine.config fi le or in the root server’s web.config fi le. Versions of ASP.NET before ASP.NET 2.0, however, did not provide an administration tool to make it easy to configure the settings. Because of this, a large number of developers around the world ended up creating their own configuration tools to avoid having to work with the XML fi le manually. The ASP.NET Web Site Administration Tool enables you to manage Web site configuration through a simple, easy-to -use Web interface. It eliminates the need for manually editing the web.config fi le. If no web.config fi le exists when you use the administration tool for the fi rst time, it creates one. By default, the ASP.NET Web Site Administration Tool also creates the standard ASPNETDB.MDF SQL Server Express Edition fi le in the App_Data folder of your Web site to store application data. The changes made to most settings in the ASP.NET Web Site Administration Tool take effect immediately. You fi nd them reflected in the web.config fi le.

www.it-ebooks.info

1316  ❘  Chapter 35   Administration and Management

The default settings are automatically inherited from any configuration files that exist in the root folder of a Web server. The ASP.NET Web Site Administration Tool enables you to create or update your own settings for your Web application. You can also override the settings inherited from up-level configuration files, if an override for those settings is allowed. If overriding is not permitted, the setting appears dimmed in the administration tool. The ASP.NET Web Site Administration Tool is automatically installed during installation of the .NET Framework version 4. To use the administration tool to administer your own Web site, you must be logged in as a registered user of your site and have read and write permissions to the web.config file. You cannot access the ASP.NET Web Site Administration Tool remotely or even locally through IIS. Instead, you access it with Visual Studio 2010, which, in turn, uses its integrated web server (formally named Cassini) to access the administration tool. To access this tool through Visual Studio 2010, open the Web site and click the ASP.NET Configuration button found in the menu located at the top of the Solution Explorer pane. Another way to launch this tool is to select ASP.NET Configuration from the Website option in the main Visual Studio menu. Figure 35-1 shows the ASP.NET Web Site Administration Tool’s welcome page.

Figure 35-1

The ASP.NET Web Site Administration Tool features a tabbed interface that groups related configuration settings. The following sections describe the tabs and the configuration settings that they manage.

The Home Tab The Home tab (shown previously in Figure 35-1) is a summary that supplies some basic information about the application you are monitoring or modifying. It provides the name of the application and the current user context in which you are accessing the application. In addition, you see links to the other administration tool tabs that provide you with summaries of their settings. To make any changes to your Web application, you simply click the appropriate tab or link. Remember that most changes to configuration settings made using this administration tool take effect immediately, causing the Web application to be restarted and currently active sessions to be lost if you are using an InProc session. The best practice for administrating ASP.NET is to make configuration changes to a development version of your application and later publish these changes to your production application. That’s why this tool can’t be used outside of Visual Studio. Some settings (those in which the administration tool interface has a dedicated Save button) do not save automatically. You can lose the information typed in these windows if you do not click the Save button to

www.it-ebooks.info

The ASP.NET Web Site Administration Tool  ❘ 

1317

propagate the changes you made to the web.config file. The ASP.NET Web Site Administration Tool also times out after a period of inactivity. Any settings that do not take effect immediately and are not saved will be lost if this occurs. As extensive as the ASP.NET Web Site Administration Tool is, it manages only some of the configuration settings that are available for your Web application. All other settings require modification of configuration files manually, by using the Microsoft Management Console (MMC) snap-in for ASP.NET if you are using Windows XP, using the Internet Information Services (IIS) Manager if you are using Windows 7, or by using the Configuration API.

The Security Tab You use the Security tab to manage access permissions to secure sections of your Web application, user accounts, and roles. From this tab, you can select whether your Web application is accessed on an intranet or from the Internet. If you specify the intranet, Windows-based authentication is used; otherwise, forms-based authentication is configured. The latter mechanism relies on you to manage users in a custom datastore, such as SQL Server database tables. The Windows-based authentication employs the user’s Windows logon for identification. User information is stored in a SQL Server Express database by default (ASPNETDB.MDF). The database is automatically created in the App_Data folder of the Web application. Storing such sensitive information on a different and more secure database, perhaps located on a separate server, is recommended. Changing the data store might mean that you also need to change the underlying data provider. To accomplish this, you simply use the Provider tab to select a different data provider. The Provider tab is covered later in this chapter. You can configure security settings on this tab in two ways: select the Setup Wizard, or simply use the links provided for the Users, Roles, and Access Management sections. Figure 35-2 shows the Security tab.

Figure 35-2

You can use the wizard to configure initial settings. Later, you will learn other ways to create and modify security settings.

www.it-ebooks.info

1318



chaPTer 35 AdministrAtion And mAnAgement

The security setup Wizard The Security Setup Wizard provides a seven-step process ranging from selecting the way the user will be authenticated to selecting a data source for storing user information. This is followed by defi nitions of roles, users, and access rules. Be sure to create all folders that need special permissions before you engage the wizard. Follow these steps to use the Security Setup Wizard:

1.

On the Security tab, click the “Use the Security Setup Wizard to configure security step by step” link. The wizard welcome screen (shown in Figure 35-3) appears and is informational only. It educates you on the basics of security management in ASP.NET. When you fi nish reading the screen, click Next.

figure 35 -3

2.

Select Access Method. From the Select Access Method screen, shown in Figure 35- 4, select your access method (authentication mechanism). You have two options: ➤

From the Internet: Indicates you want forms-based authentication. You must use your own database of user information. This option works well in scenarios where non- employees need to access the Web application.



From a Local Area Network: Indicates users of this application are already authenticated on the domain. You do not have to use your own user information database. Instead, you can use the Windows web server domain user information.

Select From the Internet, and click the Next button.

www.it-ebooks.info

The ASP.NET Web Site Administration Tool  ❘ 

1319

Figure 35-4



3. Data Store. As mentioned earlier, the ASP.NET Web Site Administration Tool uses SQL Server Express Edition by default. You can configure additional providers on the Providers tab. In the Step 3 screen shown in Figure 35-5, only an advanced provider is displayed because no other providers have been configured yet. Click Next.

Figure 35-5



4. Define Roles. If you are happy with all users having the same access permission, you can simply skip

this step by deselecting the Enable Roles for This Web Site check box (see Figure 35-6). If this box is not selected, clicking the Next button takes you directly to the User Management screens. Select this box to see how to define roles using this wizard. When you are ready, click Next.

www.it-ebooks.info

1320  ❘  Chapter 35   Administration and Management

Figure 35-6

The next screen (see Figure 35-7) in the wizard enables you to create and delete roles. The roles simply define categories of users. Later, you can provide users and access rules based on these roles. Go ahead and create roles for Administrator, Human Resources, Sales, and Viewer. Click Next.

Figure 35-7



5. Add New Users. Earlier, you selected the From the Internet option, so the wizard assumes that you

want to use forms authentication and provides you with the option of creating and managing users. The From a Local Area Network option, remember, uses Windows-based authentication. The Add New Users screen (see Figure 35-8) enables you to enter the username, password, e-mail address, and a security question and answer.

www.it-ebooks.info

The ASP.NET Web Site Administration Tool  ❘ 

1321

Figure 35-8

You can create as many users as you like; but to delete or update information for users, you must leave the wizard and manage the users separately. As mentioned earlier, the wizard is simply for creating the initial configuration for future management. Click Next.

6. Add New Access Rules (see Figure 35-9). First, select the folder in the Web application that needs

s­ pecial security settings. Then choose the role or user(s) to whom the rule will apply. Select the ­permission (Allow or Deny) and click the Add This Rule button. For example, if you had a folder named Secure you could select it and the Administrator role, and then click the Allow radio button to permit all users in the Administrator role to access the Secure folder.

Figure 35-9

www.it-ebooks.info

1322



chaPTer 35 AdministrAtion And mAnAgement

All folders that need special permissions must be created ahead of time. The information shown in the wizard is cached and is not updated if you decide to create a new folder inside your Web application while you are already on this screen, so remember to create your special security folders before starting the wizard. The wizard gives you the capability to apply access rules to either roles or specific users. The Search for Users option is handy if you have defined many users for your Web site and want to search for a specific user. All access rules are shown at the bottom on the screen, and you can delete a specific rule and start again. Rules are shown dimmed if they are inherited from the parent configuration and cannot be changed here. When you are ready, click Next.

7.

The last screen in the Security Setup Wizard is an information page. Click the Finish button to exit the wizard.

Creating new Users The ASP.NET Web Site Administration Tool’s Security tab provides ways to manage users without using the wizard and is very helpful for ongoing maintenance of users, roles, and access permissions. To create a new user, simply click the Create User link on the main page of the Security tab (shown earlier in Figure 35-2). The Create User screen, shown in Figure 35-10, appears, enabling you to provide username, password, confi rmation of password, e-mail, and the security question and answer. You can assign a new user to any number of roles in the Roles list; these are roles currently defi ned for your Web application. Use this tool to create users named Admin, HRUser, and SalesUser and assign them the corresponding roles.

figure 35-10

www.it-ebooks.info

The ASP.NET Web Site Administration Tool  ❘ 

1323

Managing Users You can manage existing users by clicking the Manage Users link on the Security tab. A new screen displays a list of all existing users (see Figure 35-11). A search option is available, which makes finding a specific user easier if the list is long.

Figure 35-11

Find the user you want to manage, and then you can update his information, delete the user, reassign roles, or set the user to active or inactive.

Managing Roles Two links are provided in the Security tab for managing roles: Disable Roles and Create or Manage Roles. Clicking Disable Roles does just that — disables role management in the Web application; it also dims the other link. Click the Create or Manage Roles link to start managing roles and assigning users to specific roles. A screen displays all roles you have defined so far. You have options to add new roles, delete existing roles, or manage specific roles. Click the Manage link next to a specific role, and a screen shows all the users currently assigned to that role (see Figure 35-12). You can find other users by searching for their names, and you can then assign them to or remove them from a selected role.

www.it-ebooks.info

1324  ❘  Chapter 35   Administration and Management

Figure 35-12

Managing Access Rules The Security tab provides options for creating and managing access rules. Access rules are applied either to an entire Web application or to specific folders inside it. Clicking the Create Access Rules link takes you to the Add New Access Rule screen, where you can view a list of the folders inside your Web application (see Figure 35-13). You can select a specific folder, select a role or a user, and then choose whether you want to enable access to the selected folder.

Figure 35-13

Clicking Manage Access Rules on the Security tab takes you to the Manage Access Rules screen, which shows all existing access rules (see Figure 35-14).You can remove any of these rules and add new ones. You can also readjust the list of access rules if you want to apply them in a specific order.

www.it-ebooks.info

The ASP.NET Web Site Administration Tool  ❘ 

Figure 35-14

The Application Tab The Application tab provides a number of application-specific configurations, including the configuration of appSettings, SMTP mail server settings, debugging and trace settings, and starting/stopping the entire Web application.

Managing Application Settings The left side of the screen shows links for creating and managing application settings. The settings are stored in the section of the web.config file. Most ASP.NET programmers are used to manually modifying this tag in previous versions of ASP.NET. Figure 35-15 shows the Application tab.

Figure 35-15

www.it-ebooks.info

1325

1326  ❘  Chapter 35   Administration and Management

Clicking the Create Application Settings link takes you to a screen where you can provide the name and the value information. Clicking Manage Application Settings takes you to a screen where you can view existing settings and edit or delete them. You can also create a new setting from this screen.

Managing SMTP Configuration Click the Configure SMTP E-Mail Settings link to view a screen like the one shown in Figure 35-16. The configure SMTP mail settings feature is useful if your Web application can send auto-generated e-mails. Instead of denoting SMTP server configuration in the code, you can spell it out in the configuration file by entering values here in the administration tool.

Figure 35-16

Specify the server name, port, sender e-mail address, and authentication type.

Managing Tracing and Debugging Information Clicking the Application tab’s Configure Debugging and Tracing link takes you to a screen (see Figure 35-17) where you can enable or disable tracing and debugging. Select whether you want to display trace information on each page. You can also specify whether to track just local requests or all requests, as well as trace sorting and caching configuration.

www.it-ebooks.info

The ASP.NET Web Site Administration Tool  ❘ 

1327

Figure 35-17

To configure default error pages, you simply click Define Default Error Page on the screen you saw in Figure 35-15. This takes you to a screen where you can select a URL that is used for redirection in case of an error condition (see Figure 35-18).

Figure 35-18

www.it-ebooks.info

1328  ❘  Chapter 35   Administration and Management

Taking an Application Offline You can take your entire Web application offline simply by clicking the Take Application Offline link (again, refer to Figure 35-15). The link stops the app domain for your Web application. This feature is useful if you want to perform a scheduled maintenance for an application.

The Provider Tab The final tab in the ASP.NET Web Site Administration Tool is Provider, shown in Figure 35-19. You use it to set up additional providers and to determine the providers your application will use.

Figure 35-19

The Provider page is simple, but it contains an important piece of information: the default data provider with which your application is geared to work. In Figure 35-19, the application is set up to work with the AspNetSqlProvider provider, the default data provider. The two links on this tab let you set up either a single data provider or a specific data provider for each of the features in ASP.NET that requires a data provider. If you click the latter, the screen shown in Figure 35-20 appears. It enables you to pick the available providers separately for Membership and Role management.

Figure 35-20

www.it-ebooks.info

Configuring ASP.NET in IIS on Windows 7  ❘ 

1329

As you can see from the screenshots and brief explanations provided here, you could now handle a large portion of the necessary configurations through a GUI. You no longer have to figure out which setting must be placed in the web.config file. This functionality becomes even more important as the web.config file grows. In ASP.NET 1.0/1.1, the web.config file was a reasonable size, but with all the features provided by ASP.NET 2.0 or 3.5, the web.config file became very large. Again, like ASP.NET 1.0/1.1, the web.config file in ASP.NET 4 is now quite small by default. These GUI-based tools are an outstanding way to configure some of the most commonly needed settings. However, many settings cannot be modified with the Web Server Administration Tool, so you will still need to edit the web.config file in many cases.

Configuring ASP.NET in IIS on Windows 7 If you are using IIS as the basis of your ASP.NET applications, you will find that configuring the ASP.NET application directly through the Internet Information Services (IIS) Manager is quite easy if you are using Windows 7. To access the ASP.NET configurations, open IIS and expand the Sites folder, which contains all the sites configured to work with IIS. Remember that not all your Web sites are configured to work in this manner because it is also possible to create ASP.NET applications that make use of the ASP.NET built-in Web server. After you have expanded the IIS Sites folder, right-click one of the applications in this folder; the options available to you for configuration will appear in the IIS Manager (see Figure 35-21).

Figure 35-21

The options available to you enable you to completely configure ASP.NET or even configure IIS itself. The focus of this chapter is on the ASP.NET section of the options. In addition to the options you can select from one of the available icons, you can also configure some basic settings of the application by clicking the Basic Settings link in the Actions pane on the right side of the IIS Manager. When you click the Basic Settings link, the Edit Web Site dialog box appears, as shown in Figure 35-22.

www.it-ebooks.info

1330



chaPTer 35 AdministrAtion And mAnAgement

Changes you are making in the IIS Manager are actually being applied to the web.config file of your application; making changes to the Default Web site (the root node) lets you edit the machine.config file. This dialog box enables you to change the following items: ➤

Web site name: The name of the Web site. In the case of Figure 35-22, naming the Web site “Wrox” means that the URL will be http://[IP address or domain name]/Wrox.



Application pool: The application pool you are going to use for the application. You will notice that you have two options by default — DefaultAppPool (which uses the .NET Framework 4 and an integrated pipeline mode) and Classic .NET AppPool (which uses the .NET Framework 4 and a classic pipeline mode).



figure 35-22

Physical path: The folder location where the ASP.NET application can be found. In this case, it is C:\Wrox.

The sections that follow review some of the options available to you through the icons in the IIS Manager.

.neT compilation You use the Application tab to make changes that are more specific to the pages in the context of your application. From the .NET Compilation dialog (accessible via the IIS Manager) shown in Figure 35-23, you can change how your pages are compiled and run. You can also make changes to global settings in your application.

figure 35-23

This section of the IIS Manager deals with compilation of the ASP.NET application and how some of the pages of the application will behave. The Batch section deals with the batch compilation of the application — fi rst, whether or not it is even supported and then, details on batch sizes and the time it takes to incur the compilation.

www.it-ebooks.info

Configuring ASP.NET in IIS on Windows 7  ❘ 

1331

The Behavior section deals with whether or not the compilation produces a release or debug build; you will also find some Visual Basic–specific compilation instructions on whether Option Explicit or Option Script are enabled across the entire application. The General section focuses on the assemblies that are referenced as well as your code subdirectories if you are going to break up your App_Code folder into separate compiled instances (required for when you want to incorporate Visual Basic and C# code in the same application). You can also specify the default language that is used in the compilation process — such as VB or C#.

.NET Globalization The .NET Globalization option in the IIS Manager enables you to customize how your ASP.NET application deals with culture and the encoding of the requests and responses. Figure 35-24 shows the options available in this dialog. In addition to picking a specific Culture or UI Culture setting, you can also select Auto Detect, Figure 35-24 which will pick up the culture of the client if it is available. By default, you can also see that the encoding of the requests and the responses are set to utf-8, which will work fine for most Latin-based languages.

.NET Profile The IIS Manager.NET Profile options enable you to customize how your ASP.NET application deals with the ASP.NET personalization system. This system was discussed earlier in Chapter 14 of this book. Figure 35-25 shows the dialog that is provided when you add a new profile to the personalization system. In this case, as presented in Figure 35-25, you can specify the name of the personalization property, the Figure 35-25 data type used, its default value, how it is serialized, and whether it is read-only or available for anonymous users. To better understand these settings, it is important to review Chapter 14. In addition to building properties to use in the personalization system, you can also specify the provider that is used by the system as a whole. By default, it will be using the AspNetSqlProfileProvider, as illustrated in Figure 35-26. You can get to this dialog by selecting the ‘Set Default Provider’ link from the .NET Profile section.

Figure 35-26

.NET Roles You can enable role-based management by adding roles to your application from the .NET Roles section of the IIS Manager. Figure 35-27 shows an example of adding a role called Admin to the application after clicking on the Add link from the Actions section.

www.it-ebooks.info

Figure 35-27

1332  ❘  Chapter 35   Administration and Management

Clicking OK will add the role to the system and the role will then be shown in a list of roles from the main screen of the section, as illustrated in Figure 35-28. By default, there will be no users added to the role. You will be able to add users to roles through the .NET Users section, discussed shortly.

Figure 35-28

.NET Trust Levels The .NET Trust Levels section of the IIS Manager allows you to specify the level of security to apply to your application through the selection of a specific pre-generated configuration file. This is illustrated in the list of options presented in Figure 35-29. By default, your application makes use of the web.config file, but specifying a different trust level will cause the application to use a different .config file. All of these .config files are found at

Figure 35-29

C:\Windows\Microsoft.NET\Framework\v4.0.xxxxx\CONFIG.

.NET Users Probably one of the easiest ways to work with the ASP.NET membership system (covered in Chapter 15 of this book) is to create your users in the .NET Users section of the IIS Manager. Adding a user is easy to do through the dialogs provided, as illustrated in Figure 35-30.

Figure 35-30

As shown in Figure 35-30, you can provide the username, password, and security question and answer in a simple wizard. Figure 35-31 shows the second screen of the wizard.

www.it-ebooks.info

Configuring ASP.NET in IIS on Windows 7  ❘ 

1333

Figure 35-31

In this second screen of the wizard, you can assign users to specific roles that are present in the role management system. Because the Admin role was created earlier in this chapter, I am able to assign the user to this particular role as it exists in the system. After a user is created, you can then see the entire list of users for this particular application from the main .NET Users screen, as illustrated in Figure 35-32.

Figure 35-32

Application Settings In the IIS Application Settings section of the IIS Manager you can click the Add or Edit button, and the Edit/Add Application Settings dialog opens (see Figure 35-33). After you enter a key and value pair, click OK; the settings appear in the list in the main dialog. Then you can edit or delete the settings from the application. Figure 35-33

Connection Strings

In the Connection Strings section of the IIS Manager you can add a connection string to your application by clicking its Add button. You also can edit or remove existing connection strings. Figure 35-34 shows the Edit Connection String dialog for the default connection string — LocalSqlServer. Figure 35-35 shows that adding a brand-new connection is also rather simple.

www.it-ebooks.info

1334  ❘  Chapter 35   Administration and Management

Figure 35-34

Figure 35-35

Pages and Controls The Pages and Controls section of the IIS Manager deals with a group of settings that control the overall ASP.NET pages (.aspx) and user controls in the application (.ascx). Figure 35-36 shows the available settings for this section.

Figure 35-36

Providers The Providers section of IIS deals with all the providers that are defined within the application. From the example in Figure 35-37, you can see that only two providers are defined for the .NET Roles engine — a SQL Server role provider and a Windows Token role provider.

Figure 35-37

www.it-ebooks.info

Configuring ASP.NET in IIS on Windows 7  ❘ 

1335

You can look at all the other engines found in ASP.NET by selecting the option in the drop-down list at the top of the dialog.

Session State ASP.NET applications, being stateless in nature, are highly dependent on how state is stored. The Session State section of the IIS Manager (see Figure 35-38) enables you to change a number of different settings that determine how state management is administered.

Figure 35-38

You can apply state management to your applications in a number of ways, and this dialog allows for a number of different settings — some of which are enabled or disabled based on what is selected. The following list describes the items available in the Session State Settings section: ➤➤

Session state mode: Determines how the sessions are stored by the ASP.NET application. The default option (shown in Figure 35-38) is InProc. Other options include Off, StateServer, and SQLServer. Running sessions in-process (InProc) means that the sessions are stored in the same process as the ASP.NET worker process. Therefore, if IIS is shut down and then brought up again, all the sessions are destroyed and unavailable to end users. StateServer means that sessions are stored out-of-process by a Windows service called ASPState. SQLServer is by far the most secure way to deal with your sessions; it stores them directly in SQL Server. StateServer is also the least performance-efficient method.

➤➤

Cookieless mode: Changes how the identifiers for the end user are stored. The default setting uses cookies (UseCookies). Other possible settings include UseUri, AutoDetect, and UseDeviceProfile.

➤➤

Session timeout: Sessions are stored for only a short period of time before they expire. For years, the default has been 20 minutes. Modifying the value here changes how long the sessions created by your application are valid.

www.it-ebooks.info

1336  ❘  Chapter 35   Administration and Management

SMTP E-mail If you need to work with an application that delivers e-mail, then you must specify the settings to do this. You define the required settings for sending e-mail using SMTP through the SMTP E-mail section of the IIS Manager (see Figure 35-39).

Figure 35-39

Summary This chapter showed you some of the management tools which are part of ASP.NET. These tools make the web.config file more manageable because they take care of setting the appropriate values in the application’s configuration file. The IIS Manager console in Windows 7 is a wonderful tool for managing applications that are configured to work with IIS. The ASP.NET Web Site Administration Tool provides even more value to administrators and developers by enabling them to easily manage settings.

CONFER PROGRAMMER TO PROGRAMMER ABOUT THIS TOPIC.

Visit p2p.wrox.com www.it-ebooks.info

36

Packaging and deploying asP.neT applications whaT’s in This chaPTer? ➤

Understanding packaging and deploying fundamentals



Selecting a deployment method



Building ASP�NET Web Packages

Packaging and deploying ASP.NET applications are topics that usually receive little attention. This chapter takes a more in-depth look at how you can package and deploy your ASP.NET applications after they are built. After you have developed your ASP.NET application on a development computer, you will need to deploy the fi nished product to a quality assurance or staging server, and eventually onto a production server. An important reason to consider the proper packaging and deploying of your ASP.NET applications is that many applications are built as saleable products, starter kits, or solutions. In this case, you may have to allow complete strangers to download and install these products in their own environments that you have absolutely no control over. If this is the case, giving the consumer a single installer fi le that ensures proper installation of the application in any environment is ideal. Nevertheless, regardless of whether you will distribute your web application outside your company you still need a way to deploy it to another server where it can be tested before production deployment. You should never assume that it would be perfect just because it worked on your computer. Most of the time you just develop using the internal Web server in Visual Studio, so you will need a full test using IIS before you assume all is well. Even if you do test with IIS on your computer, deployment-related factors still need to be ironed out and fully tested before the application goes to production. Before you start choosing your deployment approach you should understand the basics of packaging and deploying ASP.NET applications. In the process of packaging your ASP.NET applications, you are putting your applications into a package and utilizing a process of deployment that is initiated through a deployment procedure, such as using a Windows installer or even the new ASP.NET Web Package that is now available via Visual Studio 2010.

www.it-ebooks.info

1338  ❘  Chapter 36   Packaging and Deploying ASP.NET Applications

Deployment Pieces So what are you actually deploying? ASP.NET contains a lot of pieces that are all possible parts of the overall application and need to be deployed with the application for it to run properly. The following list details some of the items that are potentially part of your ASP.NET application and need deployment consideration when you are moving your application: ➤➤

.aspx pages

➤➤

The code-behind pages for the .aspx pages (.aspx.vb or .aspx.cs files)

➤➤

User controls (.ascx)

➤➤

Web service files (.asmx and .wsdl files)

➤➤

.htm or .html files

➤➤

Image files such as .jpg or .gif

➤➤

ASP.NET system folders such as App_Code and App_Themes

➤➤

JavaScript files (.js)

➤➤

Cascading Style Sheets (.css)

➤➤

Configuration files such as the web.config file

➤➤

.NET components and compiled assemblies

➤➤

Data files such as .mdb files

➤➤

IIS settings

➤➤

Registry keys

Steps to Take before Deploying Before deploying your ASP.NET Web applications, you should take some basic steps to ensure that your application is ready for deployment. These steps are often forgotten and are mentioned here to remind you of how you can ensure that your deployed application performs at its best. Before you begin, turn off debugging in the web.config file. You do this by setting the debug attribute in the element to false, as shown in Listing 36-1. Listing 36-1:  Setting debug to false before application deployment

By default, most developers set the debug attribute to true when developing their applications. Doing this inserts debug symbols into the compiled ASP.NET pages. These symbols degrade the performance of any application. After the application is built and ready to be deployed, keeping these debug symbols in place is unnecessary. For those who have been coding ASP.NET for some time now, it is important to note that the Debug option in the drop-down list in the Visual Studio menu does not accomplish much in changing the configuration file or anything similar (shown in Figure 36-1). In the ASP.NET 1.0 and 1.1 days, Visual Studio .NET (as it was called at that time) actually controlled the compilation of the ASP.NET project to a DLL. Now, and ever since ASP.NET 2.0, it is actually ASP.NET itself that controls the compilation process at runtime.

www.it-ebooks.info

Methods of Deploying Web Applications  ❘ 

1339

Therefore, although the drop-down with the Debug designation is present, it really has no meaning in the context of building an ASP.NET project. You completely control the compilation designation through what is set in the web.config file, as shown earlier in Listing 36-1.

Figure 36-1

You will also find that you can provide Web.Debug.config and Web.Release.config files in your application as well. Using Web.Debug.config allows you to put settings in the configuration file that will be utilized when you do a deployment or run of the application while in the debug mode. You can use the same approach with the Web.Release.config file when running or deploying the application in the Release mode. This approach with the configuration files allows you to use different database connection strings, change the authentication mode, and more. You will find using these varied configuration files beneficial if you are working with more than one environment (for example, testing, staging, and production).

Methods of Deploying Web Applications Remember that deployment is the last step in a process. The first is setting up the program — packaging the program into a component that is best suited for the deployment that follows. You can actually deploy a Web application in a number of ways. You can use the XCopy capability that simply wows audiences when demonstrated (because of its simplicity). A second method is to use Visual Studio 2010’s capability to copy a Web site from one location to another using the Copy Web Site feature, as well as an alternative method that uses Visual Studio to deploy a precompiled Web application. The final method uses Visual Studio to build an installer program that can be launched on another machine. After reviewing each of the available methods, you can decide which is best for what you are trying to achieve. Start by looking at the simplest of the three methods: XCopy.

Using XCopy Because of the nature of the .NET Framework, deploying .NET applications is considerably easier now than it was to deploy applications constructed using Microsoft’s predecessor technology — COM. Applications in .NET compile down to assemblies, and these assemblies contain code that is executed by the Common Language Runtime (CLR). One great thing about assemblies is that they are ­self-describing. All the details about the assembly are stored within the assembly itself. In the Windows DNA world, COM stored all its self-describing data within the server’s registry, so installing (as well as uninstalling) COM components meant shutting down IIS. Because a .NET assembly stores this information within itself, XCOPY deployment is possible and no registry settings are needed. Installing an assembly is as simple as copying it to another server and you do not need to stop or start IIS while this is going on. XCOPY is mentioned here because it is the command-line way of basically doing a copy-and-paste of the files you want to move. XCOPY, however, provides a bit more functionality than just a copy-and-paste, as you will see shortly. XCOPY enables you to move files, directories, and even entire drives from one point to another.

www.it-ebooks.info

1340  ❘  Chapter 36   Packaging and Deploying ASP.NET Applications

The default syntax of the XCOPY command is as follows: xcopy [source] [destination] [/w] [/p] [/c] [/v] [/q] [/f] [/l] [/g] [/d[:mm-dd-yyyy]] [/u] [/i] [/s [/e]] [/t] [/k] [/r] [/h] [{/a|/m}] [/n] [/o] [/x] [/exclude:file1[+[file2]][+file3]] [{/y|/-y}] [/z]

To see an example of using the XCOPY feature, suppose you are working from your developer machine (C:\) and want to copy your ASP.NET application to a production server (Y:\). In its simplest form, the following command would do the job: xcopy c:\Websites\Website1 y:\Websites\ /f /e /k /h

This command copies the files and folders from the source drive to the destination drive. Figure 36-2 shows an example of this use on the command line.

Figure 36-2

When you copy files using XCOPY, be aware that this method does not allow for the automatic creation of any virtual directories in IIS. When copying a new Web application, you also need to create a virtual directory in the destination server and associate this virtual directory with the application you are copying. It is a simple process, but you must take these extra steps to finalize the site copy actions. You can provide a number of parameters to this XCOPY command to get it to behave as you want it to. Table 36-1 details these parameters. Table 36-1 Parameter

Description

/w

Displays the message: Press any key to begin copying file(s). It waits for your response to start the copying process.

/p

Asks for a confirmation on each file being copied. This is done in a file-by-file manner.

/c

Ignores errors that might occur in the copying process.

/v

Performs a verification on the files being copied to make sure they are identical to the source files.

/q

Suppresses any display of the XCOPY messages.

/f

Displays the filenames for the source and destination files while the copying process is occurring.

/l

Displays a list of the files to be copied to the destination drive.

/g

Builds decrypted files for the destination drive.

/d

When used as simply /d, the only files copied are those newer than the existing files located in the destination location. Another alternative is to use /d[:mm-dd-yyyy], which copies files that have been modified either on or after the specified date.

www.it-ebooks.info

Methods of Deploying Web Applications  ❘ 

1341

Parameter

Description

/u

Copies only source files that already exist in the destination location.

/i

If what is being copied is a directory or a file that contains wildcards and the same item does not exist in the destination location, a new directory is created. The XCOPY process also copies all the associated files into this directory.

/s

Copies all directories and their subdirectories only if they contain files. All empty directories or subdirectories are not copied in the process.

/e

Copies all subdirectories regardless of whether these directories contain files.

/t

Copies the subdirectories only and not the files they might contain.

/k

By default, the XCOPY process removes any read-only settings that might be contained in the source files. Using /k ensures that these read-only settings remain in place during the copying process.

/r

Copies only the read-only files to the destination location.

/h

Specifies that the hidden and system files, which are usually excluded by default, are included.

/a

Copies only files that have their archive file attributes set, and leaves the archive file attributes in place at the XCOPY destination.

/m

Copies only files that have their archive file attributes set, and turns off the archive file attributes.

/n

Copies using the NTFS short file and short directory names.

/o

Copies the discretionary access control list (DACL) in addition to the files.

/x

Copies the audit settings and the system access control list (SACL) in addition to the files.

/exclude

Allows you to exclude specific files. The construction used for this is exclude:File1 .aspx + File2.aspx + File3.aspx.

/y

Suppresses any prompts from the XCOPY process that ask whether to overwrite the destination file.

/-y

Adds prompts to confirm an overwrite of any existing files in the destination location.

/z

Copies files and directories over a network in restartable mode.

/?

Displays help for the XCOPY command.

Using XCOPY is an easy way to move your applications from one server to another with little work on your part. If you have no problem setting up your own virtual directories, this mode of deployment should work just fine for you. When the Web application is copied (and if placed in a proper virtual directory), it is ready to be called from a browser.

Using the VS Copy Web Site Option The next option for copying a Web site is to use a GUI provided by Visual Studio 2010. This Copy Web Site GUI enables you to copy Web sites from your development server to either the same server or a remote server (as you can when you use the XCOPY command). You can open this Copy Web Site dialog in Visual Studio in two ways. The first way is to click in the Copy Web Site icon in the Visual Studio Solution Explorer. Figure 36-3 shows this icon.

www.it-ebooks.info

1342  ❘  Chapter 36   Packaging and Deploying ASP.NET Applications

Nest Related Files Properties

Refresh

View Class Diagram Copy Web Site

ASP .NET Configuration

Figure 36-3

The other way to open the Copy Web Site GUI is to choose Website ➪ Copy Web Site from the Visual Studio menu. Using either method opens the Copy Web Site GUI in the Document window, as illustrated in Figure 36-4.

Figure 36-4

www.it-ebooks.info

Methods of Deploying Web Applications  ❘ 

1343

From this GUI, you can click the Connect To a Remote Server button (next to the Connections text box). This action opens the Open Web Site dialog shown in Figure 36-5.

Figure 36-5

As you can see from this dialog, you have a couple of options to connect to and copy your Web application. These options include the following: ➤➤

File System: This option allows you to navigate through a file explorer view of the computer. If you are going to install on a remote server from this view, you must have already mapped a drive to the installation location.

➤➤

Local IIS: This option enables you to use your local IIS in the installation of your Web application. From this part of the dialog, you can create new applications as well as new virtual directories directly. You can also delete applications and virtual directories from the same dialog. The Local IIS option does not permit you to work with IIS installations on any remote servers.

➤➤

FTP Site: This option enables you to connect to a remote server using FTP capabilities. From this dialog, you can specify the server that you want to contact using a URL or IP address, the port you are going to use, and the directory on the server that you will work with. From this dialog, you can also specify the username and password that may be required to access the server via FTP. Note that if you access this server with this dialog via FTP and provide a username and password, the items are transmitted in plain text.

➤➤

Remote Site: This option enables you to connect to a remote site using FrontPage Server Extensions. From this option in the dialog, you can also choose to connect to the remote server using Secure Sockets Layer (SSL).

After being connected to a server, you can copy the contents of your Web application to it by selecting all or some of the files from the Source Web Site text area. After you select these files in the dialog, some of the movement arrows become enabled. Clicking the right-pointing arrow copies the selected files to the destination server. In Figure 36-6 you can see that, indeed, the files have been copied to the remote destination.

www.it-ebooks.info

1344  ❘  Chapter 36   Packaging and Deploying ASP.NET Applications

Figure 36-6

If you open the same copy dialog later, after working on the files, you see an arrow next to the files that have been changed in the interim and are, therefore, newer than those on the destination server (see Figure 36-7). These arrows enable you to select only the files that must be copied again and nothing more. All the copying actions are recorded in a log file. You can view the contents of this log file from the Copy Web Site dialog by clicking the View Log button at the bottom of the dialog. This opens the CopyWebSite.log text file. From the copy that you made previously, you can see the transaction that was done. Here is an example log entry:

Figure 36-7

Copy from 'C:\Websites\Website1' to 'E:\Website1' started at 10/6/2009 7:52:31 AM. Create folder App_Data in the remote Web site.

Copy Copy Copy Copy Copy

file file file file file

Default.aspx from source to remote Web site. Default.aspx.cs from source to remote Web site. About.aspx from source to remote Web site. About.aspx.cs from source to remote Web site. web.config from source to remote Web site.

Copy from 'C:\Websites\Website1' to 'E:\Website1' is finished. Completed at 10/6/2009 7:52:33 AM.

Deploying a Precompiled Web Application In addition to using Visual Studio to copy a Web application from one location to another, using this IDE to deploy a precompiled application is also possible. The process of precompiling a Web application is

www.it-ebooks.info

Methods of Deploying Web Applications  ❘ 

1345

explained in Chapter 1. ASP.NET 4 includes a precompilation process that allows for a process referred to as precompilation for deployment. What happens in the precompilation for deployment process is that each page in the Web application is built and compiled into a single application DLL and some placeholder files. These files can then be deployed together to another server and run from there. The nice thing about this precompilation process is that it obfuscates your code by placing all page code (as well as the page’s code-behind code) into the DLL, thereby making it more difficult for your code to be stolen or changed if you select this option in the compilation process. This is an ideal situation when you are deploying applications your customers are paying for, or applications that you absolutely do not want changed in any manner after deployment. Chapter 1 showed you how to use the command-line tool aspnet_compiler.exe to accomplish the task of precompilation. Although this method is great for precompiling your Web applications and deploying them to remote servers, you can also use Visual Studio 2010 to accomplish the precompilation and deployment process. To accomplish this task, open the project you want to deploy and get the application ready for deployment by turning off the debugging capabilities as described earlier in the chapter. Then open the precompilation and deployment dialog by choosing Build ➪ Publish Web Site in the Visual Studio menu. The Publish Web Site dialog shown in Figure 36-8 appears. Using the Browse (. . .) button in this dialog, you can choose any remote location to which you want to deploy the application. As in earlier examples, your options are a file system location, a place in the local IIS, a location accessed using FTP, or a location accessed via FrontPage Server Extensions.

Figure 36-8

Other options in this dialog include the “Allow this precompiled site to be updateable” check box. When this option is selected, the site will be compiled and copied without any changes to the .aspx pages. This means that after the precompilation process, you can still make minor changes to the underlying pages, and the application will work and function as normal. If this check box is not selected, all the code from the pages is stripped out and placed inside one or more DLLs. In this state, the application is not updateable because updating any of the placeholder files from this compilation process is impossible. Another option in this dialog is to assign a strong name to the DLL that is created in this process. You can select the appropriate check box and assign a key to use in the signing process. The created DLL from the precompilation will then be a strong assembly — signed with the key of your choice. When you are ready to deploy, click OK in the dialog and then the open application is built and published. Published means that the application is deployed to the specified location. Looking at this location, you can see that a bin directory has now been added that contains some precompiled DLLs, which is your Web application. This is illustrated in Figure 36-9.

www.it-ebooks.info

1346  ❘  Chapter 36   Packaging and Deploying ASP.NET Applications

Figure 36-9

In this state, the code contained in any of the ASP.NET-specific pages is stripped out and placed inside the DLL. The files that you see are actually just placeholders that the DLL needs for reference.

Building an ASP.NET Web Package One of the easiest ways to deploy your Web application is to use the new built-in publishing features from Visual Studio 2010. Behind the scenes, this new capability uses Microsoft’s Web Deployment Tool, also known as MSDeploy, which means that if you want to deploy this tool to your server this server must have MSDeploy on the machine for the hydration of your application to actually work. The package that is created and passed around is an actual physical file — a .zip file. This .zip file contains everything you need to redeploy your Web application with all of its settings into the new environment. In addition to the .zip file, it also includes a manifest file and a command file that are used to initiate the installation of the package on the host machine. The first way in which this application can be deployed is to use the new 1-Click Publishing capability found in Visual Studio 2010. To do this, right-click on the project within the Visual Studio Solution Explorer and select the Publish option from the provided menu. The Publish Web dialog appears, as shown in Figure 36-10. Figure 36-10

www.it-ebooks.info

Methods of Deploying Web Applications  ❘ 

1347

Because many developers are continually deploying their Web applications to their testing, staging, and production environments, there is a capability to store your deployment options in a profile that you can use again and again. Also notice in the dialog in Figure 36-10 that the build configuration is set to Release for this deployment. This setting comes from what you have set in the toolbar of Visual Studio 2010 before you select the Publish option. The Publish Web dialog contains the following options as defined in Table 36-2. Table 36-2 Setting

Description

Profile name

The name of your saved profile. This provides you the ability to reuse your settings as a default for repeated deployments.

Build Configuration

Specifies whether the build compilation will be done in either the Debug or Release mode. You can first establish this setting from the toolbar of Visual Studio.

Publish Method

The method you want to employ for the deployment. The possible options include MSDeploy Publish, FTP, File System, and FPSE.

Service URL

Specifies the location of the MSDeploy on the host server. This URL points to the actual IIS handler that has the MSDeploy capability and will be constructed similar to http://myhostserver:8172/MsDeploy .axd.

Site/Application

Specifies the location where your application will be deployed. Here you can specify the site as well as the virtual directory to place the application. An example of this is MyDomain.com/MyApplication.

Mark as IIS application on destination

If this option is selected, IIS will treat the endpoint defined in the Site/ Application textbox as the application root.

Do not delete extra files on destination

If this option is not selected, the 1-Click Publishing option will first delete everything on the host server location before applying the files and settings.

Allow Untrusted Certificate

If this option is selected, you will trust certificates that are self-published or certificates that don’t match the URL of the destination.

User Name

The username used for IIS connections.

Password

The password used for IIS connections.

Save password

Specifies to the Visual Studio publishing feature whether to save the password in the profile.

Besides using the MSDeploy option, you will find that the other options provided through the new Publish Web dialog are even simpler. Figure 36-11 shows you the settings for the other three deployment options provided.

YOU CAN DOWNLOAD THE CODE FOUND IN THIS BOOK. VISIT WROX.COM AND SEARCH FOR ISBN 9780470502204

www.it-ebooks.info

1348  ❘  Chapter 36   Packaging and Deploying ASP.NET Applications

Figure 36-11

In this figure you can see some standard settings for doing deployments using FTP, the file system, or FrontPage Server Extensions. The interesting thing with the MSDeploy capabilities that Visual Studio 2010 now works with is that instead of just connecting to a remote MSDeploy handler and running your deployment real-time, you can also create an MSDeploy package that can be run at any time. Visual Studio 2010 now includes the ability to create these packages that can then be e-mailed or by other means provided to someone else to run on their systems. To create a Web deployment package, right-click on the project within the Visual Studio Solution Explorer and choose the Create Package option from the provided menu. When you select the Create Package option, the progress of the package creation appears in the Visual Studio status bar. After you’re notified that the Publish succeeded, you can find the entire package in your application’s obj folder, such as C:\Users\Evjen\Documents\Visual Studio 10\ Projects\MyWebApplication\MyWebApplication\obj\Release\Package

Within this folder are all the files that constitute the package. Figure 36-12 presents these files.

www.it-ebooks.info

Methods of Deploying Web Applications  ❘ 

1349

Figure 36-12

All of these files constitute the package that you can use in the deployment process. ➤➤

The MyWebApplication.deploy.cmd file is what your infrastructure team would use to run on the host machine (which has MSDeploy) to install the application and all the settings that are required for the application.

➤➤

The other files, MyWebApplication.SetParameters.xml and MyWebApplication. SourceManifest.xml, are used to define the settings used in the installation process.

➤➤

The final file, MyWebApplication.zip, contains the contents of the application.

With this package, passing another team the installation required for your application is easy. A nice example of this usage is if you are deploying to a Web farm because multiple deployments need to occur and you want these deployments to all be the same. Running your installations from this single package ensures this similarity. The other nice advantage to this package system is that it allows you to save your previous deployments, and if you need to go back and verify deployments, you can easily grab hold of a saved package. It also makes rollbacks easy for your deployment process.

Building an Installer Program The final option you should look at is how to use Visual Studio to build an installation program. After the program is constructed, a consumer can run the installation program on a server where it performs a series of steps to install the Web application. Packaging your Web application into an installer program works in many situations. For example, if you sell your Web application, one of the simpler ways for the end user to receive the application is as an executable that can be run on the computer and installed — all without much effort on his part.

The Windows Installer The Windows Installer service was introduced with Windows 2000, although it is also available in Windows Server 2003 and Windows Server 2008. This service was introduced to make the installation process for your Windows-based applications as easy as possible.

www.it-ebooks.info

1350  ❘  Chapter 36   Packaging and Deploying ASP.NET Applications

You use the Windows Installer technology not only for ASP.NET applications but also for any type of Windows-based application. The Windows Installer service works by creating a set of rules that determine how the application is to be installed. These rules are packaged into a Windows Installer Package File that uses the .msi file extension. The Windows Installer service considers all applications to be made up of three parts: ➤➤

Products: The large-bucket item being installed, also known as the application itself. An example is the ASP.NET Web application.

➤➤

Features: Features are subsets of products. Products are made up of one or more features.

➤➤

Components: Components make up features. A feature is made up of one or more components. A single component can be utilized by several features in the product.

The Windows Installer service is a powerful offering and can be modified in many ways. Not only does the Windows Installer technology detail the product, features, and components of what is to be installed, but it can also take other programmatic actions or show a sequence of user interfaces as the installation process proceeds. For detailed information on the Windows Installer, be sure to view the MSDN documentation on the Windows Installer SDK. With that said, working with the Windows Installer SDK is complicated at best; that was the reason for the release of the Visual Studio Installer (VSI) as an add-on with Visual Studio 6. This addition made the steps for building an installer much easier to follow. Visual Studio 2010 continues to expand on this capability. You have quite a few options for the deployment projects you can build with Visual Studio 2010. Such projects include the following: ➤➤

Setup Project: This project type allows you to create a standard Windows Installer setup for a Windows application.

➤➤

Web Setup Project: This project type is covered in this chapter. It’s the type of setup project you use to create an installer for an ASP.NET Web application.

➤➤

Merge Module Project: This project type creates a merge module similar to a cabinet file. A merge module, such as a cabinet file, allows you to package a group of files for distribution but not for installation. The idea is that you use a merge module file with other setup programs. This project type produces a file type with an extension of .msm.

➤➤

Setup Wizard: This selection actually gives you a wizard to assist you through one of the other defined project types.

➤➤

Cab Project: This project type creates a cabinet file (.cab) that packages a group of files for distribution. It is similar to a merge module file, but the cabinet file is different in that it allows for installation of the files contained in the package.

Although a number of different setup and deployment project types are at your disposal, the Web Setup Project is the only one covered in this chapter because it is the project you use to build an installer for an ASP.NET Web application.

Actions of the Windows Installer You might already be thinking that using the Windows Installer architecture for your installation program seems a lot more complicated than using the methods shown previously in this chapter. Yes, it is a bit more complicated — mainly because of the number of steps required to get the desired result; but in the end, you get a lot more control over how your applications are installed. Using an installer program gives you programmatic logic over how your applications are installed. You also gain other advantages, such as: ➤➤

The capability to check whether the .NET Framework is installed, as well as which version of the Framework is installed

www.it-ebooks.info

Methods of Deploying Web Applications  ❘ 

➤➤

The capability to read or write values to the registry

➤➤

The capability to collect information from the end user during the installation process

➤➤

The capability to run scripts

➤➤

The capability to include such features such as dialogs and splash screens during the installation process

1351

Creating a Basic Installation Program You can apply a tremendous amount of customization to the installation programs you build. Start, however, by looking at how to create a basic installation program for your ASP.NET Web application. To create an installer for your application, open the project for which you want to create a deployment project in Visual Studio and add an installer program to the solution. To do this, you add the setup program as a new project contained within the same solution. Choose File ➪ New ➪ Project from the Visual Studio menu. The New Project dialog launches. From the New Project dialog, expand Other Project Types from the left pane in the dialog and then select Setup and Deployment. A list of all the available setup and deployment projects in Visual Studio appears. For the purposes of this example, select Web Setup Project (shown in Figure 36-13).

Figure 36-13

Clicking OK in this dialog adds the Web Setup Project type to your solution. It uses the default name of WebSetup1. Visual Studio also opens the File System Editor in the document window, which is shown in Figure 36-14.

www.it-ebooks.info

1352  ❘  Chapter 36   Packaging and Deploying ASP.NET Applications

Figure 36-14

The File System Editor shows a single folder: the Web Application Folder. This is a representation of what is going to be installed on the target machine. Now add the files from the WebSite1 project to this folder, which you do by choosing Project ➪ Add ➪ Project Output from the Visual Studio menu. The Add Project Output Group dialog opens. This dialog (shown in Figure 36-15) enables you to select the items you want to include in the installer program. From this dialog, you can see that the project, Wrox, is already selected. Highlight the Content Files option and click OK. This adds all the files from the Wrox project to the WebSetup1 installer program. This addition is then represented in the File System Editor as well. After the files are added to the installer program, click the Launch Conditions Editor button in the Solution Explorer (see Figure 36-16) to open the editor. The Launch Conditions Editor Figure 36-15 also appears in Visual Studio’s document window. From this editor, you can see that a couple of conditions are already defined for you. Obviously, for Web applications, it is important that IIS be installed. Logically, one of the defined conditions is that the program must perform a search to see whether IIS is installed before installing the application. You should also stipulate that the installation server must have version 4 of the .NET Framework installed. To establish this condition, right-click the Requirements On Target Machine node. Then select Add .NET Framework Launch Condition (as shown in Figure 36-17).

www.it-ebooks.info

Methods of Deploying Web Applications  ❘ 

Registry Editor File System Editor Properties

File Types Editor User Interface Editor

Custom Actions Editor Launch Conditions Editor

Figure 36-16

Figure 36-17

www.it-ebooks.info

1353

1354  ❘  Chapter 36   Packaging and Deploying ASP.NET Applications

This adds the .NET Framework requirement to the list of launch conditions required for a successful installation of the Web application. As a final step, highlight the WebSetup1 program in the Visual Studio Solution Explorer so you can modify some of the properties that appear in the Properties window. For now, just change some of the selfexplanatory properties, but you will review these again later in this chapter. For this example, however, just change the following properties: ➤➤

Author: Wrox

➤➤

Description: This is a test project.

➤➤

Manufacturer: Wrox

➤➤

ManufacturerUrl: http://www.wrox.com

➤➤

SupportPhone: 1-800-555-5555

➤➤

SupportUrl: http://www.wrox.com/support/

Now the installation program is down to its simplest workable instance. Make sure Release is selected as the active solution configuration in the Visual Studio toolbar; then build the installer program by choosing Build ➪ Build WebSetup1 from the menu. Looking in C:\Documents and Settings\\My Documents\Visual Studio 2010\ Projects\Wrox\WebSetup1\Release, you find the following files: ➤➤ ➤➤

Setup.exe: This is the installation program. It is meant for machines that do not have the Windows Installer service installed. WebSetup1.msi: This is the installation program for those machines that have the Windows Installer

service installed.

That’s it! You now have your ASP.NET Web application wrapped up in an installation program that can be distributed in any manner you want. It can then be run and installed automatically for the end user. Take a quick look in the following section at what happens when the consumer actually fires it up.

Installing the Application Installing the application is a simple process (as it should be). Double-click the WebSetup1.msi file to launch the installation program. The Welcome screen shown in Figure 36-18 appears. From this dialog, you can see that the name of the program being installed is WebSetup1. Clicking Next gives you the screen shown in Figure 36-19.

Figure 36-18

Figure 36-19

www.it-ebooks.info

Methods of Deploying Web Applications  ❘ 

1355

This screen tells you what you are installing (the Default Web Site) as well as the name of the virtual directory created for the deployed Web application. The consumer can feel free to change the name of the virtual directory in the provided text box. A button in this dialog allows for an estimation of the disk cost (space required) for the installed application. In .NET 4, the installer also allows the end user to choose the application pool he or she is interested in using for the application. The next series of screens install the WebSetup1 application (shown in Figure 36-20). After the application is installed, you can find the WebSetup1 folder and application files located in the C:\Inetpub\wwwroot folder (within IIS). The application can now be run on the server from this location.

Figure 36-20

Uninstalling the Application To uninstall the application, the consumer has a couple of options. One option is to re-launch the .msi file and use the option to either repair the current installation or to remove the installation altogether (as shown in Figure 36-21).

Figure 36-21

The other option is to open the Add/Remove Programs dialog from the server’s Control Panel. On the Control Panel, you see WebSetup1 listed (as shown in Figure 36-22).

www.it-ebooks.info

1356  ❘  Chapter 36   Packaging and Deploying ASP.NET Applications

Figure 36-22

This dialog holds information about the size of the installed application and, if you are using Windows XP, it will also show you how often the application is used. Also, if you are using Windows XP, clicking the support link opens the Support Info dialog, which shows the project’s properties that you entered a little earlier (see Figure 36-23). However, if you are using Windows 7, you can get at the same information by right-clicking on the column headers and selecting the More option from the provided menu. This gives you a list of options (shown here in Figure 36-24), providing the same information as what you can see in Windows XP.

Figure 36-23

Figure 36-24

www.it-ebooks.info

Looking More Closely at Installer Options  ❘ 

1357

From the Add/Remove Programs dialog, you can remove the installation by clicking the Remove button of the selected program.

Looking More Closely at Installer Options The Windows Installer service easily installs a simple ASP.NET Web application. The installer takes care of packaging the files into a nice .msi file from which it can then be distributed. Next, the .msi file takes care of creating a virtual directory and installing the application files. The installer also makes uninstalling the application from the server just as easy. All these great services are provided with very little work on the user’s part. Even though this approach addresses almost everything needed for an ASP.NET installer program, the setup and deployment project for Web applications provided by Visual Studio really provides much more in the way of options and customizations. This next section looks at the various ways you can work with modifying the installer program.

Working with the Deployment Project Properties You can work with the project properties of the installer from Visual Studio in several ways. The first way is by right-clicking the installer project from the Solution Explorer of Visual Studio and selecting Properties from the menu. This opens the WebSetup1 Property Pages dialog shown in Figure 36-25. This dialog has some important settings for your installer application. Notice that, like other typical projects, this setup and deployment project allows for different active build configuration settings. For instance, you can have the active build configuration set to either Release or Debug. You can also click on the Configuration Manager button to get access to configuration settings for all the projects involved. In addition, this dialog enables you to add or remove build configurations from the project.

Figure 36-25

The Output File Name The Output File Name setting lets you set the name of the .msi file that is generated. By default, it is the name of the project, but you can change this value to anything you want. The properties section also allows you to modify the location where the built .msi is placed on the system after the build process occurs.

Package Files The Package files section of this properties page enables you to specify how the application files are packaged in the .msi file. The available options include the following: ➤➤

As loose, uncompressed files: This option builds the project so that a resulting .msi file is created without the required application files. Instead, these application files are kept separate from the .msi file but copied to the same location as the .msi file. With this type of structure, you must distribute both the .msi file and the associated application files.

➤➤

In setup file: This option (which is the default option) packages the application files inside the .msi file. This makes distribution an easy task because only a single file is distributed.

www.it-ebooks.info

1358  ❘  Chapter 36   Packaging and Deploying ASP.NET Applications

➤➤

In cabinet file(s): This option packages all the application files into a number of cabinet files. The size of the cabinet files can be controlled through this same dialog (discussed shortly). This type of installation process is ideal to use if you have to spread the installation application over a number of DVDs, CDs, or floppy disks.

Installation URL Invariably, the ASP.NET applications you build have some component dependencies. In most cases, your application depends on some version of the .NET Framework. The installation of these dependencies, or components, can be made part of the overall installation process. This process is also referred to as bootstrapping. Clicking the Prerequisites button next to the Installation URL text box gives you a short list of available components that are built into Visual Studio in order to bootstrap to the installation program you are constructing (see Figure 36-26). As you can see from when you first enter this settings dialog, the .NET Framework 4 and the Windows Installer 3.1 options are enabled by default, and you check the other components (thereby enabling them) only if your Web application has some kind of dependency on them.

Figure 36-26

From this dialog, you can also set how the dependent components are downloaded to the server where the installation is occurring. The options include downloading from Microsoft, from the server where the application originated, or from a defined location (URL) specified in the provided text box.

Compression The Windows Installer service can work with the compression of the application files included in the build process so that they are optimized for either speed or size. You also have the option to turn off all compression optimizations. The default setting is Optimized for Speed.

CAB Size The CAB Size section of the properties page is enabled only if you select In Cabinet File(s) from the Package Files drop-down list, as explained earlier. If this is selected, it is enabled with the Unlimited radio button selected. As you can see from this section, the two settings are Unlimited and Custom: ➤➤

Unlimited: This selection means that only a single cabinet file is created. The size of this file is dependent on the size of the collection of application files in the Web application and the type of compression selected.

➤➤

Custom: This selection allows you to break up the installation across multiple cabinet files. If the Custom radio button is selected, you can enter the maximum size of the cabinet files allowed in the provided text box. The measure of the number you place in the text box is in kilobytes (KB).

Additional Properties You learned one place where you can apply settings to the installer program; however, at another place in Visual Studio you can find even more properties pertaining to the entire installer program. By selecting the WebSetup1 installer program in the Solution Explorer, you can work with the installer properties directly from the Properties window of Visual Studio. Table 36-3 lists the properties that appear in the Properties window.

www.it-ebooks.info

Looking More Closely at Installer Options  ❘ 

1359

Table 36-3 Property

Description

AddRemoveProgramsIcon

Defines the location of the icon used in the Add/Remove Programs dialog found through the system’s Control Panel.

Author

The author of the installer. This could be the name of a company or individual.

Description

Allows for a textual description of the installer program.

DetectNewerInstalledVersion

Instructs the installer to make a check on the installation server if a newer version of the application is present. If one is present, the installation is aborted. The default setting is True (meaning that the check will be made).

Keywords

Defines the keywords used when a search is made for an installer.

Localization

Defines the locale for any string resources and the runtime user interface. An example setting is English (United States).

Manufacturer

Defines the name of the company that built or provided the installer program.

ManufacturerUrl

Defines the URL of the company that built or provided the installer program.

PostBuildEvent

Specifies a command line executed after the build ends.

PreBuildEvent

Specifies a command line executed before the build begins.

ProductCode

Defines a string value that is the unique identifier for the application. An example value is {885D2E86-6247-4624-9DB1-50790E3856B4}.

ProductName

Defines the name of the program being installed.

RemovePreviousVersions

Specifies as a Boolean value whether any previous versions of the application should be uninstalled prior to installing the fresh version. The default setting is False.

RestartWWWService

Specifies as a Boolean value whether or not IIS should be stopped and restarted for the installation process. The default value is False.

RunPostBuildEvent

Defines when to run the post-build event. The default setting is On successful build. The other possible value is Always.

SearchPath

Defines the path to use to search for any files, assemblies, and merge modules on the development machine.

Subject

Allows you to provide additional descriptions for the application.

SupportPhone

Specifies the support telephone number for the installed program.

SupportUrl

Specifies the URL by which the end user can get support for the installed application.

TargetPlatform

Defines the target platform of the installer. Possible values include x86, x64, and Itanium.

Title

Defines the title of the installer program.

UpgradeCode

Defines a shared identifier that can be used from build to build. An example value is {A71833C7-3B76-4083-9D34-F074A4FFF544}.

Version

Specifies the version number of the installer, cabinet file, or merge module. An example value is 1.0.1.

www.it-ebooks.info

1360  ❘  Chapter 36   Packaging and Deploying ASP.NET Applications

The following sections look at the various editors provided to help you build and customize the construction of the installer. You can get at these editors by clicking the appropriate icon in the Solution Explorer in Visual Studio or by choosing View ➪ Editor in the Visual Studio menu. These editors are explained next.

The File System Editor The first editor that appears when you create your installer program is the File System Editor. The File System Editor enables you to add folders and files that are to be installed on the destination server. In addition to installing folders and files, it also facilitates the creation of shortcuts. Figure 36-27 shows this editor.

Figure 36-27

The File System Editor has two sections. The left section is the list of folders to be installed during the installation process. By default, only the Web Application Folder is shown. Highlighting this folder, or one of the other folders, gives you a list of properties for that folder in the Properties window of Visual Studio. Table 36-4 details some of the properties you might find in the Properties window. Table 36-4 Property

Description

AllowDirectoryBrowsing

Allows browsing of the selected directory in IIS. The default value is False.

AllowReadAccess

Specifies whether the selected folder should have Read access. The default value is True.

AllowScriptSourceAccess

Specifies the script source access of the selected folder. The default value is False.

AllowWriteAccess

Specifies whether the selected folder should have Write access. The default value is False.

ApplicationProtection

Defines the IIS Application Protection property for the selected folder. Possible values include vsdapLow, vsdapMedium, and vsdapHigh. The default value is vsdapMedium.

AppMappings

Enables you to define the IIS application mappings for the selected folder.

DefaultDocument

Defines the default document of the selected folder. The default value is Default.aspx.

ExecutePermissions

Defines the IIS Execute Permissions property. Possible values include vsdepNone, vsdepScriptsOnly, vsdepScriptsAndExecutables. The default value is vsdepScriptsOnly.

Index

Specifies the IIS Index of this resource property for the selected folder. The default value is True.

www.it-ebooks.info

Looking More Closely at Installer Options  ❘ 

Property

Description

IsApplication

Specifies whether an IIS application root is created for the installed application. The default value is True.

LogVisits

Specifies the IIS Log Visits property for the selected folder. The default value is True.

VirtualDirectory

Defines the name of the virtual directory created. The default value is the name of the project.

1361

Adding Items to the Output You can add files, folders, and assemblies to the installer output quite easily. To add some of these items to the output list, right-click the folder and select Add from the menu. You have four choices: Web Folder, Project Output, File, and Assembly. If you want to add a custom folder to the output (for example, an Images folder), you can select Web Folder and provide the name of the folder. This enables you to create the folder structure you want. If you want to add system folders, you highlight the File System on Target Machine node and then choose Action ➪ Add Special Folder. A large list of folders that are available for you to add to the installer program appears. You can also get at this list of folders by simply right-clicking a blank portion of the left pane of the File System Editor (see Figure 36-28).

Figure 36-28

Table 36-5 defines the possible folders you can add to the installer structure you are building.

www.it-ebooks.info

1362  ❘  Chapter 36   Packaging and Deploying ASP.NET Applications

Table 36-5 Folders and Menus

Description

Common Files Folder

Meant for non-system files not shared by multiple applications.

Common Files (64-bit) Folder

Meant for non-system files on a 64-bit machine not shared by multiple applications.

Fonts Folder

Meant for only fonts you want installed on the client’s machine.

Program Files Folder

A Windows Forms application would be a heavy user of this folder because most applications are installed here.

Program Files (64-bit) Folder

A Program Files folder meant for 64-bit machines.

System Folder

Meant for storing files considered shared system files.

System (64-bit) Folder

Meant for storing files on 64-bit machines considered shared system files.

User’s Application Data Folder

A hidden folder meant for storing data that is application- and user-specific.

User’s Desktop

Meant for storing files on a user’s desktop (also stores these files in the My Desktop folder).

User’s Favorites Folder

Meant for storing files in a user’s Favorites folder (browser-specific).

User’s Personal Data Folder

Meant for storing personal data specific to a single user. This is also referred to as the My Documents folder.

User’s Programs Menu

Meant for storing shortcuts, which then appear in the user’s program menu.

User’s Send To Menu

Meant for storing files that are presented when a user attempts to send a file or folder to a specific application (by right-clicking the folder or file and selecting Send To).

User’s Start Menu

Meant for storing files in the user’s Start menu.

User’s Startup Folder

Meant for storing files that are initiated whenever a user logs into his machine.

User’s Template Folder

Meant for storing templates (applications like Microsoft’s Office).

Windows Folder

Meant for storing files in the Windows root folder. These are usually system files.

Global Assembly Cache Folder

Meant for storing assemblies that can then be utilized by all the applications on the server (shared assemblies).

Custom Folder

Another way of creating a unique folder.

Web Custom Folder

Another way of creating a unique folder that also contains a bin folder.

Creating a Desktop Shortcut to the Web Application For an example of using one of these custom folders, try placing a shortcut to the Web application on the user’s desktop:

1. Right-click on a blank portion of the left pane in the File System Editor and choose Add Special Folder ➪ User’s Desktop. This adds that folder to the list of folders presented in the left pane.



2. Because you want to create a desktop shortcut to the Web Application Folder and not to the desktop

itself, you next right-click the Web Application folder and select Create Shortcut to Web Application Folder. The created shortcut appears in the right pane.



3. Right-click the shortcut and rename it to something a little more meaningful, such as Wrox



4. Because you do not want to keep the shortcut in this folder, drag the shortcut from the Web Application

Application.

Folder and drop it onto the User’s Desktop folder.

With this structure in place, this installer program not only installs the application (as was done previously), but it also installs the application’s shortcut on the user’s desktop.

www.it-ebooks.info

Looking More Closely at Installer Options  ❘ 

1363

The Registry Editor The next editor is the Registry Editor. This editor enables you to work with the client’s registry in an easy and straightforward manner. Using this editor, you can perform operations such as creating new registry keys, providing values for already existing registry keys, and importing registry files. Figure 36-29 shows the Registry Editor.

Figure 36-29

From this figure, you can see that the left pane provides the standard registry folders, such as HKEY_ CLASSES_ROOT and HKEY_LOCAL_MACHINE, as well as others. You right-click one of these folders to add a new key from the menu selection. This creates a new folder in the left pane where it is enabled for renaming. By right-clicking this folder, you can add items such as those illustrated in Figure 36-30.

Figure 36-30

As you can see in the figure, you can add items such as the following: ➤➤

Key

➤➤

String Value

➤➤

Environment String Value

➤➤

Binary Value

➤➤

DWORD Value

Selecting String Value allows you to apply your settings for this item in the right pane, as illustrated in Figure 36-31.

Figure 36-31

The other values work in a similar manner.

The File Types Editor All files on a Windows operating system use file extensions to uniquely identify themselves. A file such as Default.aspx, for example, uses the file extension .aspx. This file extension is then associated with ASP.NET. Another example is .xls. This file extension is associated with Microsoft Excel. When someone

www.it-ebooks.info

1364  ❘  Chapter 36   Packaging and Deploying ASP.NET Applications

attempts to open an .xls file, the file is passed to the Excel program because of mappings that have been made on the computer to associate these two entities. Using the File Types Editor in Visual Studio, you can also make these mappings for the applications you are trying to install. Right-clicking the File Types On Target Machine allows you to add a new file type. From here, you can give your file type a descriptive name and provide a file extension (shown in Figure 36-32).

Figure 36-32

Highlighting the defined file type provides some properties that you can set in the Visual Studio Properties window, as shown in Table 36-6. Table 36-6 Property

Description

Name

Specifies a name used in the File System Editor to identify a file type and its associated settings.

Command

Specifies the executable file (.exe) that is launched when the specified file extension is encountered.

Description

Defines a textual description for the file type.

Extensions

Defines the file extension associated with the executable through the Command property. An example is wrox. You should specify the extension without the period in front of it. If you are going to specify multiple extensions, you can provide a list separated by semicolons.

Icon

Defines the icon used for this file extension.

MIME

Specifies the MIME type associated with this file type. An example is application/msword.

The User Interface Editor The User Interface Editor defines the dialogs used in the installation process. You can change the installation process greatly with the dialogs you decide to use or not use. By default, these dialogs (shown in Figure 36-33) appear in your installer. From this figure, you can see how the dialogs are divided into two main sections. The first section, labeled Install, is the dialog sequence used for a typical install. However, because some applications might require it, a second installation process is defined through the Administrative Install. The Administrative Install process is initiated only if the user is logged onto the machine under the Administrator account. If this is not the case, the Install section is used instead. By default, the Install and Administrative Install sections are exactly the same. Both the Install and Administrative Install sections are

www.it-ebooks.info

Figure 36-33

Looking More Closely at Installer Options  ❘ 

1365

further divided into three subsections: Start, Progress, and End. These sections are defined in the following list: ➤➤

Start: A sequence of dialogs that appears before the installation occurs. By default, the Start section includes a welcome screen, a definition stating where the application is to be installed, and a dialog asking for an installation confirmation.

➤➤

Progress: The second stage, the Progress stage, is the stage in which the actual installation occurs. Throughout this stage no interaction occurs between the installer program and the end user. This is the stage where the end user can watch the installation progress through a progress bar.

➤➤

End: The End stage specifies to the end user whether the installation was successful. Many installer programs use this stage to present the customer with release notes and ReadMe.txt files, as well as the capability to launch the installed program directly from the installer program itself.

Adding Dialogs to the Installation Process Of course, you are not limited to just the dialogs that appear in the User Interface Editor by default. You can add a number of other dialogs to the installation process. For instance, right-click the Start node and select Add Dialog (or highlight the Start node and choose Action ➪ Add Dialog). This opens the Add Dialog dialog, as shown in Figure 36-34. As you can see from this figure, you can add quite a number of different steps to the installation process, such as license agreements and splash screens. After adding a dialog to the process, you can highlight the dialog to get its properties to appear in the Properties window so that you can assign the items needed. For example, you can assign the image to use for the splash screen or the .rtf file to use for the license agreement.

Figure 36-34

When you add an additional dialog to the installation process (for instance, to the Install section), be sure to also install the same dialog on the Administrative Install (if required). If no difference exists between the two user types in the install process, be sure to add the dialogs in unison to keep them the same.

Changing the Order in Which the Dialogs Appear in the Process In working with the dialogs in the Start, Process, and End sections of the User Interface Editor, you can always determine the order in which these dialogs appear. Even if you are working with the default dialogs, you can easily change their order by right-clicking the dialog and selecting Move Up or Move Down, as shown in Figure 36-35.

Figure 36-35

www.it-ebooks.info

1366  ❘  Chapter 36   Packaging and Deploying ASP.NET Applications

The Custom Actions Editor The Custom Actions Editor is a powerful editor that enables you to take the installer one step further and perform custom actions during various events of the installation cycle (but always after the installation process is completed) such as Install, Commit, Rollback, and Uninstall. Figure 36-36 presents the Custom Actions Editor.

Figure 36-36

The idea is that you can place a reference to a .dll, .exe, or .vbs file from one of the folders presented in the Custom Actions Editor to perform a custom action. For example, you can insert a custom action to install a database into Microsoft’s SQL Server in the Commit folder (after the install has actually been committed). Descriptions of the four available folders are as follows: ➤➤

Install: This is the point at which the installation of the files for the Web application are finished being installed. Although the files are installed, this point is right before the installation has been committed.

➤➤

Commit: This is the point at which the actions of the installation have been actually committed (taken) and are considered successful.

➤➤

Rollback: This is the point at which the installation has failed and the computer must return to the same state that it was in before the installation occurred.

➤➤

Uninstall: This is the point at which a successfully installed application is uninstalled for a machine.

Using these capabilities, you can take the installation process to the level of complexity you need for a successfully installed application.

The Launch Conditions Editor Certain conditions are required for your Web application to run on another server automatically. Unless your application is made up of HTML files only, you must make sure that the .NET Framework is installed on the targeted machine to consider the install a success. The Launch Conditions Editor is an editor that you can use to ensure that everything that must be in place on the installation computer for the installation to occur is there. Figure 36-37 shows the Launch Conditions Editor.

Figure 36-37

www.it-ebooks.info

Summary  ❘ 

1367

In this figure, you can see some of the conditions required in this instance. The first folder defines the items that must be in place on the computer where the installation is to occur. A search is done on the computer to see whether IIS is installed. It can also check whether any files or registry keys are present on the computer before the installation occurs. The second folder is important because certain conditions must be in place before the installation. This folder shows two conditions. One is that the .NET Framework must be installed, and the second is that IIS must be installed. You can add these types of launch conditions by right-clicking the Requirements On Target Machine node in the dialog. A short list of conditions then appears. After a condition is in place, you can highlight the condition to see the property details of this condition in the Properties window. For instance, highlighting the IIS Condition gives you some basic properties in the Properties window. One of these is the Condition property. By default, for an IIS Condition, the value of the Condition property is the following: IISVERSION >= "#4"

This means that the requirement for this installation is that IIS must be equal to or greater than version 4. If it is not, the installation fails. If the IIS version is 4, 5, or 6, the installation can proceed. You can feel free to change this value to whatever you deem necessary. You can change the value to IISVERSION >= “#5”, for example, to ensure it is either IIS 5.0, 6.0, or 7.0 at a minimum. Another example of fine-tuning these launch conditions is the .NET Framework condition that enables you to set the minimum version of the .NET Framework you want to allow. You do this by setting the Version property of the condition.

Summary As you can see, you have many possibilities for installing your ASP.NET applications — from the simplest mode of just copying the files to a remote server (sort of a save-and-run mode) to building a complex installer program that can run side events, provide dialogs, and even install extra items such as databases and more. Just remember that when working on the installation procedures for your Web applications, you should be thinking about making the entire process logical and easy for your customers to understand. You do not want to make people’s lives too difficult when they are required to programmatically install items on another machine.

www.it-ebooks.info

www.it-ebooks.info

A

Migrating older asP.neT Projects In some cases, you build your ASP.NET 4 applications from scratch — starting everything new. In many instances, however, this is not an option. You need to take an ASP.NET application that was previously built on the 1.0, 1.1, 2.0, or 3.5 versions of the .NET Framework and migrate the application so that it can run on the .NET Framework 4. This appendix focuses on migrating ASP.NET 1. x, 2.0, or 3.5 applications to the 4 Framework.

migraTing is noT difficulT Be aware that Microsoft has done a lot of work to ensure that the migration process from ASP.NET 1. x is as painless as possible. In most cases, your applications run with no changes needed. When moving a 1. x, 2.0, or 3.5 application to 4, you don’t have to put the ASP.NET application on a new server or make any changes to your present server beyond installing the .NET Framework 4. After you install the .NET Framework 4, you see the framework versions on your server at C:\WINDOWS\Microsoft.NET\Framework, as illustrated in Figure A-1. In this case, you can see that all the official versions of the .NET Framework installed, including v1.0.3705, v1.1.4322, v2.0.50727, v3.0, v3.5, and v4.0.

www.it-ebooks.info

1370  ❘  Appendix A   Migrating Older ASP.NET Projects

Figure A-1

Running Multiple Versions of the Framework Side by Side From this figure, you can see that running multiple versions of the .NET Framework side by side is possible. ASP.NET 1.0, ASP.NET 1.1, ASP.NET 2.0, ASP.NET 3.5, and ASP.NET 4 applications can all run from the same server. Different versions of ASP.NET applications that are running on the same server run in their own worker processes and are isolated from one another.

Upgrading Your ASP.NET Applications When you install the .NET Framework 4, it does not remap all your ASP.NET applications so that they now run off of the new framework instance. Instead, you selectively remap applications to run off of the ASP.NET 4 Framework. To accomplish this task if you are migrating ASP.NET 1.x applications to ASP.NET 2.0, you use the ASP.NET MMC Snap-In that is a part of the .NET Framework 2.0 install. You get to this GUI-based administration application by rightclicking and selecting Properties from the provided menu using Windows XP when you are working with your application domain in Microsoft’s Internet Information Services (IIS). After selecting the MMC console (the Properties option), select the ASP. NET tab, and something similar to that shown in Figure A-2 appears. You can see from this figure that an option exists for selecting the application’s ASP.NET version (the top-most option). This allows you to select

Figure A-2

www.it-ebooks.info

Migrating is not difficult

❘ 1371

the version of the .NET Framework in which this ASP.NET application should run. In this case, the Wrox application on my machine was retargeted to the 2.0 release of ASP.NET when I selected 2.0.50727 in the drop -down list. You should always test your older ASP.NET application by fi rst running on the newer version of ASP.NET in a developer or staging environment. Do not change the version to a newer version on a production system without fi rst testing for any failures. If you are not ready to upgrade your entire application to a newer version of ASP.NET, one option is to create additional virtual directories in the root virtual directory of your application and target the portions of the application to the versions of the .NET Framework that you want them to run on. This enables you to take a stepped approach in your upgrade process. If you are upgrading from ASP.NET 2.0 or ASP.NET 3.5 to ASP.NET 4, there really is very little that you have to do. Upgrading to version 4 is a bit different than it was when upgrading from version 2.0 to 3.5 because the 3.5 version of the .NET Framework was built upon the .NET Framework 2.0. In this case, the System.Web DLL in both versions of the framework was the same. Now, though, the .NET Framework 4 is a complete recompilation of the framework. The differences are even more evident when working with the IIS Manager on Windows 7. From this management tool, you can see that the DefaultAppPool is running off version 4.0. xxxxx of the .NET Framework, as shown in Figure A-3.

figure a-3

Upgrading your application to ASP.NET 4 using Visual Studio 2010 will cause the IDE to make all the necessary changes to the application’s configuration fi le. This is illustrated later in this appendix.

www.it-ebooks.info

1372  ❘  Appendix A   Migrating Older ASP.NET Projects

When Mixing Versions — Forms Authentication If you have an ASP.NET application that utilizes multiple versions of the .NET Framework then, as was previously mentioned, you must be aware of how forms authentication works in ASP.NET 2.0, 3.5, and 4. In ASP.NET 1.x, the forms authentication process uses Triple DES encryption (3DES) for the encryption and decryption process of the authentication cookies. Ever since ASP.NET 2.0, though, it has now been changed to use the AES (Advanced Encryption Standard) encryption technique. AES is faster and more secure. However, because the two encryption techniques are different, you must change how ASP.NET 4 generates these keys. You do this by changing the < machineKey> section of the web.config file in your ASP.NET 4 application so that it works with Triple DES encryption instead (as presented in Listing A-1). Listing A-1:  Changing your ASP.NET 4 application to use Triple DES encryption

By changing the machine key encryption/decryption process to utilize Triple DES, you enable the forms authentication to work across an ASP.NET application that is using both the .NET Framework 1.x and 4. Also, this example shows the validationKey and decryptionKey attributes using a specific set of keys. These keys should be the same as those you utilize in your ASP.NET 1.x application. You should understand that you are not required to make these changes when upgrading an ASP.NET 2.0 or 3.5 application to ASP.NET 4 because they are all enabled to use AES encryption and are not using Triple DES encryption. If you are mixing an ASP.NET 1.x application along with ASP.NET 2.0, 3.5, or 4, then you must move everything to use Triple DES encryption, as shown in Listing A-1.

Upgrading — ASP.NET Reserved Folders As described in Chapter 1 of this book, ASP.NET 4 includes a number of application folders that are specific to the ASP.NET Framework. In addition to the Bin folder that was a reserved folder in ASP.NET 1.x, the following folders are all reserved in ASP.NET 2.0, 3.5, and 4: ➤➤

Bin: This folder stores the application DLL and any other DLLs used by the application. This folder was present in both ASP.NET 1.0 and 1.1. It is also present in both ASP.NET 2.0, 3.5, and 4.

➤➤

App_Code: This folder is meant to store your classes, .wsdl files, and typed datasets. Any items stored in this folder are automatically available to all the pages within your solution.

➤➤

App_Data: This folder holds the data stores utilized by the application. It is a good, central spot to store all the data stores used by your application. The App_Data folder can contain Microsoft SQL Express files (.mdf files), Microsoft Access files (.mdb files), XML files, and more.

➤➤

App_Themes: Themes are a way of providing a common look-and-feel to your site across every page. You implement a theme by using a .skin file, CSS files, and images used by the server controls of your site. All these elements can make a theme, which is then stored in the App_Themes folder of your solution.

www.it-ebooks.info

ASP.NET 4 Pages Come as XHTML  ❘ 

1373

➤➤

App_GlobalResources: This folder enables you to store resource files that can serve as data dictionaries for your applications if these applications require changes in their content (based on things such as changes in culture). You can add Assembly Resource Files (.resx) to the App_GlobalResources folder, and they are dynamically compiled and made part of the solution for use by all the .aspx pages in the application.

➤➤

App_LocalResources: Quite similar to the App_GlobalResources folder, the App_LocalResources folder is a simple method to incorporate resources that can be used for a specific page in your application.

➤➤

App_WebReferences: You can use the App_WebReferences folder and have automatic access to the remote Web services referenced from your application.

➤➤

App_Browsers: This folder holds .browser files, which are XML files used to identify the browsers making requests to the application and to elucidate the capabilities these browsers have.

The addition of the App_ prefix to the folder names ensures that you already do not have a folder with a similar name in your ASP.NET 1.x applications. If, by chance, you do have a folder with one of the names you plan to use, you should change the name of your previous folder to something else because these ASP.NET 4 application folder names are unchangeable.

ASP.NET 4 Pages Come as XHTML ASP.NET 4, by default, constructs its pages to be XHTML-compliant. You can see the setting for XHTML 1.0 Transitional in the Visual Studio 2010 IDE, as shown in Figure A-4.

Figure A-4

www.it-ebooks.info

1374  ❘  Appendix A   Migrating Older ASP.NET Projects

In this case, you can see a list of options for determining how the ASP.NET application outputs the code for the pages. By default, it is set to XHTML 1.0 Transitional. You can also make a change to the web.config file so that the output is not XHTML-specific (as illustrated in Listing A-2). Listing A-2:  Reversing the XHTML capabilities of your ASP.NET 4 application

Setting the mode attribute to Legacy ensures that XHTML is not used, but instead, ASP.NET 4 will use what was used in ASP.NET 1.x. Note that using the Legacy setting as a value for the mode attribute will sometimes cause you problems for your application if you are utilizing AJAX. One of the symptoms that you might experience is that instead of doing a partial page update (as AJAX does), you will get a full-page postback instead. This is because the page is not XHTML compliant. The solution is to set the mode property to Traditional or Strict and to make your pages XHTML compliant. If you take this approach, you also have to make some additional changes to any new ASP.NET 4 pages that you create in Visual Studio 2010. Creating a new ASP.NET 4 page in Visual Studio 2010 produces the results illustrated in Listing A-3. Listing A-3:  A typical ASP.NET 4 page <%@ Page Language="VB" %> Untitled Page


From this listing, you can see that a element is included at the top of the page. This element signifies to some browsers (such as Microsoft’s Internet Explorer) that the page is XHTMLcompliant. If this is not the case, then you want to remove this element altogether from your ASP.NET 4 page. In addition to the element, you also want to change the element on the page from

to the following:

www.it-ebooks.info

Converting ASP.NET 1.x Applications in Visual Studio 2010  ❘ 

1375

The original also signifies that the page is XHTML-compliant (even if it is not) and must be removed if your pages are not XHTML-compliant.

No Hard-Coded .js Files in ASP.NET 4 ASP.NET 1.x provides some required JavaScript files as hard-coded .js files. For instance, in ASP.NET a JavaScript requirement was necessary for the validation server controls and the smart navigation capabilities to work. If you are utilizing either of these features in your ASP.NET 1.x applications, ASP.NET could pick up the installed .js files and use them directly. These .js files are found at C:\WINDOWS\Microsoft.NET\Framework\v1.0.3705\ASP.NETClientFiles. Looking at this folder, you see three .js files — two of which deal with the smart navigation feature (SmartNav.js and SmartNavIE5.js) and one that deals with the validation server controls (WebUIValidation.js). Because they are hard-coded .js files, opening them and changing or altering the code in these files to better suit your needs is possible. In some cases, developers have done just that. If you have altered these JavaScript files in any manner, you must change some code when migrating your ASP.NET application to ASP.NET 2.0, 3.5, or 4. ASP.NET 4 dynamically includes .js files from the System.Web.dll instead of hard-coding them on the server. In ASP.NET 4, the files are included via a handler — WebResource.axd.

Converting ASP.NET 1.x Applications in Visual Studio 2010 As previously mentioned, if you have a pre-existing ASP.NET 1.x application, you can run the application on the ASP.NET 4 runtime by simply making the appropriate changes in IIS to the application pool. Using the IIS manager or the MMC Snap-In, you can select the appropriate framework on which to run your application from the provided drop-down list. ASP.NET 4 applications work with the Visual Studio 2010 IDE. If you still intend to work with ASP.NET 1.0 or 1.1 applications, you should keep Visual Studio .NET 2002 or 2003, respectively, installed on your machine. Installing Visual Studio 2010 gives you a complete, new copy of Visual Studio and does not upgrade the previous Visual Studio .NET 2002 or 2003 IDEs. All copies of Visual Studio can run side by side. If you want to run ASP.NET 1.x applications on the .NET Framework, but you also want to convert the entire ASP.NET project for the application to ASP.NET 4, you can use Visual Studio 2010 to help you with the conversion process. After the project is converted in this manner, you can take advantage of the features that ASP.NET 4 offers. To convert your ASP.NET 1.x application to an ASP.NET 4 application, you simply open the solution in Visual Studio 2010. This starts the conversion process. When you open the solution in Visual Studio 2010, it warns you that your solution will be upgraded if you continue. It does this by popping up the Visual Studio Conversion Wizard, as presented in Figure A-5.

Figure A-5

www.it-ebooks.info

1376  ❘  Appendix A   Migrating Older ASP.NET Projects

Notice that the upgrade wizard has been dramatically improved from the early days of Visual Studio .NET to this newer one provided by Visual Studio 2010. To start the conversion process of your ASP.NET 1.x applications, click the Next button in the wizard. This example uses the open source Issue Tracker Starter Kit — an ASP.NET 1.1 starter kit found on the ASP.NET Web site at www.asp.net. The first step in the process is deciding whether you want the Visual Studio 2010 Conversion Wizard to create a backup of the ASP.NET 1.1 application before it attempts to convert it to an ASP.NET 4 application. Definitely, if this is your only copy of the application, you want to make a backup copy even though this conversion wizard does a good job in the Figure A-6 conversion process. The conversion wizard also enables you to specify the location where you want to store the backup copy, as shown in Figure A-6. The final step is a warning on how to handle the project if it is controlled by a source control system. If it is, you want to ensure that the project or any of its components are checked out by someone. You also want to ensure that the check-in capabilities are enabled. This warning is shown in Figure A-7. When you are ready to convert the project, click the Finish button. The actual conversion process could take some time, so allow a few minutes for it. When the process is complete, you are offered a completion notification that also enables you to see the conversion log that was generated from the conversion process (see Figure A-8).

Figure A-7

Figure A-8

After the first part of the conversion process for this project is done, the conversion log appears, as shown in Figure A-9.

www.it-ebooks.info

Converting ASP.NET 1.x Applications in Visual Studio 2010  ❘ 

1377

Figure A-9

The next step is to convert the project to a Web Application. To do so, right-click on the project within the Visual Studio 2010 Solution Explorer and select Convert to Web Application from the provided menu. The warning shown in Figure A-10 then appears. As you look over the project in the Solution Explorer, notice that some major changes have been made to the project. Some of these changes include the following:

Figure A-10

➤➤

All class files are removed from their folders and placed in the new App_Code folder. The folder from which the class files were removed is left in place, even if the folder is empty after all the class files are removed.

➤➤

All the Visual Studio .NET 2002/2003 Web project files are deleted because Visual Studio 2010 does not use these any longer.

➤➤

The application’s DLL is deleted from the Bin folder.

➤➤

The code-behind classes for the .aspx pages are converted to partial classes (presented here in C#). This is what the code behind for the Default.aspx page looked like before the conversion: public class _Default : System.Web.UI.Page { // Code removed for clarity }

➤➤

After the conversion process, the page class appears as shown here: public partial class _Default : System.Web.UI.Page { // Code removed for clarity }

www.it-ebooks.info

1378 ❘ aPPendix a MIGRATING OLDER ASP�NET PROJECTS

After the project is converted, you can build and run the application from Visual Studio 2010. The application is now built and run on the ASP.NET 4 runtime. Remember: Do not upgrade production solutions without testing your programs fi rst in a staging environment to ensure that your application is not affected by the changes between versions of the .NET Framework.

migraTing from asP.neT 2.0/3.5 To 4 Visual Studio 2010 enables you to build applications at more than one framework. For instance, Visual Studio .NET 2002 would only let you build 1.0 applications. If you wanted to build .NET Framework 1.1 applications, then you were required to install and use Visual Studio .NET 2003. At the same time, Visual Studio .NET 2003 would not enable you to build .NET Framework 1.0 applications, meaning that if you were dealing with applications that made use of either framework, then you were required to have both IDEs on your computer. When you create a new project in Visual Studio 2010, you have the option of targeting the project at any of the following frameworks: ➤

.NET Framework 2.0



.NET Framework 3.0



.NET Framework 3.5



.NET Framework 4

If you open an ASP.NET application that is built upon the .NET Framework 2.0, you can retarget the application to a newer version of the framework quite easily from the IDE. To do this, right- click on the project in the Solution Explorer and select Property Pages from the provided menu. This gives you a form that enables you to change the target framework of the application. In this case, you can see the default options on a Microsoft Windows 7 computer (as shown in Figure A-11).

figure a-11

www.it-ebooks.info

Summary  ❘ 

1379

Changing the target framework as is illustrated in Figure A-11, you will notice that Visual Studio 2010 needs to close and reopen your solution. After this is complete, you will see that even the web.config file was changed to account for working with the newer version of the framework. You might have to address some issues that deal with any breaking changes that have been presented between the releases of ASP.NET, but you can get a quick list of those problems by building the solution within Visual Studio.

Summary The nice thing with the Visual Studio 2010 IDE is that you are able to upgrade just your ASP.NET solution and not upgrade the framework version that your solution is targeted to. Though, in upgrading your ASP.NET solution to the .NET Framework 4, you might find that Visual Studio makes this an easy task to achieve. This appendix took a look at upgrading using the IDE as well as some important changes between the releases that are aimed at making your migration as easy as possible.

www.it-ebooks.info

www.it-ebooks.info

B

asP.neT Ultimate Tools I’ve always believed that I’m only as good as my tools. I’ve spent years combing the Internet for excellent tools to help me be a more effective developer. There are thousands of tools out there to be sure, many overlapping in functionality with others. Some tools do one thing incredibly well and others aim to be a Swiss Army knife with dozens of small conveniences packed into their tiny toolbars. Here is a short, exclusive list of some of the ASP.NET tools that I keep turning back to. These are tools that I fi nd myself using consistently while developing ASP.NET-based Web sites. I recommend that you give them a try if they sound useful. Many are free; some are not. In my opinion, each is worth at least a trial on your part, and many are worth your hard- earned money as they’ll save you precious time. These tools can be easily searched for in your favorite search engine and found in the fi rst page. For those that are harder to fi nd, I’ve included URLs. I also encourage you to check out my annually updated Ultimate Tools List at www.hanselman.com/tools, and you might also enjoy my weekly podcast at www.hanselminutes.com as we often discover and share new tools for the developer enthusiast. Enjoy! — Scott Hanselman

deBugging made easier “ There has never been an unexpectedly short debugging period in the history of computers.” — steven levy

firebug There are so many great things about this application one could write a book about it. Firebug is actually a Firefox plug-in, so you’ll need to download and install Firefox to use it.

www.it-ebooks.info

1382 ❘ aPPendix B ASP�NET ULTIMATE TOOLS

Figure B -1 shows Firebug analyzing all the network traffic required to download my page. This shows a very detailed graph of when each asset is downloaded and how long it took from fi rst byte to last byte as seen in Figure B -1.

figure B-1

It has a wealth of interesting features that allow you to inspect HTML and deeply analyze your CSS, including visualization of some more complicated CSS techniques such as offsets, margins, borders, and padding. Firebug also includes a powerful JavaScript debugger that will enable you to debug JavaScript within Firefox. Even more interesting is its JavaScript profi ler and a very detailed error handler that helps you chase down even the most obscure bugs. Finally, Firebug includes an interactive console feature like the Visual Studio Immediate window that lets you execute JavaScript on-the-fly, as well as console debugging that enables classic “got here” debugging. Firebug is indispensable for the Web developer and it’s highly recommended. There is also Firebug Lite in the form of a JavaScript file. You can add it to the pages in which you want a console debugger to work in Internet Explorer, Opera, or Safari. This file will enable you to do “got here” debugging using the Firebug JavaScript console.log method.

yslow YSlow is an add- on to an add- on. Brought to you by Yahoo!, YSlow extends Firebug and analyzes your Web pages using Yahoo!’s 13 rules for fast Web sites. In Figure B -2, you can see Yahoo!’s YSlow analyzing my blog.

www.it-ebooks.info

Debugging Made Easier  ❘ 

1383

Figure B-2

In some instances, I do well, but in others I receive a failing grade. For example, rule number one says to make fewer HTTP requests. My site has too many external assets. Each one of these requires an HTTP request, so I suspect I could speed up my site considerably with some refactoring. Not every rule will apply to you exactly, but Yahoo! knows what they’re doing and it’s worth your time to use this tool and consider your grades in each category. At the very least, you’ll gain insight into how your application behaves. For example, Figure B-3 shows how many HTTP requests and bytes are transmitted with an empty cache versus a primed one. YSlow is free and is an excellent resource to help you get a clear understanding about how hard the client’s browser must work in order to view your Web site.

Figure B-3

IE8 Developer Tools and Firefox WebDeveloper Both of these developer toolbars are free and absolutely essential for Web development. The IE8 Developer Tools are from Microsoft and come built-in with IE8. You just activate them by pressing F12. It extends Internet Explorer 8 with features such as DOM inspection, JavaScript profiling, and element outlining. You can even visualize the box-model as seen in Figure B-4.

www.it-ebooks.info

1384  ❘  Appendix B   ASP.NET Ultimate Tools

Figure B-4

Firefox has a similar but even more powerful Web Developer Toolbar created by Chris Pederick. It takes a slightly different direction for its user interface by including a number of menus, each literally chock full of menu options. You can disable cookies, CSS, images, inspect elements, form inputs, and outline tables, as shown in Figure B-5.

Figure B-5

www.it-ebooks.info

Debugging Made Easier  ❘ 

1385

ASP.NET developers today need their sites to look great in both browsers. These two toolbars put a host of usefulness at your fingertips. Highly recommended.

jQuery and jQuery UI While not explicitly “tools,” the JavaScript libraries jQuery and its partner jQuery UI make complex JavaScript tasks a joy. Yes, there were JS libraries before jQuery, but it’s hard to overestimate how much jQuery not only changed the Web, but made JavaScript fun again. JQuery includes a clean selector engine that makes moving around the HTML DOM (Document Object Model) trivial, allowing you to select and filter nodes and easily apply events and animations to them. JQuery also includes methods for easily making AJAX calls. It’s such a great library that the Microsoft ASP.NET MVC team decided to ship jQuery with ASP.NET MVC, making it the first Open Source product to ship with .NET along with full support. The IntelliSense improvements in the Visual Studio 2010 IDE along with the jQuery “vs-doc” files that Microsoft contributed back to the jQuery team mean that using jQuery in ASP.NET 4 is a natural fit. JQuery UI is an additional library that adds even more animation support on top of jQuery, but more importantly adds a scaffold for themeable high-level widgets like sliders, calendars, and more. Check them out at http://jquery.com and http://jqueryui.com as shown in Figure B-6.

Figure B-6

www.it-ebooks.info

1386  ❘  Appendix B   ASP.NET Ultimate Tools

Profilers: dotTrace or ANTS If you’re not measuring your code with a good profiler you really don’t realize what you’re missing out on. Only a profiler can give you extensive metrics and a clear understanding of what your code is doing. Some SKUs of Visual Studio 2010 include a Profiler in the top-level Analyze menu. In addition there are excellent third-party profilers such as JetBrains’ dotTrace and Red Gate Software’s ANTS that are worth your 10-day trial. .NET profilers instrument a runtime session of your code and measure how many times each line is hit and how much time is spent executing that line, as shown in Figure B-7. They create a hierarchical series of reports that allow you to analyze time spent not only within a method, but within child methods executed through the stack. Reports can be saved and multiple versions can be analyzed as you improve your applications, revision after revision.

Figure B-7

If you haven’t already done so, consider adding profiling of your ASP.NET application to your software development lifecycle. You’d be surprised to learn how few developers formally analyze and profile their applications. Set aside some time to profile an application that you’ve never looked at before and you’ll be surprised how much faster it can be made using analysis from a tool such as ANTS or dotTrace.

References “He who lends a book is an idiot. He who returns the book is more of an idiot.”



— A nonymous, A rabic P roverb

www.it-ebooks.info

References  ❘ 

1387

PositionIsEverything.net, QuirksMode.org, and HTMLDog.com When you’re creating Web sites that need to look nice on all browsers, you’re bound to bump into bugs, “features,” and general differences in the popular browsers. Web pages are composed of a large combination of standards (HTML, CSS, JS). These standards are not only open to interpretation, but their implementations can differ in subtle ways, especially when they interact. Reference Web sites, such as PositionIsEverything and QuirksMode, collect hundreds of these hacks and workarounds. Then they catalog them for your benefit. Many of these features aren’t designed, but rather discovered or stumbled upon. HTMLDog is a fantastic Web designer’s resource for HTML and CSS. It’s full of tutorials, articles, and a large reference section specific to XHTML. QuirksMode includes many resources for learning JavaScript and CSS and includes many test and demo pages demonstrating the quirks. PositionIsEverything is hosted by John and Holly Bergevin and showcases some of the most obscure bugs and browser oddities with demo examples for everything.

Visibone Visibone is known for its amazing reference cards and charts that showcase Color, Fonts, HTML, JavaScript, and CSS. Visibone reference cards and booklets are available online and are very reasonably priced. The best value is the Browser Book available at www.visibone.com/products/browserbook.html. I recommend the laminated version. Be sure to put your name on it because your co-workers will make it disappear.

www.asp.net I work for Microsoft on the team that runs www.asp.net. The site is a huge resource for learning about ASP. NET and the various technologies around it. Figure B-8 shows part of the Community page for the site, where you’ll link to my Weblog, among others, and links to other community resources. The www.asp.net/learn/ section includes dozens and dozens of videos about general ASP.NET and how to use it.

Figure B-8

www.it-ebooks.info

1388  ❘  Appendix B   ASP.NET Ultimate Tools

Tidying Up Your Code “After every war someone has to tidy up.”



— Wislawa Szymborska

Refactor! for ASP.NET from Devexpress Refactoring support in Visual Studio 2010 continues to get better. The third-party utilities continue to push the envelope, adding value to the IDE. Refactor! for ASP.NET adds refactoring to the ASP.NET source view. For example, in Figure B-9 while hovering over the Refactor! context menu and selecting the “Extract UserControl” refactoring, a preview of the changes that would be made appear within the source view. A new UserControl would be created in a new file WebUserControl0.ascx. The currently selected label control would turn into a WebUserControl0 control. You can then choose a new name for the UserControl immediately after the refactoring.

Figure B-9

The most amazing thing about Refactor! for ASP.NET is that it’s a free download from www.devexpress.com/Products/NET/IDETools/RefactorASP/. It includes 28 refactorings that make it easier to simplify your code and your ASP.NET markup.

Code Style Enforcer Code Style Enforcer from Joel Fjordén does just that. It’s a DXCore Plugin that enforces code style rules that you configure. DXCore is the free engine from DevExpress that Refactor! uses to extend Visual Studio.

www.it-ebooks.info

Tidying Up Your Code  ❘ 

1389

Every team has a coding standard that they’d like programmers to follow, but it’s not only hard to keep track of all the rules, it’s tedious. Are methods supposed to be CamelCased or Pascalcased? Are we putting “m_” in front of member fields? Code Style Enforcer is a lot like Microsoft Word’s spelling and grammar checker except for code. As shown in Figure B-10, identifiers that do not meet the code style guidelines are underlined in red, though you can’t see the color in the black and white figure here. You can right-click on each error, and Code Style Enforcer will use DxCore to refactor and fix each violation.

Figure B-10

Style guidelines are configurable and the default uses Juval Lowy’s excellent C# Code Style Guidelines available from www.idesign.net. The latest version will also generate code rule violation reports for a solution using XML and XSLT, providing customizable different templates. Code Style Enforcer is an excellent tool to add to your team’s toolbox.

Microsoft Ajax Minifier — JavaScript Minimizer When creating an ASP.NET Web site, you often find yourself creating custom JavaScript files. During development, you want these files to be commented and easy to read. During production, however, every byte counts and it’s nice to reduce the size of your JavaScript files with a JavaScript “minimizer.” Microsoft Ajax Minifier is a C# application that offers compression of JavaScript or simple “minification” by stripping comments and white space. It’s been released on CodePlex within the ASP.NET project at http://aspnet.codeplex.com.

www.it-ebooks.info

1390  ❘  Appendix B   ASP.NET Ultimate Tools

You’d be surprised how well these techniques work. For example, Steve Kallestad once reported that a copy of the JavaScript library Prototype 1.50 was 70K before JavaScript-specific compression. It became 30K after the process, and then reached only 14K when gzip HTTP compression was applied. From 70K to 14K is a pretty significant savings. JavaScript-specific compression does things such as renaming variables to single letters, being aware of global variable renaming vs. local variable renaming, as well as stripping unnecessary white space and comments. Microsoft Ajax Minifier includes utilities for compression at both the command line and within MSBuild projects. The MSBuild targets can be added to your build process. Consequently, your integration server is continuous so you receive these benefits automatically and unobtrusively. As an example, a JavaScript library might start out looking like this: var Prototype = { Version: '1.5.0', BrowserFeatures: { XPath: !!document.evaluate }, ScriptFragment: '(?:)((\n|\r|.)*?)(?:<\/script>)', emptyFunction: function() {}, K: function(x) { return x } }

Minified, the JavaScript might end up looking like this (as an example). . . but it will still work! (c(){f 7.2q(/<\\/?[϶>]+>/5a,"")}),2C:(c(){f 7.2q(P 5d(1m.5s,"9n"),"")}),9j:(c(){k 9m=P 5d(1m.5s,"9n");k 9k=P 5d(1m.5s,"ce");f(7.E(9m)||[]).1F((c(9l){f(9l.E(9k)||["",""])[1]}))}), 3P:(c(){f7.9j().1F((c(4s){f 6A(4s)}))}),cd:(c(){k 1h=N.4f("1h");k 2V=N.cc(7);1h.63(2V);f 1h.2P}),cb:(c(){k 1h=N.4f("1h");1h.2P=7.9i();f 1h.2O[0]?(1h.2O.o>1?$A(1h.2O).2A("",(c(3Y,1G){f 3Y+1G.4j})):1h.2O[0].4j):""}),6J:(c(9h){k E=7.4d().E(/([϶?#]*)(#.*)?$/);h(!E){f{}}f E[1]. 3m(9h||"&").2A({},(c(2E,Q){h((Q=Q.3m("="))[0]){k v=9g(Q[0]);k l=Q[1]?9g(Q[1]):1b;h(2E[v]!==1b){ h(2E[v].3k!=1M){2E[v]=[2E[v>}h(l){2E[v].M(l)}}1k{2E[v]=l}}f 2E}))}),2F:(c(){f 7.3m("")})

There are many JavaScript minimizing libraries available; this is just one of them. However, its options, completeness, and integration with MSBuild make Microsoft Ajax Minifier worth trying out.

Extending ASP.NET “Oh man! :-) I have shoot into my foot myself ;-) Sorry!” — matz



ASP.NET AJAX Control Toolkit The AJAX Control Toolkit is a collaboration between Microsoft and the larger ASP.NET community. Its goal was to provide the largest collection of Web client components available. It includes excellent examples if you want to learn how to write ASP.NET Ajax yourself, and then it gives you the opportunity to give back and have your code shared within the community. There are literally dozens of controls that build on and extend the ASP.NET Ajax Framework. Some of the controls are simple and provide those nice “little touches” such as drop shadows, rounded corners, watermarks, and animations. Others provide highly functional controls such as calendars, popups, and sliders. Complete source is available for all the controls so that they can be extended and improved by you. These controls are more than just samples; they are complete and ready to be used in your applications.

www.it-ebooks.info

Extending ASP.NET  ❘ 

1391

There’s a complete site showcasing the Toolkit and MSAjax at http://www.asp.net/ajax/ with examples of each control so you can try each one to see if it meets your needs, as illustrated in Figure B-11, for example. There’s even a new Content Distribution Network (CDN) so you can let Microsoft pay the bandwidth for hosting these JavaScript libraries. They’ll be faster and closer to the user as well!

Figure B-11

Note also that these JavaScript libraries include “reference tags” for each JavaScript file providing complete JavaScript IntelliSense support within Visual Studio 2008 and above.

Atif Aziz’s ELMAH — Error Logging Modules and Handlers Troubleshooting errors and unhandled exceptions in your applications can be a full-time job. Rather than writing your own custom global exception handlers every time, consider looking at ELMAH (Error Logging Modules And Handlers) from Atif Aziz. It’s a very flexible application-wide error logging facility with pluggable extension points to the interfaces at nearly every location. You can even configure it in your application without re-compilation or even redeployment. Simply modify your web.config file to include the error logging modules and handlers, and then you’ll receive a single Web page to remotely review the entire log of unhandled exceptions. ELMAH captures so much information about exceptions that it can reconstitute the original “yellow screen of death” that ASP.NET would have generated given an exception, even if customErrors was turned off. It’s almost like TiVo for your exceptions! Figure B-12 shows ELMAH, providing a developer’s view, including all the details you might need to debug this error.

www.it-ebooks.info

1392  ❘  Appendix B   ASP.NET Ultimate Tools

Figure B-12

Another clever feature is an RSS feed that shows the last 15 years from your log. This flexible tool is open source and the recent beta includes support for medium trust environments. You can plug in SQL Server or use an XML file to manage your error logs. I highly recommend you take the time to learn about ELMAH.

Helicon’s ISAPI_Rewrite and IIS7 URLRewrite Users of the Apache Web server sing the praises of the power of mod_rewrite, their URL rewriting mechanism. IIS6 users have this available to them in the form of the ISAPI_Rewrite module from Helicon. It’s incredibly fast because it’s written in pure C. It integrates nicely with ASP.NET in the IIS “Classic” Pipeline because URLs are rewritten before ASP.NET realizes anything has happened. IIS7 users can use the new URL Rewriting Module I’ll talk about in a moment. Because it uses regular expressions, ISAPI_Rewrite can initially be very frustrating due to its terse syntax. However, if you are patient, it can be an incredibly powerful tool for your tool belt. I recently discovered that there were a dozen ways to visit my blog that all lead to the same location. This was confusing Google because it appeared that my blog had multiple addresses. I wanted not only to canonicalize my URL but also send back a 301 HTTP redirect to search indexes, thereby raising my standing within the search by appearing to have only one official URL.

www.it-ebooks.info

General Purpose Developer Tools  ❘ 

1393

For example, all of these links were valid ways to reach my blog: ➤➤

www.hanselman.com/blog/

➤➤

www.hanselman.com/blog/default.aspx

➤➤

www.hanselman.com/blog

➤➤

http://hanselman.com/blog/

➤➤

http://hanselman.com/blog/default.aspx

➤➤

http://hanselman.com/blog

➤➤

www.hanselman.com/blog/Default.aspx

➤➤

www.computerzen.com/blog/

➤➤

www.computerzen.com

➤➤

http://computerzen.com/blog/

➤➤

http://computerzen.com/

Notice that there’s a difference between a trailing slash and no trailing slash in the eyes of a search engine. Using ISAPI Rewrite, I created this rather terse but very effective configuration file: [ISAPI_Rewrite] RewriteRule /blog/default\.aspx http\://www.hanselman.com/blog/ [I,RP] RewriteCond Host: ϶hanselman\.com RewriteRule (.*) http\://www.hanselman.com$1 [I,RP] RewriteCond Host: ϶computerzen\.com RewriteRule (.*) http\://www.hanselman.com$1 [I,RP] RewriteCond Host: ϶www.computerzen\.com RewriteRule (.*) http\://www.hanselman.com/blog/ [I,RP]

The I and RP at the end of the line indicate that this match is case insensitive and the redirect should be permanent rather than temporary. The rules that include a $1 at the end of line cause the expression to include any path after the domain name. This allows the rule to apply site-wide and provides these benefits to every single page on my site. It’s powerful and that’s worth your time. There’s also an IIS7-specific URL Rewrite Module available at http://www.iis.net/extensions/ URLRewrite. It is easier to use than ISAPI Rewrite because it includes a complete UI for managing and creating rewrites and an import tool for bringing your existing Apache-style rewrites into IIS7. An extra bonus is that the IIS7 rewrite tool runs inside the managed pipeline for an extra performance boost when you’re using it on ASP.NET applications.

General Purpose Developer Tools “If you get the dirty end of the stick, sharpen it and turn it into a useful tool.” — Gen. Colin L. Powell (R et.)



Telerik’s Online Code Converter Creating samples that should appear in both C# and Visual Basic can be very tedious without the assistance of something like Telerik’s CodeChanger.com. While it’s not an officially supported tool, this little application will definitely get you 80 percent of the way when converting between Visual Basic and C#.

www.it-ebooks.info

1394  ❘  Appendix B   ASP.NET Ultimate Tools

It also understands a surprising number of rather obscure syntaxes, as shown in Figure B-13, where I tried to convert an immediate if from C#’s ?: syntax to VB’s IIf syntax. It’s not only useful for the writer, and blog author, but also anyone who’s trying to switch projects between the two languages.

Figure B-13

WinMerge and Differencing Tools Everyone has their favorite merge tool. Whether yours is WinMerge (Figure B-14), or Beyond Compare, or the old standby WinDiff, just make sure that you have one in your list of tools that you’re very familiar with. When managing large numbers of changes across large numbers of individuals on software development teams, a good merge tool can help you untangle even the most complicated conflicting checkins.

www.it-ebooks.info

General Purpose Developer Tools  ❘ 

1395

Figure B-14

A number of different plug-ins are available for WinMerge that extend its functionality to include comparison of Word and Excel documents and XML files. Other highly recommended merge tools include Beyond Compare from Scooter Software and DiffMerge from SourceGear. Each of these three tools integrates with Windows Explorer, so comparing files is as easy as a right-click.

Reflector If you’re not using Reflector, your .NET developer experience is lesser for it. Reflector is an object browser, decompiler, help system, powerful plug-in host, and incredible learning tool. This tiny utility, originally from Microsoft developer Lutz Roeder and now maintained by Redgate, is consistently listed as the number one most indispensable tool available to the .NET developer after Visual Studio. Reflector is amazing because it not only gives you a representation of the programmer’s intent by transforming IL back into C# or VB, but it includes analysis tools that help you visualize dependencies between methods in the .NET Base Class Library and within your code or any third-party code. In Figure B-15, you can see not only a C# representation of the code inside System.RolePrincipal, but more importantly the methods that use it within the framework. You can continue on as deep as you want within the theoretical call stack.

www.it-ebooks.info

1396  ❘  Appendix B   ASP.NET Ultimate Tools

Figure B-15

While Reflector’s decompilation abilities may become less useful with the release of the Base Class Library source code under the Microsoft Reference License, its abilities as an object browser and its vibrant plug-in community will keep this tool on the top shelf for years to come. After purchasing the software from Lutz, Redgate is taking Reflector even closer to the stuff of legend with a new step-into feature that will allow you to debug into code that you don’t have by automatically decompiling it and “fooling” the debugger into thinking you do have the source.

Process Explorer Last, but unquestionably not least, is Process Explorer from Mark Russinovich. To call it “Task Manager on steroids” would not even begin to do it justice. Process Explorer puts Windows itself under a microscope by allowing you to peer inside your active processes, their threads, and the environment to get a clearer understanding about what is actually going on. Advanced and detailed use of this tool, along with the entire SysInternals Suite of Tools, should be required for all developers. In Figure B-16, I’m looking at the Properties dialog box of an application running under the Visual Studio 2010 Web Server while a debugging session is in process. I can see the process tree, the DLLs loaded into the Web server’s process, their versions, and their paths, making this an excellent tool for debugging assembly loading and versioning issues.

www.it-ebooks.info

Summary  ❘ 

1397

Figure B-16

Summary Having the right tools can mean the difference between a week of time spent with your head against the wall versus five minutes of quick analysis in debugging. The right tool can mean the difference between a tedious and keyboard-heavy code slogging or a refactoring session that is actually pleasant. I encourage you to try each of the tools listed here, as well as to explore the ecosystem of available tools to find those that make your development experience not just more productive but more enjoyable.

www.it-ebooks.info

www.it-ebooks.info

C

silverlight 3 and asP.neT Silverlight is a Web platform from Microsoft that allows you to create and run Rich Internet Applications (RIAs) on all major browsers available on both Mac and Windows. Silverlight applications execute within a browser plug-in installed on the local machine similar to the way Flash animations run. The Silverlight plug-in supports all the wow factors that you would expect from a RIA platform, such as vector-based graphics and animations and full audio and video integration, including high-defi nition video support. Beginning with version 2, the platform introduced a cross-browser, cross-platform version of the .NET Framework. This means developers can write Silverlight applications in any .NET language and use the rich Silverlight base class library, networking stack, controls, and XAML -based UI, all without requiring the end user to install the actual .NET Framework. Although giving more than an introduction to Silverlight 3 is outside the scope of this book, this appendix will focus on the key points that an ASP.NET developer needs to know about integrating a Silverlight application into a new or existing ASP.NET application. The appendix starts by showing you how to create a new Silverlight application in Visual Studio. Next, it looks at how you can add the Silverlight player to your Web pages using HTML and JavaScript, and the available options for configuring the player’s behavior in your application. Finally, the appendix demonstrates how to add interoperability between Silverlight and the browser using JavaScript. If you are interested in learning more about creating RIA applications with Silverlight 3, then you might want to pick up a copy of Wrox’s Silverlight 3 Programmer’s Reference by J. Ambrose Little, Jason Beres, Grant Hinkson, Devin Rader, and Joe Croney (Wiley, 2009).

geTTing sTarTed To get you started creating Silverlight applications, Visual Studio 2010 offers several Silverlightspecific project templates that allow you to set up both a Silverlight project and a Web site to host it. Figure C -1 shows the project template in Visual Studio’s New Project dialog.

www.it-ebooks.info

1400



aPPendix c silverlight 3 And Asp.net

figure c-1

The Silverlight Application project template sets up a new Silverlight application that compiles to the XAP fi le for deployment to your Web server. An XAP fi le is the deployment package that Silverlight 3 uses that generally contains all the assemblies and resources used by an application and a manifest fi le, which tells Silverlight about the contents of the XAP. An XAP file is simply a ZIP archive. You can change the extension of the XAP file from .xap to .zip, and then open the archive using your favorite ZIP utility. The Silverlight Navigation application also sets up a new Silverlight application, but adds references to the assemblies required to use Silverlight 3’s navigation framework and includes a set of default XAML fi les that set up a basic application that uses the navigation framework. The Silverlight Class Library project template sets up a basic Silverlight class library. A class library enables you to create assemblies that are compiled against the Silverlight libraries and that you can reference in other Silverlight application projects. When an external assembly is referenced by a Silverlight application, it is, by default, automatically included in the XAP fi le generated by the Silverlight application. A Silverlight class library might appear to be very similar to a standard class library project; however, one significant difference exists. The Silverlight class library references libraries that are specific to Silverlight, so even though both projects have references to identically named assemblies (such as System and System.Core), these references actually point to different assemblies. You can see an example by looking at the Path property of the assembly references. This detail is important to remember because you cannot simply reference an existing .NET class library in a Silverlight application. It must be explicitly compiled against the Silverlight 3 framework assemblies.

www.it-ebooks.info

Getting Started  ❘ 

1401

As with other Visual Studio project types, you can select the .NET Framework version you want to target from the New Project dialog. When you open the framework version selection drop-down list, however, you will notice that it does not include options for selecting a Silverlight version. Instead, changing the target framework on this dialog determines the framework version the host Web site will be configured for. Clicking the New Project dialog’s OK button loads the New Silverlight Application dialog, shown in Figure C-2, which allows you to configure the Silverlight application. By default, Visual Studio assumes you want to create a new Web application in which to host the Silverlight application, but you can also choose to generate a new Web Site or a new ASP.NET MVC Web Project. Additionally, if you are adding a new Silverlight project to an existing solution, Visual Studio will detect any existing Web sites in that solution and allow you to associate the Silverlight project with the existing site.

Figure C-2

Choosing not to associate the Silverlight application with any Web project means Visual Studio will dynamically generate a host Web page when you run the application. After you configure the Web application association, you are done configuring the Silverlight application, and it is loaded in Visual Studio. Figure C-3 shows the default solution structure of a newly created Silverlight application. In this case, a new Web site project has also been created and added to the solution. You can see that Visual Studio automatically includes in the Silverlight application default App.xaml and MainPage.xaml files that contain the application content. The Web application created with the Silverlight application includes HTML and ASP.NET test host pages, as well as the ClientBin folder, where the Silverlight application’s compiled XAP file is placed. Should you ever want to change the Silverlight application associated with a Web project, or even add additional Silverlight associations to a Web project, you can do so by accessing the Silverlight Applications section of the Web projects Properties dialog, as shown in Figure C-4.

www.it-ebooks.info

Figure C-3

1402  ❘  Appendix C   Silverlight 3 and ASP.NET

Figure C-4

After you have created your new Silverlight application, you can begin to develop your Silverlight application by adding the appropriate XAML and code to the MainPage.xaml file.

Using the Silverlight Plug-in Because Silverlight is a browser plug-in, eventually you must embed the Silverlight player into a host Web page. When you set up a new host Web site, a test ASP.NET page is generated that includes the HTML and JavaScript needed to embed the player, but you can do this manually in a different page by adding an tag to your page, filling in all the appropriate object tags, and including the appropriate JavaScript files and code. Listing C-1 shows the contents of the test ASP.NET page. Listing C-1:   Embedding the Silverlight plug-in into an ASP.NET page AppC


www.it-ebooks.info

1403

1404  ❘  Appendix C   Silverlight 3 and ASP.NET

This HTML embeds the Silverlight plug-in into your page using an tag, and the JavaScript provides functionality such as detecting whether or not the plug-in is installed (and proceeds to install if it is not) and what version is installed. Exploring the HTML markup a bit, you can see that within the object tag are a number of tags, which are used to specify the parameters of the player. Two of the more important parameters of the Silverlight plug-in are minRumtimeVersion and autoUpgrade. The minRunTimeVersion property allows you to specify the minimum Silverlight version the client must have to run your application. As you can see in Listing C-1, the default templates automatically set it to the current Silverlight 3 version. The autoUpgrade property tells the control whether or not it should automatically render the appropriate JavaScript needed to automatically upgrade the client’s version of Silverlight if it does not meet the minimum version requirement. Using these properties together makes providing your end users with a positive experience when interacting with your Web site easy. If the end user has a version of Silverlight installed that is older than the application requires and the Silverlight control is not configured to autoupgrade, then the default template includes content that lets the user know he or she needs to upgrade. You, of course, can customize this content, which is shown in Listing C-2. Listing C-2:   Providing custom content when the Silverlight plug-in is not installed

Whoops!

Looks like you don’t have the right version of Silverlight installed. This means you're missing out on the greatest thing on the Internet!

I really suggest that you go download the latest version of Silverlight as it will greatly enhance your life.



This sample shows custom HTML content added to the object tag, which tells end users about the content they could be viewing if they installed the right version of Silverlight. A handful of other interesting properties are available on the Silverlight control and are discussed in the following sections.

windowless The windowless parameter (which applies only when running on Windows) enables you to configure the Silverlight plug-in to be displayed directly by the browser rather than having its own render window as it normally would. Running the plug-in in windowless mode allows the control’s content to overlap and better blend with other surrounding HTML content. Listing C-3 shows how you can use the windowless property to more seamlessly integrate your Silverlight application into its host HTML page. In this case, the Silverlight application has had its root UserControl’s background color set to its default transparent color.

www.it-ebooks.info

Using the silverlight Plug-in

❘ 1405

lisTing c-3: setting the windowless property ”Get

You can see in Figure C -5 how enabling and disabling the windowless property affects how the plug-in is rendered in the browser.

figure c-5

With the windowless property set to true, the underlying DIV containing the image shows through. The Silverlight plug- in background is set to White by default. Therefore, to achieve the transparency shown in Figure C -5, you must explicitly set the plug- in’s background parameter to Transparent. Also note that the Silverlight User Control template in Visual Studio has its root layout element ’s background property set to White by default, which you also must change to see the transparency shown in Figure C -5.

www.it-ebooks.info

1406  ❘  Appendix C   Silverlight 3 and ASP.NET

Use caution when enabling the windowless property as performance can be significantly hindered when using the plug-in in windowless mode. Specifically, complex animations and high-definition video content will not perform as well when running in windowless mode.

splashScreenSource The splashScreenSource parameter enables you to specify the URI of an XAML file that the Silverlight plug-in should use to replace its default “loading” splash screen. The splash screen is the content that Silverlight displays while downloading and loading its application content, which is typically an XAP file. Replacing the default splash screen enables you to provide a highly customized experience to your users; however, you must note a number of restrictions when providing your own splash screen content. First, unlike the source parameter, which accepts both XAML and XAP files, the splashScreenSource property accepts only a simple XAML file. Second, significant restrictions exist regarding the XAML that is allowed to be run for the splash screen. Finally, the splash screen XAML URI must come from the same domain as the Silverlight application and the hosting page. As part of this step, you must make sure your Web server is properly configured to serve files with a .xaml extension, which may mean adding a new MIME type to your Web server. To create a new splash screen content XAML file, you can simply add a new Silverlight 1.0 JScript Page to your Web application, as shown in Figure C-6.

Figure C-6

Next, you simply add some content to the default canvas of the XAML file. Listing C-4 shows a simple TextBlock as the content of the XAML file. Listing C-4:  Simple splash screen XAML content

Finally, you specify the XAML file as the splashScreenSource in the Silverlight control (Listing C-5).

www.it-ebooks.info

Using the silverlight Plug-in

❘ 1407

lisTing c-5: specifying the splash screen source ”Get

When you run your application, you should now see that the default Silverlight splash screen has been replaced by the custom splash screen content. To test your splash screen, make sure the XAP being downloaded is large enough. Silverlight displays the splash screen only if the content load time exceeds 0.5 seconds. To simulate a longer load time, you can artifi cially infl ate the XAP size by embedding a large resource in your application. Although the XAML in Listing C - 4 is completely static, Silverlight does allow you to provide a better experience to your end users by adding animation to the splash screen XAML, and by using two JavaScript events that the plug-in exposes, which provide information relevant to the plug-in’s loading process. The onSourceDownloadProgressChanged and onSourceDownloadCompleted events provide details about the current state of the source download and notification that the download has completed. Using these events in JavaScript, you can provide your end users with download progress information by using the Silverlight JavaScript API to change the splash screen XAML content. Using the JavaScript API to manipulate XAML is discussed in depth later in this appendix. Note that although the plug-in will fi re the onSourceDownloadCompleted event, when this point in the plug-in lifecycle is reached, the plug-in immediately stops displaying the splash screen content and begins to display main application content. You have no opportunity to provide any type of graceful transition from the splash screen to the main player content.

initParams The initParams parameter enables you to pass initialization parameters that you can use inside of your application into the Silverlight player. initParams accepts a comma-delimited list of key/value pairs, as shown in Listing C - 6. lisTing c- 6: specifying initParams in the silverlight control

(continues)

www.it-ebooks.info

1408  ❘  Appendix C   Silverlight 3 and ASP.NET

Listing c-6  (continued)

”Get


The list of initialization parameters is exposed as a property of type Dictionary off the application’s Startup event arguments. Listing C-7 demonstrates how you can use initialization parameters to alter the content loaded by the Silverlight application at startup. Listing C-7:  Accessing the initParams in the Silverlight application

VB

C#

Private Sub Application_Startup(ByVal o As Object, ByVal e As StartupEventArgs) _ Handles Me.Startup Select e.InitParams("DefaultStartPoint") Case "Customer" Me.RootVisual = New Customer() Case "Order" Me.RootVisual = New Order() Case Else Me.RootVisual = New Home() End Select End Sub private void Application_Startup(object sender, StartupEventArgs e) { this.RootVisual = new Page(); switch (e.InitParams["DefaultStartPoint"]) { case "Customer": this.RootVisual = new Customer(); break; case "Order": this.RootVisual = new Order(); break; default: this.RootVisual = new Home(); break; } }

In this listing, the application uses the InitParams property, which is a member of StartUpEventArgs, within the application’s Startup event. As mentioned earlier, the InitParams passed into the plug-in are exposed as a Dictionary, which allows you to access the parameters as key/value pairs, in this case using the value of the DefaultStartPoint key to select a specific XAML UserControl as the application’s RootVisual.

enablehtmlaccess The enablehtmlaccess parameter indicates whether the Silverlight player can access the Document Object Model (DOM) of the host page. The default value allows access to elements from the same domains. Specifying a true value broadens access to any domain, whereas a false value blocks all DOM access. This property is important if you want to allow or deny communication between the Silverlight plug-in and JavaScript running on a browser, which is discussed later in this appendix.

www.it-ebooks.info

Using the Silverlight Plug-in  ❘ 

1409

enableAutoZoom The enableAutoZoom parameter allows you to configure whether or not the plug-in should respect the zoom settings from its host. For example, in Internet Explorer 8 you can set a zoom level. By default, Silverlight will respect this level as it is changed. Using this parameter you can opt out of this behavior.

enableGPUAcceleration The enableGPUAcceleration parameter allows you to indicate that you want to leverage the video hardware for rendering in your application. To enable this feature you also must set the CacheMode property on the XAML elements in your application that you want to accelerate. You can also use the enableGPUAcceleration parameter in conjunction with several other useful diagnostics parameters, such as the enableCacheVisualization parameter, which allows you to see visually which parts of your application are taking advantage of GPU rendering; enableFramerateCounter, which adds a display showing the current application frame rate; and the enableRedrawRegions parameter, which allows you to see which regions of the plug-in are being redrawn with each frame.

enableNavigation The enableNavigation parameter allows you to control the behavior of the HyperlinkButton controls in the application, configuring that application to allow or disallow navigation to external URIs. The parameter accepts two values: all, which allows HyperlinkButtons to navigate to any URI, and none, which prevents this behavior. Regardless of the parameter setting, relative URIs for internal navigation are always permitted.

Plug-in API The Silverlight plug-in also includes a full client-side API that you can use to interact with the control in the browser using JavaScript. You can find a complete description of the plug-in’s client-side API at http:// msdn.microsoft.com/en-us/library/cc838259(VS.95).aspx. The plug-in’s JavaScript API lets you change various property settings such as the plug-in source, splash screen source, and scale mode. Additionally, you can use these APIs to handle events raised by the plug-in, such as the OnLoad event. Table C-1 lists the events exposed by the Silverlight plug-in and a description of the event. Table C-1 Plug-in Event

Description

onLoad

Occurs when the plug-in and its content are successfully loaded

onError

Occurs when something prevents the plug-in or content from loading

onResize

Occurs when the ActualWidth or ActualHeight properties of the plug-in change

onFullScreenChanged

Occurs when the player enters or leaves Full Screen mode

onZoom

Occurs when the plug-in receives a host-generated zoom event

onSourceDownloadCompleted

Occurs when the plug-in source has been downloaded

onSourceDownloadProgress Changed

Occurs as the download progress of the plug-in changes

www.it-ebooks.info

1410



aPPendix c silverlight 3 And Asp.net

The default host page template in Visual Studio automatically configures a JavaScript handler for the plugin’s onError event in order to provide a more graceful handling of errors that might happen in the player.

silverlighT and javascriPT After you have a Silverlight control embedded in a host Web page, you can begin to add interaction between your Silverlight application running in the Silverlight plug-in, and the host Web page running in a browser window. You have two ways to interoperate between the Silverlight plug-in and the browser. ➤

The fi rst option is to use the plug-in’s JavaScript APIs. Introduced in Silverlight 1.0, these APIs allow developers to programmatically reach into the Silverlight plug-in and manipulate the plug-in or XAML content running inside of the plug-in.

Although most of the original Silverlight 1.0 JavaScript APIs exist in newer versions of Silverlight, certain features have been removed in favor of managed code options now available. For example, the original CreateFromXaml method that was available in the Silverlight 1.0 APIs has been removed in favor of using the managed XamlReader class inside of your Silverlight application.



The second option is to use the HTML Bridge, which was introduced with Silverlight 2. The HTML Bridge is a set of managed APIs that allow you to reach out from the Silverlight plug-in and access elements of the browser, like the Document Object Model (DOM) of the host, as well as to expose managed code contained in the plug-in and allow it to be executed from JavaScript running the host page.

javascript aPi Now, take a look at how you can use the Silverlight plug-in’s JavaScript APIs to reach into the plug-in and manipulate content running in a Silverlight application. Listing C -8 shows a sample that uses JavaScript to get a reference to the Silverlight application’s root visual element. After the reference is obtained, the JavaScript simply displays an alert showing that object’s type. lisTing c-8: accessing the root visual element using Javascript

If you are familiar with JavaScript, then the code in this sample should look fairly simple to you. First, using the getElementById function, the sample gets a reference to the Silverlight plug-in and assigns it to the global plug-in variable. Next, the plugin_onload function, which uses the plug-in variable to access the Silverlight content property, is defined. The content property represents Silverlight’s visual tree, which contains a reference to all the visual elements in the Silverlight application. Finally, the code uses the Root property to access root visual elements of the application’s visual tree. After the JavaScript code is written, you still need a way to tell the Silverlight plug-in that it should use the plugin_onload function to handle its Loaded event. To do that, you can provide the function name to the plug-in by specifying it in a tag:

www.it-ebooks.info

Silverlight and JavaScript  ❘ 

1411

Waiting for the plug-in’s Loaded event to fire before trying to access elements in the Silverlight application is a good idea because trying to access content prior may cause null reference exceptions. Waiting for the Loaded event ensures that Silverlight has completed successfully loading all of its content. Running the code in Listing C-8, you should see that the JavaScript alert tells you that the Root element in the plug-in is of type UserControl. Knowing this, you can start to walk through the rest of the application’s visual tree. The JavaScript APIs also allow you to access and change element properties. For example, suppose you want to dynamically change the text of a TextBlock element in your Silverlight application. You can do this via the JavaScript API by locating the named TextBlock element, and then setting its Text property, as demonstrated in Listing C-9. Listing C-9:  Accessing XAML elements and properties in JavaScript function plugin_onload(sender) { var root = plugin.content.Root; var textBlock1 = root.FindName("textBlock1"); if (textBlock1 != null) { textBlock1.Text = "Hello from the Host!"; } }

This sample shows the use of the plug-in’s FindName method to locate the named element textBlock1 in the element tree. After it is located, you simply set its Text property. You can even get and set dependency properties on elements, although to do that, you must use the getValue and setValue functions provided by the element. Listing C-10 demonstrates setting an attached property on the TextBlock. Listing C-10:  Setting attached properties in JavaScript function plugin_onload(sender) { var root = plugin.content.Root; var textBlock1 = root.FindName("textBlock1"); if (textBlock1 != null) { textBlock1.Text = "Hello from the Host!"; } var currentColumn = textBlock1.getValue("Grid.Column"); if (currentColumn == 0) { textBlock1.setValue("Grid.Column", 1); } }

Being able to access elements contained in the XAML also allows you to connect event handlers to element events. It’s important to note that although the Silverlight 1.0 JavaScript APIs allowed you to access and manipulate every XAML element available in Silverlight 1.0, the same cannot be said of later Silverlight JavaScript APIs. After Silverlight 1.0, a significant number of new XAML elements were added to the platform. These elements make designing and laying out applications much easier; however, not all of those elements have been exposed through the JavaScript API. You can find the full JavaScript API for Silverlight 3 at http:// msdn.microsoft.com/en-us/library/bb979679(VS.95).aspx. This documentation lists all the XAML elements that have been exposed to the JavaScript APIs. Also, with the addition of significant new functionality in Silverlight since version 1.0, many XAML elements gained new properties, methods, and events. However, not all of these properties are useful unless

www.it-ebooks.info

1412  ❘  Appendix C   Silverlight 3 and ASP.NET

used in conjunction with other features available only in the managed API. You can find a list of objects, types, and members that are not accessible via the JavaScript API, or that somehow otherwise expose only limited functionality via the JavaScript API, at http://msdn.microsoft.com/en-us/library/ cc964287(VS.95).aspx.

HTML Bridge Even though the Silverlight JavaScript APIs can be useful, Silverlight contains a powerful set of managed APIs that allow you not only to manipulate XAML elements from JavaScript, but also to access any managed type, method, property, or event included in the Silverlight application from JavaScript. Additionally, the APIs allow you to access the entire browser DOM (including JavaScript code) from within the Silverlight plug-in. Reaching out from the Silverlight plug-in into the browser allows you to add interesting interoperability capabilities to your application, such as accessing properties of the current browser window or leveraging existing JavaScript libraries you may have. The HTML Bridge managed code APIs are contained in the System.Windows.Browser namespace (located in the System.Windows.Browser.dll assembly). The primary class in the System.Windows.Browser namespace that you will work with is the HtmlPage class, whose primary function is to allow you to access and manipulate the browser’s DOM. The class exposes a variety of static properties that enable you to access the actual HTML document, the browser window, basic browser information such as the name and version, and even the Silverlight plug-in itself. Additionally, as you’ll see in the next section, it includes several static methods that help you expose managed code included in your Silverlight application, via JavaScript APIs.

Exposing Managed Code in JavaScript Exposing managed code via JavaScript APIs is a powerful tool that can help form a bridge between managed code developers and developers who are skilled in JavaScript. The easiest way to expose managed types to JavaScript is to use the ScriptableType attribute on the class you want to expose. Using this attribute exposes any public member of the class to JavaScript, including methods, properties, and events. Listing C-11 shows how you can use the ScriptableType attribute on a custom Silverlight class. Listing C-11:  Exposing a class using the ScriptableType attribute

VB

Public Class Employee Private _status As Boolean = False Public Sub New() End Sub Public Public Public Public Public

Property Property Property Property Property

FirstName() As String LastName() As String Department() As String SSN() As String StartDate() As DateTime

Public ReadOnly Property Status As Boolean Get Return _status End Get End Property Public Sub ChangeStatus(ByVal status As Boolean) Me._status = status End Sub

www.it-ebooks.info

Silverlight and JavaScript  ❘ 

1413

Public Function GetFullName() As String Return String.Format("{0} {1}", Me.FirstName, Me.LastName) End Function End Class

C#

[System.Windows.Browser.ScriptableType()] public class Employee { private bool _status = false; public Employee() { } public public public public public public

string FirstName { get; set; } string LastName { get; set; } string Department { get; set; } string SSN { get; set; } DateTime StartDate { get; set; } bool Status { get { return _status; } }

public void ChangeStatus(bool status) { this._status = status; } public string GetFullName() { return string.Format("{0} {1)", this.FirstName, this.LastName); } }

After you have decorated a type with the ScriptableType attribute, you can register instances of that type as scriptable objects. Registering the instances allows them to be accessed from the host Web page using JavaScript. To register an object instance, use the RegisterScriptableObject method of the HtmlPage class, as shown in Listing C-12. Listing C-12:  Registering the scriptable type

VB

Private Sub Application_Startup(ByVal o As Object, ByVal e As StartupEventArgs) _ Handles Me.Startup Me.RootVisual = New MainPage() Dim employee As Employee = New Employee() System.Windows.Browser.HtmlPage.RegisterScriptableObject( "Employee", employee) End Sub

C#

private void Application_Startup(object sender, StartupEventArgs e) { this.RootVisual = new Page(); Employee employee = new Employee(); HtmlPage.RegisterScriptableObject("Employee", employee); }

RegisterScriptableObject requires two parameters: a Key, which represents the name used

to register the object, and the actual object instance you want to expose. Although you can call the RegisterScriptableObject anywhere in your code, in the preceding sample it is called in the

application Startup event, allowing you to access this member in JavaScript as soon as the Silverlight application is loaded.

www.it-ebooks.info

1414  ❘  Appendix C   Silverlight 3 and ASP.NET

Listing C-13 shows you how to use the Silverlight plug-in’s JavaScript API to access the registered object instance from JavaScript and call its ChangeStatus method. Listing C-13:  Accessing scriptable objects from JavaScript function onLoaded(sender) { alert("Current Status: " + plugin.Content.Employee.Status); plugin.Content.Employee.ChangeStatus(true); alert("Updated Status: " + plugin.Content.Employee.Status); }

Notice that Silverlight exposes the managed type as a property of the plug-in’s Content object. The property name exposed from the Content object is determined by the Key parameter provided in the RegisterScriptableObject. Therefore, in Listing C-12, had you used “Foo” as the key, you would have accessed the object in JavaScript by using plugin.Content.Foo.Status. As stated earlier, applying the ScriptableType attribute exposes all public members of a type to the JavaScript API, but you may not want to do that. Thankfully, Silverlight provides the more granular ScriptableMember attribute, which allows you to more specifically control which members of a type are exposed through the JavaScript API. The use of this attribute is shown in Listing C-14. Rather than decorating the entire Employee class with the ScriptableType, only specific members are exposed by using the ScriptableMember attribute. Listing C-14:  Exposing specific class properties using the ScriptableMember attribute Public Class Employee Private _status As Boolean = False

VB

Public Sub New() End Sub Public Property FirstName() As String Public Property LastName() As String Public Property Department() As String Public Property SSN() As String Public Property StartDate() As DateTime Public ReadOnly Property Status As Boolean Get Return _status End Get End Property Public Sub ChangeStatus(ByVal status As Boolean) Me._status = status End Sub Public Function GetFullName() As String Return String.Format("{0} {1}", Me.FirstName, Me.LastName) End Function End Class

C#

public class Employee { private bool _status = false;

www.it-ebooks.info

Silverlight and JavaScript  ❘ 

1415

public Employee() { } [ScriptableMember] public string FirstName { get; set; } [ScriptableMember] public string LastName { get; set; } public string Department { get; set; } public string SSN { get; set; } [ScriptableMember()] public DateTime StartDate { get; set; } [ScriptableMember()] public bool Status { get { return _status; } } public void ChangeStatus(bool status) { this._status = status; } public string GetFullName() { return string.Format("{0} {1)", this.FirstName, this.LastName); } }

The ScriptableMember attribute also enables you to change the name of the member that is exposed through the JavaScript API by setting an alias on the member being exposed. This is shown in the following code where the Status property has been given the alias CurrentStatus. [ScriptableMember(ScriptAlias = "CurrentStatus")] public bool Status { get { return _status; } }

In addition to accessing existing type instances, the HTML Bridge allows you to register specific types as Creatable Types. A Creatable Type is a type that can be instantiated directly in JavaScript. For example, rather than instantiating an instance of the Employee type in managed code and registering that specific instance, Silverlight allows you to register the Employees type as Creatable and instantiate new instances of it directly in JavaScript. To register a type as creatable using the JavaScript API, call the HtmlPage object’s RegisterCreatableType method, as shown here: HtmlPage.RegisterCreatableType("Employee", typeof(Employee));

This method requires two parameters, a ScriptAlias and the type to expose. After it is exposed, you can use JavaScript to instantiate the Employee class, as shown in Listing C-15. Listing C-15:  Creating managed types in JavaScript function onLoaded(sender) { var employee = plugin.Content.services.createObject("Employee"); employee.FirstName = "John"; employee.LastName = "Doe"; }

Notice that to create the type in JavaScript, you use the Silverlight JavaScript API’s createObject function, passing it the ScriptAlias provided to the RegisterCreatableType method. After it is created, you can set properties and call functions on the object just as you would any other JavaScript object.

Accessing the DOM Using Managed Code So far in this section, you have seen how you can expose managed code to JavaScript. However, the HTML Bridge is a two-way street, allowing you to also access the browser. Accessing the browser allows you to

www.it-ebooks.info

1416  ❘  Appendix C   Silverlight 3 and ASP.NET

access the DOM, reference specific elements in the DOM, execute JavaScript functions contained in the host page, or even access aspects of the browser window that contains the host page. In this section, you look at some of the APIs included in the HTML Bridge that can help you access information about the browser window and the HTML document, beginning with returning to the familiar HtmlPage object, which exposes three important properties, BrowserInformation, Document, and Window. The BrowserInformation property returns a BrowserInformation object, which, as the name implies, allows you to access basic information about the browser the application is currently running in such as the browser name, version, and platform. The Document property returns an HtmlDocument object, which represents the browser’s document object. The managed HtmlDocument provides similar functionality to its JavaScript equivalent, allowing you to locate elements in the document using the familiar GetElementByID and GetElementByTagName methods, as well as create new HTML elements and attach and detach events to HTML elements. When working with existing HTML elements obtained from the HtmlDocument, or when new elements are created, the HTML Bridge uses the HtmlElement object, which represents the managed version of basic HTML elements present in the DOM. As with the HtmlDocument object, the HtmlElement object exposes much of the same functionality as its client-side peer, enabling you to get and set element property and attribute values, and access and manipulate its collection of child elements. Listing C-16 demonstrates the use of the HtmlDocument and HtmlElement objects to dynamically manipulate the loaded document structure. Listing C-16:  Manipulating the HTML document structure

VB

Public Sub AddListItem() Dim unorderedlist As System.Windows.Browser.HtmlElement = System.Windows.Browser.HtmlPage.Document.GetElementById( "demoList") If unorderedlist IsNot Nothing Then Dim listitem As System.Windows.Browser.HtmlElement = System.Windows.Browser.HtmlPage.Document.CreateElement( "li") listitem.SetAttribute("Id", "listitem1") listitem.SetAttribute("innerHTML", "Hello World!") unorderedlist.AppendChild(listitem) End If End Sub

C#

public void AddListItem() { System.Windows.Browser.HtmlElement unorderedlist = System.Windows.Browser.HtmlPage.Document.GetElementById( "demoList"); if (unorderedlist != null) { System.Windows.Browser.HtmlElement listitem = System.Windows.Browser.HtmlPage.Document.CreateElement( "li"); listitem.SetAttribute("Id", "listitem1"); listitem.SetAttribute("innerHTML", "Hello World!"); unorderedlist.AppendChild(listitem); } }

www.it-ebooks.info

Summary  ❘ 

1417

In this sample, the HtmlDocument’s GetElementById method is used to locate a specific unordered list element in the DOM. If it is found, then a new HtmlElement representing an HTML list item is created and its id and innerHTML attributes set. Then, the new list item element is added as a child of the unordered list element. Finally, the Window property of the HtmlPage object returns an HtmlWindow, which provides you with a managed representation of the browser’s window object. The HtmlWindow allows you to do things such as raise Alert and Prompt dialogs, navigate to new URIs or bookmarks, create instances of JavaScript types, and evaluate strings containing arbitrary JavaScript code. Listing C-17 demonstrates how to create a managed representation of a JavaScript type. Listing C-17:  Creating a JavaScript type in managed code

VB

C#

Public Sub Calculate() Dim calculator = System.Windows.Browser.HtmlPage.Window.CreateInstance( "Calculator") Dim sum = Convert.ToInt32(calculator.Invoke("add", 5, 1)) System.Windows.Browser.HtmlPage.Window.Alert(sum.ToString()) End Sub public void Calculate() { var calculator = System.Windows.Browser.HtmlPage.Window.CreateInstance( "Calculator"); var sum = Convert.ToInt32(calculator.Invoke("add", 5, 1)); System.Windows.Browser.HtmlPage.Window.Alert(sum.ToString()); }

This sample uses the HtmlWindow object’s CreateInstance method, which requires two parameters, a string containing the type you want to create, and an object array containing the type’s creation parameters. The method returns an instance of a ScriptObject, which you can then use to call methods and properties of the JavaScript type. Finally, take a look at how you can use the HtmlWindow object to call a JavaScript function located in the host Web page. The following code shows a simple JavaScript function that could be included in the HTML page hosting the Silverlight plug-in: function Add(a, b) { return a + b; }

To execute this function, use Silverlight’s Invoke method, which is exposed from the HtmlWindow object, shown here: HtmlWindow window = HtmlPage.Window; object result = window.Invoke("Add", new object[] {1,2});

The Invoke method takes two parameters—the name of the function you want to execute and an object array of function parameters—and returns an object type.

Summary This appendix introduced some of the basic concepts that an ASP.NET developer must know to integrate a Silverlight application into a Web site, and to add interoperability between the Silverlight application and its host Web page. The appendix started by introducing the basics of creating a new Silverlight application and the tools that are available for Visual Studio 2010 developers. It showed you how you can automatically have Visual Studio create a new Web site project to host your Silverlight application or even associate the Silverlight application with an existing Web site.

www.it-ebooks.info

1418  ❘  Appendix C   Silverlight 3 and ASP.NET

Next, you looked at the Silverlight plug-in and how to embed it into a Web page. You looked at the configuration parameters exposed by the plug-in that allow you to customize the default Silverlight loading splash screen and pass initialization parameters into the Silverlight plug-in. Finally, you explored the different options Silverlight provides for interoperating between JavaScript and managed code. You first looked at how to use the Silverlight plug-in’s JavaScript API to reach into a Silverlight application and manipulate its XAML content. This appendix also demonstrated how to use the HTML Bridge to expose managed code contained in a Silverlight application out to the browser via a JavaScript API, to directly access browser properties information from within your Silverlight application, to manipulate the browser’s DOM, and to run client-side JavaScript code from within a Silverlight application.

www.it-ebooks.info

D

dynamic Types and languages When Microsoft originally introduced .NET, many developers, especially those coming from Visual Basic 6 (or earlier), were brought into a new world of statically typed languages. No longer were developers allowed to create variant types, or easily create instances of objects at runtime using late binding. The new .NET-based languages forced them to defi ne all of their objects using a well-known type, and if the compiler did not recognize that type, it would throw errors back at them. Purists out there will argue that .NET does indeed support late binding, and it is true that VB.NET can accommodate late binding through disabling the Option Strict command, though this is certainly discouraged in most situations. In C# if you want to simulate late binding you have to resort to reflection, which usually means a lot more lines of code and application complexity. Although statically typed languages have many advantages such as strict type safety and the ability to leverage the compiler to optimize based on well-known type information, you do lose a bit of flexibility that you get from a dynamically typed language. Additionally, the lack of dynamism does make interoperating between .NET and dynamic languages more difficult. You may have experienced this if you have ever had to create applications that require COM interop. This appendix takes a look at some of the work Microsoft has done to embrace the concepts of dynamic languages and how it continues to make working directly with, as well as interoperating with, dynamic languages easier for developers.

imPliciT TyPes When Microsoft introduced .NET 3.5, one of the new concepts it introduced was the concept of implicit types, which is expressed in C# using the var keyword or in VB.NET by declaring a variable without the As operator. Implicit types allow you to declare a variable whose type is implicitly inferred from the expression used to initialize the variable. In other words, implicit types allow you to declare variables in a fairly dynamic way in your code, while through a bit of compiler magic retaining the benefits of a statically typed variable at runtime. An example of using implicit types is shown in Listing D -1.

www.it-ebooks.info

1420  ❘  Appendix D   Dynamic Types and Languages

Listing D-1:  Implicit types using the var keyword

VB C#

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Dim foo = 1 End Sub protected void Page_Load(object sender, EventArgs e) { var foo = 1; }

In this sample, a variable foo is declared as an implicit type, and immediately assigned a numeric value of “1”. Because the variable is assigned a numeric, the compiler will automatically infer that the variable foo should actually be of type Int32. In fact if you decompile the code to its Intermediate Language (IL), you will see that the code actually output by the compiler emits the variable as the correct type. Trying to assign a different type to this variable as shown in Listing D-2 results in a type mismatch exception. Listing D-2:  Violating static type rules

VB C#

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Dim foo = 1 foo = "abc" End Sub protected void Page_Load(object sender, EventArgs e) { var foo = 1; foo = "abc"; }

Trying to compile this code results in a compiler error letting you know that a string cannot be assigned to the foo variable.

The Dynamic Language Runtime To help developers gain back some of that flexibility, as well as bring the general goodness that the .NET Framework offers to a wider audience of developers, in 2007 Microsoft announced it had begun working on a new initiative called the Dynamic Language Runtime, or DLR. The DLR is a dynamic runtime that is built on top of the Common Language Runtime (CLR). It brings to the CLR a dynamic type system, dynamic method dispatch, dynamic code generation, and a hosting API. Currently, a number of languages have been created on the DLR, including ports of the popular languages Python and Ruby. The IronPython language is the only Dynamic Language implementation officially developed and maintained by Microsoft, and it runs on a release schedule separate from .NET Framework and Visual Studio. Other languages are developed and maintained by third-party developers. You can find more information on IronPython, the DLR implementation of Python, at www.codeplex.com/IronPython. You can find more information on IronRuby, the DLR implementation of Ruby, at www.codeplex.com/IronRuby.

Because the development of DLR-based languages has thus far been separate from the CLR-based languages, support for these languages inside of Visual Studio is generally lacking, with most implementations providing little to no support for common features like project and file templates and code editor support such as code coloring and IntelliSense. After you’ve loaded the languages on your system, you can develop standalone applications using them or leverage language libraries from other static languages like C# or VB.NET. Listing D-3 demonstrates using IronPython code from within an ASP.NET application using both C# and VB.NET.

www.it-ebooks.info

The Dynamic Language Runtime  ❘ 

1421

Listing D-3:  Calling IronPython libraries from ASP.NET <%@ Page Language="VB" Strict="false" %>

VB




continues

www.it-ebooks.info

1422  ❘  Appendix D   Dynamic Types and Languages

Listing D-3  (continued)

C#



You can see in this listing that when the page is loaded, the IronPython library random.py is loaded using the DLR’s hosting API. As each item in the items array is bound to the Repeater, its subitems are shuffled into a random order using the IronPython library.

Dynamic Lookup In C# 4, the language embraces dynamism even more by a new feature called dynamic lookup. Dynamic lookup brings a truly dynamic type declaration mechanism to C#, allowing you to explicitly declare variables as a dynamic type in your code and dynamically invoke methods against that type at runtime. This differs from implicit type in that dynamic types remain truly dynamic even at runtime, whereas implicit types are converted to static types at compile-time. Listing D-4 shows a simple example of using the new dynamic keyword. Listing D-4:  Creating a dynamic type using the dynamic keyword

In this sample, the property value is assigned a simple string as a value; however unlike with implicit types, the variable remains typed as a dynamic type even at runtime. You can see this if you are trying to

www.it-ebooks.info

Dynamic Lookup  ❘ 

1423

access any member of the variable in Visual Studio. Normally Visual Studio would show you a list of available properties and methods as well as the type of the variable, but because this type is dynamic, none of this information is known until runtime. Visual Studio tells you this using a tooltip, as shown in Figure D-1. At runtime, the Dynamic Language’s dynamic dispatch Figure D-1 system uses dynamic invocation to execute methods and properties of the type. This means that you can add and remove members from a type at runtime. .NET provides two mechanisms to do this: the ExpandoObject class and the DynamicObject class. The ExpandoObject class can be used in relatively simple scenarios where you need to add or remove members dynamically. Listing D-5 demonstrates using the ExpandoObject. Listing D-5:  Using the ExpandoObject to define dynamic object properties dynamic contact = new System.Dynamic.ExpandoObject(); contact.Name = "John Doe"; contact.Phone = "201-555-5555"; contact.Address = new System.Dynamic.ExpandoObject(); contact.Address.Street = "123 Main St"; contact.Address.City = "Anywhere"; contact.Address.State = "WA"; contact.Address.Postal = "12345";

In this listing you can see that an ExpandoObject is created and several properties added. These properties are added dynamically at runtime and stored internally as an IDictionary, which the ExpandoObject implements internally to maintain the list of members. If you need more control over what specific operations can be performed on a dynamic object, or what happens when an operation like a getting and setting properties or method call occurs, you can create objects that derive from DynamicObject. An example of deriving from the DynamicObject class is shown using the JsonObject class in Listing D-6. Listing D-6:  Creating a custom DynamicObject type namespace DynamicRest { public sealed class JsonObject : DynamicObject, IDictionary, IDictionary { private Dictionary _members; public JsonObject() { _members = new Dictionary(); } public JsonObject(params object[] nameValuePairs) : this() { if (nameValuePairs != null) { if (nameValuePairs.Length % 2 != 0) { throw new ArgumentException( "Mismatch in name/value pairs."); }

continues

www.it-ebooks.info

1424  ❘  Appendix D   Dynamic Types and Languages

Listing D-6  (continued) for (int i = 0; i < nameValuePairs.Length; i += 2) { if (!(nameValuePairs[i] is string)) { throw new ArgumentException( "Name parameters must be strings."); } _members[(string)nameValuePairs[i]] = nameValuePairs[i + 1]; } } } public override bool TryConvert(ConvertBinder binder, out object result) { Type targetType = binder.ReturnType; if ((targetType == typeof(IEnumerable)) || (targetType == typeof(IEnumerable>)) || (targetType == typeof(IDictionary)) || (targetType == typeof(IDictionary))) { result = this; return true; } return base.TryConvert(binder, out result); } public override bool TryDeleteMember(DeleteMemberBinder binder) { return _members.Remove(binder.Name); } public override bool TryGetMember(GetMemberBinder binder, out object result) { object value; if (_members.TryGetValue(binder.Name, out value)) { result = value; return true; } return base.TryGetMember(binder, out result); } public override bool TrySetMember(SetMemberBinder binder, object value) { _members[binder.Name] = value; return true; } // ++++ // Non-related interface implementations removed for clarity } }

www.it-ebooks.info

Dynamic Lookup  ❘ 

1425

The JsonObject class is part of a larger library based on a sample written by Nikhil Kothari that simplifies retrieving and parsing JSON-formatted data. Normally, to deal with JSON data in .NET, you have to create proxy types that mirror the structure of the JSON and then perform complex parsing operations to parse the JSON data into collections of the custom types. Using the dynamic capabilities of C# 4, you can simplify this by parsing of the JSON data into generic types that expose the data via dynamic properties and methods, which are inferred at runtime. Listing D-6 shows how you can override methods like TryGetMember and TrySetMember to control how properties on the type are gotten and set. In this case, members are stored in an internal Dictionary object in the JsonObject class. Teams within Microsoft are also leveraging the new dynamic capabilities of .NET 4 to make COM interop operations easier. The Office Primary Interop Assemblies (PIAs), which provide a managed layer over the Office COM Automation APIs, have been updated to leverage the dynamic capabilities of C#. Listing D-7 demonstrates how to interact with Excel from .NET using the PIAs. Listing D-7:  Using the new Office PIAs to interact with Excel

VB

Dim excelApp As New Microsoft.Office.Interop.Excel.Application() excelApp.Visible = True excelApp.Workbooks.Add() Dim workSheet As Microsoft.Office.Interop.Excel.Worksheet = excelApp.ActiveSheet workSheet.Cells(1, "A") = "ID Number" workSheet.Cells(1, "B") = "Current Balance" Dim row = 1 For Each acct In accounts row = row + 1 workSheet.Cells(row, "A") = acct.ID workSheet.Cells(row, "B") = acct.Balance Next workSheet.Columns(1).AutoFit() workSheet.Columns(2).AutoFit()

C#

var excelApp = new Application(); excelApp.Visible = true; excelApp.Workbooks.Add(); Worksheet workSheet = excelApp.ActiveSheet; workSheet.Cells[1, "A"] = "ID Number"; workSheet.Cells[1, "B"] = "Current Balance"; var row = 1; foreach (var acct in accounts) { row++; workSheet.Cells[row, "A"] = acct.ID; workSheet.Cells[row, "B"] = acct.Balance; } workSheet.Columns[1].AutoFit(); workSheet.Columns[2].AutoFit();

In prior versions of the PIAs, accessing certain APIs such as the Columns collection shown in the sample would have required you to cast the objects returned to the appropriate type to access that type’s properties and methods. The new versions, however, leverage the dynamic keyword to remove this requirement and allow for dynamic lookup of those properties and methods.

www.it-ebooks.info

1426  ❘  Appendix D   Dynamic Types and Languages

Summary Microsoft continues to expand the functionality of the .NET Framework and its languages by investing in features to bring more dynamism to the languages. These features give you additional programming tools in your toolbelt, allowing you to leverage the features, or even the programming languages that make the most sense for your specific application. From built-in features of C# and VB.NET such as implicit types and dynamic lookup, to entirely new languages such as IronPython and IronRuby, the choices available to you continue to expand.

www.it-ebooks.info

E

asP.neT online resources auThor Blogs and TwiTTer ids Bill Evjen: www.geekswithblogs.net/evjen @billevjen

Scott Hanselman: www.hanselman.com/blog/ @shanselman

Devin Rader: http://blogs.infragistics.com/blogs/devin_rader/ @devinrader

asP.neT influenTial Blogs Scott Guthrie: weblogs.asp.net/scottgu/ Rick Strahl: www.west-wind.com/weblog/ K. Scott Allen: odetocode.com/blogs/scott/ Phil Haack: www.haacked.com/ Steve Smith: www.stevesmithblog.com/ G. Andrew Duthie: blogs.msdn.com/gduthie/ Scott Mitchell: scottonwriting.net/sowBlog/ Nikhil Kothari: www.nikhilk.net/

weB siTes 123ASPX Directory: www.123aspx.com 4 Guys from Rolla: www.4guysfromrolla.com ASP 101: www.asp101.com ASP Alliance: aspalliance.com ASP Alliance Lists: aspadvice.com The ASP.NET Developer Portal: msdn.microsoft.com/asp.net

www.it-ebooks.info

1428  ❘  Appendix E   ASP.NET Online Resources

ASP.NET Homepage: www.asp.net ASP.NET Resources: www.aspnetresources.com ASP.NET World: www.aspnetworld.com International .NET Association: www.ineta.org Microsoft’s ASP.NET AJAX Site: www.asp.net/ajax/ Microsoft’s ASP.NET MVC Site: www.asp.net/mvc/ Microsoft’s Classic ASP Site: msdn.microsoft.com/en-us/library/aa286483.aspx Microsoft Developer Centers: msdn.microsoft.com/developercenters Microsoft Forums: www.microsoft.com/communities/forums/default.mspx Microsoft Newsgroups: msdn.microsoft.com/newsgroups/ Microsoft’s Open Source Project Community: www.codeplex.com .NET 247: www.dotnet247.com RegExLib: www.regexlib.com The ServerSide .NET: www.theserverside.net XML for ASP.NET: www.xmlforasp.net

Twitter Folks Worth Following @scottgu

@csells

@haacked

@keyvan

@brada

@danwahlin

@ambroselittle

@devhammer

@sondreb

@jglozano

@chrislove

@shawnwildermuth

@wrox

@julielermanvt

@dseven

@codinghorror

@randyholloway

@spolsky

@migueldeicaza

@elijahmanor

@donxml

@robconery

@moon

@jeremydmiller

@kvgros

@angrycoder

@richcampbell

@rickstrahl

@christoc

www.it-ebooks.info

Index

A absolute positioning, in CSS, 700 Access Control Lists. See ACLs (Access Control Lists) access rules, managing with Web Site Administration Tool, 1324–1325 AccessDataSource control description of, 239 overview of, 247 AccessKey attribute, for hot-key functionality, 87 Accordion control, 793–795 ACLs (Access Control Lists) adding rule to, 951–952 information in, 949–951 overview of, 948 removing rule from, 952–953 Active Directory defining connection string for, 468 defining membership provider instance, 468–469 Active Directory Application Mode (ADAM), 468 Active Server Pages (ASP), 1060 ActiveDirectoryMembershipProvider, 468–469 ActiveX (COM) DLLs. See COM components ADAM (Active Directory Application Mode), 468 Add Connection dialog, Visual Studio 2010, 344–345 Add Fields Dialog, customizing GridView columns, 271–272 address, WCF service, 1180–1181 AddStyleAttribute method, 1000 AddUsersToRoles() method, SqlRoleProvider, 514 Adjacent selectors, CSS, 689 Admin role, granting access to, 1259–1260 administration IIS Manager. See IIS Manager Web Site Administration Tool. See ASP.NET Web Site Administration Tool Administrative Tools

building browser-based, 1292 performance counters, 1290–1292 administrators, role management for, 562–564 ADO.NET approaches to asynchronous processing, 355 AsyncCallback class, 354 asynchronous command execution, 352 asynchronous connections, 370 asynchronous methods of SqlCommand class, 352–353 callback approach to asynchronous processing, 367–370 Command object, 317–318 connecting ListView to a database, 327–338 connecting to data source using Visual Studio, 344–345 Connection object, 315–317 creating EditItemTemplate, 341 creating EmptyItemTemplate, 341 creating InsertItemTemplate, 341–342 creating ItemTemplate, 340–341 creating layout templates, 338–340 data deletion, 313–314 data insertion, 312 data selection, 310–312 data updates, 312–313 DataList control, 330–331 DataReader object, 318–320 DataSet class, 325, 327–329 Dataset Designers and, 345–348 DataTable class, 325–327 deprecated support for Oracle databases, 329 example CustomerOrders DataSet, 348–351 IAsyncResult interface, 353 list of layout templates in ListView control, 337 ListView control and, 336–337 multiple columns and, 336 multiple wait handles in asynchronous processing, 360–367

1429

www.it-ebooks.info

ADO.NET – application folders

ADO.NET (continued) namespaces and classes, 314–315 overview of, 309–310 poll approach to asynchronous processing, 355–357 SqlDataAdapter class, 320–322 SqlParameter class, 322–325 summary, 370 templates available in DataList control, 331 using Visual Studio for ADO.NET tasks, 344 viewing defined layout elements, 342–344 wait approach to asynchronous processing, 357–360 WaitHandle class, 354–355 working with ItemTemplate, 331–334 working with other layout templates, 334–335 ADO.NET Entity Framework. See also EntityDataSource control building ASP.NET page with EDM, 1110–1113 conceptual and logical layers of, 1107 creating first entity data model, 1107–1108 EDM Wizard, 1108–1109 EntityDataSource control. See EntityDataSource control inheritance using EDM, 1119–1122 many-to-one and many-to-many relationships, 1116–1119 mapping between layers, 1107 one-to-one and one-to-many relationships, 1113–1116 O/R (Object Relation) designer, 1109–1110 overview of, 253, 1105–1107 stored procedures and, 1122–1125 summary, 1128 AdRotator control displaying rotating data with, 301 overview of, 119–121 Advanced Encryption Standard (AES), 1372 advertising, AdRotator control, 119 AES (Advanced Encryption Standard), 1372 AggregateCacheDependency class, 875 AJAX (Asynchronous JavaScript and XML). See ASP.NET AJAX Ajax Minifier, from Microsoft, 1389 alignment, of text around check boxes, 104 element anonymous users, 1258–1259 Windows-based authentication, 810 AllowPaging property, GridView control, 269 AllowReturn attribute, Wizard control, 143 AllowSorting attribute, GridView control, 267 Alternating ItemTemplate, DataList control, 334–335 AlwaysVisibleControlExtender control, 751–753 animations

AnimationExtender control, 753–755 UpdatePanelAnimationExtender control, 790–791 anonymous ID property, 581–582 anonymous personalization configuring, 1257–1258 enabling, 579–582 migrating anonymous users, 584–585 options, 583 profile storage, 583–584 working with, 582 ANTS profiler, from Red Gate Software, 1386 APIs (ASP.NET Management Objects) for manipulating web.config and machine.config, 1268 manipulating web.config from remote servers, 1273–1274 App_Browsers application folder, 31 handling reserved folders during upgrade, 1373 App_Code application folder, 27–30 build providers and, 35 handling reserved folders during upgrade, 1372 App_Data application folder, 30 handling reserved folders during upgrade, 1372 App_GlobalResources application folder, 31 creating resources (.resx) files, 560 handling reserved folders during upgrade, 1373 App_LocalResources application folder, 31 handling reserved folders during upgrade, 1373 App_Themes application folder, 31 creating folder structure for themes, 222 handling reserved folders during upgrade, 1372 App_WebReferences application folder, 31 handling reserved folders during upgrade, 1373 application configuration file, 1243 Application event log, 1288–1290 application folders App_Browsers, 31, 1373 App_Code, 27–30, 35, 1372 App_Data, 30, 1372 App_GlobalResources, 31, 560, 1373 App_LocalResources, 31, 1373 App_Themes, 31, 222, 1372 App_WebReferences, 31, 1373 overview of, 26–27

1430

www.it-ebooks.info

application monitoring – ASP.NET AJAX Control Toolkit

application monitoring, Web events for, 909–910 Application object, 856–857 application objects, mapping to relational database schemas, 1106 application pools, 1330 application profiles, 590–591 Application Settings section, of IIS Manager, 1333 application state methods for storing, 457 server-side state management options, 837 Application tab, ASP.NET Web Site Administration Tool managing application settings, 1325–1326 managing SMTP configuration, 1326 managing tracing and debugging, 1326–1327 overview of, 1325 taking applications offline, 1328 application tracing, 1296–1297 ApplicationName property, for custom provider, 504–505 applications adding roles to, 632–634 applying themes to, 219 build providers. See build providers changing how users register with applications, 612–613 COM components in. See COM components compilation of. See compilation, of applications deleting roles from, 634–635 Dynamic Data application, 1137–1139 enabling/disabling, 1263–1264 error handling, 923–924 globalization of. See globalization, of applications location options, 1–4 managing settings, 1325–1326 packaging/deploying. See packaging/deploying ASP.NET applications queue length, 1265 storing configuration settings, 1268 summary, 47 taking offline, 1328 tracing, 899 applications, ASP.NET AJAX building, 714–716 example of simple page with AJAX, 718–722 example of simple page without AJAX, 716–717 applications, Silverlight 3, 1400–1402 applications, WCF consumer. See WCF consumer application element, web.config, 1268, 1325 AppSettingsExpressionBuilder, 304

architecture provider architecture, 491–492 SOA (service-oriented architecture), 1178–1179 arithmetic operators, filtering WCF data with, 1212 arrays, generating DropDownList control fro, 96–98 .asmx file extension, 829–831 ASP (Active Server Pages), 1060 ASP.NET AJAX building applications, 714–716 building simple page with AJAX, 718–722 building simple page without AJAX, 716–717 client-side technologies in, 713–714 development with, 714 master pages and, 214–216 need for, 709–710 overview of, 709 request/response before and after, 710–712 script combining, 741–745 ScriptManager control, 723–725 ScriptManagerProxy control, 725–726 server-side controls, 722–723 server-side technologies in, 714 summary, 745 Timer control, 726–727 UpdatePanel control, 727–731 UpdateProgress control, 731–733 using multiple UpdatePanel controls, 733–736 Visual Studio 2010 and, 712–713 working with page history, 737–741 ASP.NET AJAX Control Toolkit Accordion control, 793–795 AlwaysVisibleControlExtender control, 751–753 AnimationExtender control, 753–755 AutoCompleteExtender control, 755–757 CalendarExtender control, 757–758 CascadingDropDown control, 795–798 CollapsiblePanelExtender control, 758–760 ColorPickerExtender control, 760–761 ConfirmButtonExtender control, 761–762 controls, 750–751 downloading and installing, 749 DragPanelExtender control, 763–764 DropDownExtender control, 764–766 DropShadowExtender control, 766–768 DynamicPopulateExtender control, 768–771 extenders, 751 FilteredTextBoxExtender control, 772 HoverMenuExtender control, 772–773 ListSearchExtender control, 774 MaskedEditExtender and MaskedEditValidator controls, 774–776

1431

www.it-ebooks.info

ASP.NET AJAX Control Toolkit – assembly resource (.resx)

ASP.NET AJAX Control Toolkit (continued) ModalPopupExtender control, 762–763 MutuallyExclusiveCheckBoxExtender control, 776–777 NoBot control, 798–799 NumericaUpDownExtender control, 778 overview of, 747–748, 1390–1391 PagingBulletedListExtender control, 778–779 PasswordStrength control, 799–800 PopupControlExtender control, 780–781 Rating control, 800–801 ResizableControlExtender control, 781–783 RoundedCornersExtender control, 783–784 server controls, 793 SliderExtender and MultiHandleSliderExtender controls, 784–785 SlideShowExtender control, 785–787 summary, 803 TabContainer control, 801–802 TabPanel control, 801 TextBoxWatermarkExtender control, 788–789 ToggleButtonExtender control, 789–790 UpdatePanelAnimationExtender control, 790–791 ValidatorCalloutExtender control, 791–792 ASP.NET Development Server, 911–912 ASP.NET Dynamic Data. See Dynamic Data ASP.NET Framework error handling in, 1091–1093 forms-based authentication when mixing versions of, 1372 release of, 1060 running multiple sites with multiple versions of, 1267–1268 running multiple versions side by side, 1370 upgrading ASP.NET 2.0/3.5 to 4, 1371 upgrading from ASP.NET 1.x to 2.0, 1370 using COM within .NET, 1085 using .NET from unmanaged code, 1095 ASP.NET Framework 1.0/1.1 converting ASP.NET 1.x application in Visual Studio 2010, 1375–1378 data binding in, 238 page events in, 19 using data binders in, 302 ASP.NET Framework 2.0/3.5, migrating to ASP.NET 4, 1378–1379 ASP.NET MMC snap-in managing configuration with, 1317 migrating older ASP.NET projects and, 1370 security, 832 ASP.NET MVC

Controller class and actions, 1074 Controller parameters, 1074–1075 Controllers, 1073 conventions, 1064–1065 core directories, 1064 creating first application, 1062–1064 defining routes, 1070–1073 examining sample application, 1065–1068 fundamental tenets, 1061 HTML helper methods, 1078–1079 IController interface, 1073–1074 methods vs. files and, 1061 overview of, 1059 routes and URLs, 1068–1069 routing compared to URL rewriting, 1069 specifying Views, 1076–1077 strongly typed Views, 1077–1078 Views, 1076 Web forms compared with, 1061–1062 ASP.NET Web Services (ASMX Web Services), 1178 ASP.NET Web Site Administration Tool Application tab, 1325 creating new users, 1322 Home tab, 1316–1317 managing access rules, 1324–1325 managing application settings, 1325–1326 managing roles, 1323–1324 managing SMTP configuration, 1326 managing tracing and debugging, 1326–1327 managing users, 1323 overview of, 1315–1316 Provider tab, 1328–1329 Security Setup Wizard, 1318–1322 Security tab, 1317 taking applications offline, 1328 aspnet_compiler.exe command options, 34 overview of, 33 for precompilation, 1345 aspnet_regsql.exe, 461–465, 851 aspnet_state.exe, 846 AspNetSqlProvider, 1325 .aspx pages code-behind file created within, 5 compiling, 32–35 listing of typical, 4–5 ProfileManager, see ProfileManager.aspx page using code-behind model, 8–9 working with file extensions, 829–831 @Assembly page directive, 18 assembly resource (.resx) files. See resource files (.resx)

1432

www.it-ebooks.info

AsyncCallback class – Authorization Manager policy

AsyncCallback class, 354 asynchronous callbacks, 1007–1010 asynchronous command execution, in ADO.NET AsyncCallback class, 354 asynchronous connections, 370 asynchronous methods of SqlCommand class, 352–353 callback approach to asynchronous processing, 367–370 canceling, 370 IAsyncResult interface, 353 multiple wait handles in asynchronous processing, 360–367 overview of, 352 poll approach to asynchronous processing, 355–357 wait approach to asynchronous processing, 357–360 WaitHandle class, 354–355 Asynchronous JavaScript and XML. See ASP.NET AJAX Atlas toolkit, 709 Attribute selectors, CSS, 688–689 attribute-based programming, modifying provider behavior via, 492 attributes @Assembly directive, 18 @Control directive, 14 @Implements directive, 17 @Import directive, 15 @Master directive, 13 @OutputCache directive, 18–19 @Page directive, 10–12 @PreviousPageType directive, 18 @Register directive, 17 files, 947–948 of element, 814 attributes, server controls class attributes, 992 property/event attributes, 992–993 rendering HTML tag attributes, 996–997 authentication adding users programmatically, 609–612 adding users to membership service, 600–601 applying authentication measures, 806 in ASP.NET 4, 598 attributes of element, 814 ChangePassword control, 623–624 changing how users register with applications, 612–613 configuring, 1254–1255 CreateUserWizard control, 601–602 defined, 598, 806

deleting user’s role cookie upon authentication, 639–640 denying access by turning off element, 613–614 forms-based authentication. See forms-based authentication locking out users who provide invalid passwords, 617–620 logging in programmatically, 617 login credentials provided by Login control, 614–616 login/logout features of LoginStatus control, 620–621 Passport authentication, 821 PasswordRecovery control, 625–626 personalization properties in registration process, 605–608 random password generation, 626–627 requesting login credentials, 613 setting up Web site for membership, 599–600 showing number of users online, 622–623 of specific files and folders, 822 turning off Windows authentication, 580 usernames with LoginName control, 621–622 viewing where users are stored, 603–604 Windows-based authentication. See Windows-based authentication working with CreateUserWizard control, 604–605 authentication, programmatic AuthenticationType property, 824 overview of, 822 User.Identity property and, 823 User.IsInRole method, 824 WindowsIdentity object and, 824–826 element adding to web.config, 599 encrypting, 1277–1278 overview of, 806–807 AuthenticationType property, in programmatic authentication, 824 authorization configuring, 1258 controlling information viewed via LoginView control, 627–629 defined, 598, 806 of groups, 810–811 of HTTP transmission method, 811 overview of, 627 role provider for, 472–473 URL authorization applied to specific files and folders, 822 of users, 809–810 Authorization Manager policy, 472

1433

www.it-ebooks.info

element, web.config – Button_Command event

element, web.config, 613–614 AuthorizationStoreRoleProvider, 472–473 AutoCompleteExtender control, 755–757 AutoCompleteType attribute, TextBox control, 90–91 AutoGenerateEditButton property, GridView control, 276 AutoGenerateFields property, GridView control, 263 AutoGenerateInsertButton property, DetailsView control, 287 automation, of XML, 419 AutoPostBack attribute, TextBox control, 89–90 Average operator, LINQ to Objects queries, 385 Aziz, Atif, 1391 AzMan stores, 472–473

B background color, fading, 754–755 banner ads, 120–121 Base Class Library (BCL), 927 Basic authentication, 812–813 BCL (Base Class Library), 927 Behavior section, Web Parts, 659–660 Berners-Lee, Tim, 683 Beyond Compare, from Scooter Software, 1395 Bin folder, handling during upgrade, 1372 binary data, reading/writing, 961–962 binding, ABCs of WCF service, 1180–1181 binding data. See data binding Blackberry, 1251 blogs, online resources, 1427 BooleanSwitch, diagnostics with, 908 bound list controls. See data display controls box model, CSS, 695–698 creating block box elements, 696 creating inline box elements, 696–698 overview of, 695–696 breadcrumb navigation, 521 breakpoints, in debugging, 917 Brown, Keith, 862 .browser file, 31, 1010–1011 browsers AJAX compatibility and, 713 building browser-based performance tool, 1292 client-side scripts added to, 1002–1003 configuring, 1251–1253 detecting capabilities of, 1011–1012 displaying contents of event logs in, 1286–1287 Silverlight 3 and, 1399

buffer overloads, application runtime settings, 1264 BufferedWebEventProvider, 1308 buffering Web events, 1308–1310 build providers built-in, 36 creating custom, 36–41 overview of, 35–36 BulletedList control, 124–128 binding XML data to bulleted list, 127–128 display modes, 126 dynamically populating bulleted list, 128 extending with PagingBulletedListExtender control, 778–779 list and number styles for, 125 simple example, 124 business objects accessing COM members in C#, 1090 CCW (COM-Callable Wrapper) and, 1095–1097 COM Interop, 1085 creating precompiled, 1082–1084 deploying COM components via private assemblies, 1093–1094 deploying COM components via public assemblies, 1094–1095 deploying .NET components via private assemblies, 1102 deploying .NET components via public assemblies, 1103 early vs. late binding, 1100 error handling for COM components, 1091–1093 error handling for .NET components, 1100–1102 overview of, 1081–1082 RCW (Runtime Callable Wrapper) and, 1086 releasing COM objects manually, 1090 summary, 1103 using COM objects in ASP.NET code, 1086–1089 using .NET components within COM objects, 1097–1100 using .NET from unmanaged code, 1095 using precompiled, 1084–1085 Button control building simple page with AJAX, 718 buttons working with client-side JavaScript, 93–94 CausesValidation property, 91 causing validation, 160 CommandName property, 92–93 forms-based authentication and, 816 overview of, 91 styling buttons with CSS, 707–708 Button_Command event, 92–93

1434

www.it-ebooks.info

byte arrays – Child selectors, CSS

byte arrays moving file contents from Stream object to, 137–138 streams using, 955

C C# accessing COM members in, 1090 converting to/from Visual Basic, 1393–1394 dynamic lookup, 1422 implicit types, 1419 late binding and, 1419 leveraging dynamic capabilities of, 1425 C# editor, 896 CAB (Cabinet Files) Size, in deployment projects, 1358 cache dependencies AggregateCacheDependency class, 875 CacheDependency class, 875 customizing, 876–879 overview of, 875 SQL Server. See SQL Server Cache Dependency Cache object attaching cache dependencies to, 890–893 for data caching, 873–874 methods for storing state information, 457 new options in, 879–882 server-side state management options, 837 CacheDependency class AggregateCacheDependency class, 876 creating custom cache dependencies, 876–879 unsealing, 876 CacheDuration property, 1169 caching. See also SQL Server Cache Dependency AggregateCacheDependency class, 875 Cache object for data caching, 873–874 Cache object new features, 879–882 CacheDependency class, 875 custom cache dependencies, 876–879 data source control caching, 259–260 detecting cached user controls, 985 HttpCachePolicy in client-side caching, 871–873 machine.config and web.config settings controlling, 874 with master pages, 213–214 output caching, 865–869 overview of, 865 partial page (user control) caching, 869 post-cache substitution, 870–871 programmatic caching, 873 roles, 639–640 summary, 894

Web service responses, 1169–1170 Calendar control, 112–119 date format, 113–114 date ranges, 115–116 date selection, 112–113 day, week, or month selections, 114 overview of, 112 style and behavior modification, 116–119 CalendarExtender control creating using PopupControlExtender control, 780–781 overview of, 757–758 callbacks adding asynchronous callback for data validation, 1007–1009 asynchronous processing and, 367–370 client-side callback JavaScript functions, 1009–1010 CAPI (Cryptographic API), 487 Caption attribute, Table control, 111–112 CAS (code-access security), 1242 Cascading Style Sheets. See CSS (Cascading Style Sheets) CascadingDropDown control, 795–798 Catalog Mode, Portal Framework, 644 Catalog Zone, Web Parts, 654–656 catalogs, Web Parts DeclarativeCatalogPart control, 662 PageCatalogPart control, 655–656, 662–663 CausesValidation property Button control, 91 overview of, 160 CCW (COM-Callable Wrapper), 1095–1097 CGI (Common Gateway Interface), 1060 ChangePassword control, 623–624 Chart control, 152–155 check boxes, selecting multiple options in TreeView control, 534–537 CheckBox control, 102–104 assigning values, 104 determining if check boxes are checked, 103–104 extending with MutuallyExclusiveCheckBoxExtender control, 776–777 overview of, 102 single instance of, 103 text alignment, 104 ToggleButtonExtender control used with, 789–790 CheckBoxList control overview of, 104–106 visually removing items from a collection, 98–99 Checked property, check boxes, 104 child nodes, TreeView control, 531 Child selectors, CSS, 688

1435

www.it-ebooks.info

Choose Location dialog – CommandField

Choose Location dialog choosing application locations, 3 FTP option, 3 IIS option, 3 opening, 2 Remote Site option, 4 class attributes, server controls, 992 class libraries, Silverlight 3, 1400 Class selectors, CSS, 689 class template, server controls, 990–992 classes ADO.NET, 314–315 storing in App_Code folder, 27–30 working with classes through Visual Studio 2010, 44–47 classes, Web Parts WebPart class, 668–669 WebPartManager class, 667 WebPartZone class, 668 clear text passwords, 817 click events, ImageButton control, 95 Client Script Library, 713–714 ClientID, server controls, 998 clients connecting to Oracle database via OracleClient object, 330 invoking Web service from client applications, 1164–1166 thick-clients vs. thin-clients, 709–710 client-side caching, 871–873 culture declarations, 1222–1223 Silverlight API, 1409–1410 state management options, 837 technologies in ASP.NET AJAX, 713–714 client-side scripts added to browser, 1002–1003 buttons working with JavaScript, 93–94 debugging JavaScript, 920–921 include files added to Web pages, 1006 for validating data, 1003–1005 client-side validation combining client-side and server-side validation, 176 with CustomValidator control, 172–174 vs. server-side validation, 158–159 turning off, 179–180 CLR (Common Language Runtime) creating CLR objects with LINQ to XML, 423–424 DLR built on, 1420 retrieving .NET CLR types from XML, 420–422 syntax notifications, 896

code managed code, 1412–1417 management tools, 1388–1390 reuse, 644 Code Style Enforcer, from Joel Fjordén, 1388–1389 Code view, Visual Studio 2010, 192 code-access security (CAS), 1242 code-behind model code-behind file created within .aspx page, 5 code-behind for CustomerOrders page, 350–351 content pages using, 196–197 of DynamicPopulateExtender.aspx page, 769–771 inline coding compared with, 6–7 for NoBot control’s OnGenerateChallengeAndResponse, 799 page structure options, 7–9 setting up service method for auto-complete, 756–757 Code.Changer.com, from Telerik, 1393 coding content page, 192–196 master pages, 190–192 CollapsiblePanelExtender control, 758–760 collections, adding items to, 102 ColorPickerExtender control, 760–761 columns customizing, 271–273 explicitly defined, 263 sorting, 267–269 TemplateField column, 273–275 visibility options, 1149 COM components accessing COM members in C#, 1090 deploying with .NET applications via private assemblies, 1093–1094 deploying with .NET applications via public assemblies, 1094–1095 error handling, 1091–1093 releasing COM objects manually, 1090 using COM objects in ASP.NET code, 1086–1089 XCopy deployment compared with, 1339 COM Interop, 1085 COM objects in ASP.NET code, 1086–1089 releasing COM objects manually, 1090 using .NET components within, 1097–1100 COM-Callable Wrapper (CCW), 1095–1097 Command object, ADO.NET, 317–318 Command window, switching from Immediate window to, 897–898 CommandField, adding edit functionality with, 276–277

1436

www.it-ebooks.info

command-line tool – connection strings

command-line tool, setting up providers to work with SQL Serve, 461–463 CommandName property, Button control, 92–93 Comments view, Visual Studio, 898 Common Gateway Interface (CGI), 1060 Common Language Runtime. See CLR (Common Language Runtime) CompareValidator control, 164–167 description of, 160 overview of, 164 validating against constants, 166–167 validating against other controls, 165–166 compilation, of applications configuration of, 1250–1251 .NET Compilation section of IIS Manager, 1330–1331 overview of, 32–35 composite controls creating, 1021–1022 defined, 1021 designer regions used to create, 1032–1033 exposing control properties, 1022–1023 compressing files, for deployment, 1358 compressing streams DeflateStream class, 964–965 GZipStream class, 965–966 overview of, 964 conceptual layer, of Entity Framework mapping application objects to relational database schemas, 1106 overview of, 1107 Conceptual Schema Definition Language (CSDL), 1107 concurrency errors, detecting after update, 245 configuration of anonymous identity, 1257–1258 application configuration file, 1243 applying configuration settings, 1243–1244 of ASP.NET page, 1260–1262 of authentication, 1254–1255 of authorization, 1258 of browser capabilities, 1251–1253 of compilation, 1250–1251 of connection strings, 1245–1246 creating custom sections of configuration files, 1279–1280 custom errors, 1253–1254 detecting changes to configuration files, 1244 editing configuration files, 1278–1279 enumerating connection strings, 1270–1271 of file authorization, 1259–1260 file formats of configuration files, 1244–1245 of forms-based authentication, 1256–1257

of health monitoring, 1299 of include files, 1262–1263 locking down settings, 1260 manipulating machine.config file, 1272–1273 manipulating web.config from remote servers, 1273–1274 overview of, 1239–1240 of Passport authentication, 1255 programming configuration files, 1268–1270 protecting settings, 1274–1278 runtime settings, 1263–1265 server configuration files, 1240–1243 of session state, 1246–1247 storing application-specific settings, 1268 summary, 1282–1284 of URL authorization, 1258–1259 using custom configuration handler, 1282–1284 using DictionarySectionHandler object, 1281 using NameValueFileSectionHandler object, 1280–1281 using SingleTagSectionHandler object, 1281–1282 of Web farm support, 1247–1249 of Windows-based authentication, 1255 of worker process, 1265–1268 Configuration API, 1317 configuration files. See also machine.config; web.config application configuration file, 1243 creating custom sections of, 1279–1280 detecting changes to, 1244 editing, 1278–1279 file formats of, 1244–1245 options for managing, 1317 programming, 1268–1270 server configuration files, 1240–1243 types of, 1239 configuration providers DpapiProtectedConfigurationProvider, 485–486 overview of, 485 RsaProtectedConfigurationProvider, 486–487 Configure Data Source Wizard, 239–241 ConfirmButtonExtender control, 761–762 ConflictDetection property, SqlDataSource control, 244–246 connection information, storing, 260–261 Connection object, ADO.NET, 315–317 connection strings adding, 1271–1272 adding to SQL Server Express file, 587–588

1437

www.it-ebooks.info

connection strings – CreateUser() method

connection strings (continued) configuring session state with, 1249 Connection object and, 315–316 Connection Strings section of IIS Manager, 1333 ConnectionString expression, 303 decrypting element, 1278 defining for Active Directory, 468 encrypting element, 1275–1276 enumerating, 1270–1271 exposing in web.config, 1275 providing within web.config file, 316–317 retrieving, 1246 storing, 1245 storing connection information, 260 using, 317 Connection Strings section, of IIS Manager, 1333 ConnectionStringBuilder, 261 ConnectionsZone control, Web Parts, 660 console application, hosting WCF service in, 1184 constants, validating against, 166–167 consumer Web Parts, 674, 677–679 container-specific master pages, 211–212 content areas, of content pages, 190 content pages code-behind model used by, 196–197 coding, 192–196 content areas of, 190 event ordering in, 212–213 getting Label’s text value in, 200–201 master pages and, 189 nesting master pages and, 210–211 overriding property from master page, 203–204 specifying content, 205–206 that work with more than one master page, 211 using Wrox.master file, 195 Content subfolder, of DynamicData folder, 1131 ContentPlaceHolder control content areas of, 190 mapping to, 196 element, 629 contract, ABCs of WCF service, 1180–1181 control class, designer attribute added to, 1031–1032 control designers adding actions to, 1040–1042 adding Designer attribute to control class, 1031 creating composite control using designer regions, 1032–1033 custom designer class for defining designer regions, 1034–1039 designer regions and, 1031–1032

design-time behaviors of server controls and, 1027 @Control page directive, 14 Controllers, MVC Controller class and actions, 1074 IController interface, 1073–1074 overview of, 1060, 1073 parameters, 1074–1075 controls adding to pages dynamically with PlaceHolder control, 124 in ASP.NET AJAX Control Toolkit, 750–751 exposing values of, 23–24 Pages and Controls section of IIS Manager, 1334 server controls. See server controls as set of instructions, 86 styling, 706–708 user controls. See user controls working with from master pages, 199–205 ControlState. See also ViewState client-side state management options, 837 in server controls, 1014–1015 state management, 863 Convention over Configuration, MVC frameworks and, 1061, 1064 conventions, ASP.NET MVC, 1064–1065 Conversion Wizard, Visual Studio 2010, 1376 cookies changing name of cookie for anonymous identification, 581 client-side state management options, 837 deleting user’s role cookie upon authentication, 639–640 Session State section of IIS Manager, 1335 setting anonymous cookie in HTTP header, 580–581 setting length of time stored, 581 specifying how stored, 581–582 state management and, 458, 857 coordinated universal time (UTC), 113–114 Copy Web Site, Visual Studio deploying precompiled Web applications with, 1344–1346 packaging/deploying ASP.NET applications, 1341–1344 Count operator, LINQ to Objects queries, 385 create, read, update, and delete (CRUD), 1194 Create Package option, Solution Explorer, 1348 CreateRole() method, SqlRoleProvider, 513 CreateUser() method, Membership API, 609–610

1438

www.it-ebooks.info

CreateUserWizard control – custom providers

CreateUserWizard control adding users to membership service, 601–602 ContinueButtonClick event, 604–605 CreateUser()event, 605 using personalization properties with, 606–608 credentials adding username/password values to web.config, 816–817 checking in SQL Server and, 818–819 cross-page postbacks, 857–859 cross-page posting, 21–26 CRUD (create, read, update, and delete), 1194 Cryptographic API (CAPI), 487 Cryptographic Service Provider (CSP), 487 CSDL (Conceptual Schema Definition Language), 1107 CSP (Cryptographic Service Provider), 487 CSS (Cascading Style Sheets) advantages over HTML, 683 analyzing with Firebug, 1382 Apply Styles tool window in Visual Studio, 705 box model for layout and positioning of elements, 695–698 combining selectors, 691–692 CSS Properties tool window in Visual Studio, 706 external sheets, 685–686 grouping selectors, 691 implementing themes, 31 !important attribute for controlling style override, 702 including CSS files in themes, 224–227 inheritance and, 694–695 inline styles, 687 internal sheets, 686–687 Manage Styles tool window in Visual Studio, 704–705 managing CSS links in master pages, 706 merged styles, 692–694 overview of, 684–685 positioning elements, 698–701 pseudo classes, 690–691 pseudo elements, 691 rules, 687–688 selectors, 688–690 styling ASP.NET controls, 706–708 summary, 708 themes compared with, 217 using CSS class in server controls defined in .skin file, 228–229 working with in Visual Studio, 702–704 CSS: Cascading Style Sheets for Web Design, 2nd Edition (York), 684 culture

changing culture of ASP.NET thread, 1220–1221 checking culture of ASP.NET thread, 1219–1220 client-side culture declarations, 1222–1223 currencies in different cultures, 1225–1227 date/time values in different cultures, 1223–1224 detection added to web.config , 559–560 example setting culture to Finnish, 1229–1230 .NET Globalization section of IIS Manager and, 1331 numbers in different cultures, 1224–1225 overview of, 1217–1218 preference for neutral cultures, 1235 server-side culture declarations, 1221–1222 sorting strings in different cultures, 1227–1229 translating values and behaviors, 1223 understanding culture types, 1218–1219 CultureInfo object, 1219–1221 currencies, in different cultures, 1225–1227 CurrentNode object, SiteMap API, 555–557 custom build providers, 36–41 cache dependencies, 876–879 columns, 271–273 configuration handler, 1282–1284 datasets as SOAP, 1157–1158 designer class, 1034–1039 DynamicObject type, 1423–1424 expression builder, 304 icons, 537–538 personalization types, 576–579 sections of configuration files, 1279–1280 state store, 1249 type converter, 1028–1031 Web events, 909–910 Web Parts, 669–674 XML from SQL 2008, 451–452 Custom Actions Editor, 1366 custom controls. See also CustomValidator control DetailsView control, 285 disabling themes for, 232 disabling themes for properties in, 233–235 custom providers building ReadUserFile() method, 508–510 constructing class skeleton for custom provider, 499–501 creating CustomProviders application, 498 creating data store for XML membership provider, 502 defining ApplicationName property, 504–505 defining XmlMembershipProvider in web.config, 502–503 extending Initialize() method, 505–507

1439

www.it-ebooks.info

custom providers – databases

custom providers (continued) implementing custom providers for session state, 854 implementing methods and properties of XmlMembershipProvider class, 504 not implementing methods and properties of XmlMembershipProvider class, 503–504 overview of, 497–498 user login using XmlMembershipProvider, 510–511 validating users, 507–508 CustomerOrders DataSet, 348–351 CustomPages subfolder, of DynamicData folder, 1131 CustomProviders application, 498 CustomValidator control client-side validation with, 172–174 combining client-side and server-side validation, 176 description of, 160 overview of, 172 server-side validation with, 174–176

D data binding adding data-binding expression, 275 AdRotator control, 301 bulleted list to XML, 127–128 data display controls. See data display controls data source controls. See data source controls expressions and expression builders, 303–307 generating list of Movie objects and binding to GridView, 372–376 inline data-binding syntax, 302 ListView control, 293–294 Menu control, 301 Menu control to XML files, 551–552 overview of, 237–238 syntax, 302–303 TreeView control, 300 TreeView control to XML file, 532–534 XML and, 303 data caching, 873–874. See also caching data concurrency concurrency errors, 245 LINQ to SQL and, 402 LinqDataSource control, 252 data connections, configuring with SqlDataSource control, 239–242 data contract, building WCF service with a, 1190–1192 data display controls AdRotator control, 301

DetailsView control. See DetailsView control FormView control. See FormView control GridView. See GridView control ListView. See ListView control Menu control, 301 TreeView control, 300 Data Link Properties dialog, Visual Studio 2010, 344 data ordering, LINQ to Objects, 381–382 Data Protection API. See DPAPI (Data Protection API) data source controls abstraction and, 238 AccessDataSource control, 247 caching, 259–260 EntityDataSource control, 253–255 LinqDataSource control, 247–253 list of, 239 ObjectDataSource control, 256–259 overview of, 237–239 sitemaps. See SiteMapDataSource control SqlDataSource control, 239–247 storing connection information, 260–261 using bound list controls with, 262 Visual Studio for connecting to data source, 344–345 XmlDataSource control, 255–256 data stores adding users to SQL Server Express data store, 600–601 creating for XML membership provider, 502 storing in App_Data folder, 30 data types adding column of untyped XML with SQL Server, 452–453 associating XML typed column with schemas, 455 comparing with CompareValidator control, 167 converting. See Type converters, server controls and CSS Type selectors, 688 custom personalization types, 576–579 defining types for personalization properties, 576 dynamic. See Dynamic Data implicit types, 1419–1420 inserting XML data into XML column, 455–456 retrieving CLR types from XML, 420–422 static vs. dynamic, 1419 typed DataSets, 329 using custom types for personalization properties, 576–579 ViewSate and, 1014 data visualizers, for debugging, 915 databases. See also SQL databases authenticating against values in, 818–819 connecting ListView to, 337–338

1440

www.it-ebooks.info

DataContext, LINQ to SQL – DeclarativeCatalogPart control

disabling cache invalidation, 885 enabling cache invalidation, 883 incorporating into Dynamic Data application, 1137–1139 server-side state management options, 837 XML and, 446 DataContext, LINQ to SQL, 394 data-driven Web applications. See Dynamic Data DataKeyNames, adding to GridView control, 277 DataList control changing output style, 333–334 displaying XML content, 437–439 displaying XML RSS blog feed, 439–441 list of available templates, 331 multiple columns and, 336 overview of, 330–331 working with ItemTemplate, 331–334 working with other layout templates, 334–335 DataPager control, 294–296 DataReader class comparing DataSet class with, 327–328 data selection and, 310 loading DataTable from DataReader, 326–327 reading data from SQL database, 311–312 DataReader object, 318–320 DataSet class, 327–329 accessing fields in DataSets, 329 adding DataTables to, 346 creating typed DataSet object, 345 CustomerOrders and, 348–351 deciding when to use, 327–328 example working with, 328–329 overview of, 325 Dataset Designers, 345–348 DataSet object creating typed DataSet object, 345 mapping application objects to relational database schemas, 1106 DataSets, XML changing using XmlDataDocument, 436–437 DataSet- awareness in XmlDataDocument, 435 exposing custom DataSets as SOAP, 1157–1158 overview of, 434 persisting, 434–435 DataSourceMode property, adding to SqlDataSource control, 242 DataTable class Dataset Designers working with, 346–348 filling DataTable with object from SqlDataAdapter class, 321–322 loading DataTable from DataReader, 326–327

overview of, 325–326 DataTips (Visual Studio), 914–915 dates date values in different cultures, 1223–1224 filtering WCF data with date functions, 1212 format for output from calendar, 113–114 ranges, 115–116 selection from calendar, 112–113 validating date ranges, 169–171 Date.Time.ascx file example, Dynamic Data, 1132–1136 days controlling how a day is rendered in calendars, 117–119 selecting single day in Calendar control, 112 SelectionMode attribute for day, week, or month selections, 114 debug configuration, in Visual Studio overview of, 910 vs. release configuration, 911 debugging. See also design-time, error handling and; error handling; tracing attaching to processes, 913 client-side JavaScript, 920–921 data visualizers for, 915 DataTips for, 914–915 debug vs. release, 911 Edit and Continue or Edit and Refresh, 916 error notifications, 915–916 IIS vs. Development Server, 911–912 IntelliTrace in Visual Studio, 917–919 JIT Debugging, 911 Just My Code, 916 multiple threads, 919 overview of, 895 remote, 913–914 SQL Stored Proc, 921–922 starting debugging session, 912 summary, 925 tools, 1381–1383 tracepoints (breakpoints) for, 917 turning off prior to deploying applications, 1338–1339 Web Site Administration Tool for, 1326–1327 what is required, 910 Windows XP Service Pack 2 for, 914 XSLT, 445–446 Debug.Write, compared with Trace.Write, 903–904 DeclarativeCatalogPart control adding Web Parts to pages, 662 combined with PageCatalogPart, 662–663

1441

www.it-ebooks.info

DeflateStream class – dotTrace profiler

DeflateStream class, 964–965 delayed execution, LINQ to Objects, 382 DeleteCookie() method, 639–640 DeleteRole() method, 513–514, 634–635 deleting data ADO.NET tasks, 313–314 DetailsView control, 287–288 GridView control, 281–283 LINQ to SQL, 402 denial of service attacks, 133 element anonymous users, 1259 Windows-based authentication, 810 deploying ASP.NET applications. See packaging/ deploying ASP.NET applications deploying business objects COM components with .NET applications via private assemblies, 1093–1094 COM components with .NET applications via public assemblies, 1094–1095 .NET components via private assemblies, 1102 .NET components via public assemblies, 1103 Descendant selectors, CSS, 688 Design Mode, Portal Framework, 644 Design view, Visual Studio 2010 Format and Table menus, 702 working with master pages, 192 designer actions, control designers, 1040–1042 designer regions creating composite control using, 1032–1033 custom designer class for defining, 1034–1039 overview of, 1031–1032 design-time, error handling and Immediate and Command windows and, 897–898 overview of, 895–896 syntax notifications, 896–897 Task list views and, 898 design-time, server controls and designer actions added to control designer, 1040–1042 designer attribute added to control class, 1031–1032 designer regions defined by custom designer classes, 1034–1040 designer regions used to create composite control, 1032–1033 overview of, 1027 TypeConverter class, 1028–1031 UI Type editor added to control property, 1042 DetailsView control customizing, 285 inserting, updating, and deleting data using, 287–288 overview of, 283–285

SelectParameters vs. FilterParameters, 287 using in conjunction with GridView, 285–287 developer tools, 1383–1385, 1393–1397 development, with ASP.NET AJAX, 714 DevExpress DXCore, 1388–1389 Refactor! for ASP.NET from, 1388 DHTML, AJAX support for, 711 diagnostic switches, 908–909 BooleanSwitch, 908 SourceSwitch, 909 TraceSwitch, 908–909 dialog boxes, for reporting validation error, 179 DictionarySectionHandler object, 1281 differencing tools, 1394 DiffMerge tool, from SourceGear, 1395 Digest authentication, 813 directives, page. See page directives directories ASP.NET MVC core directories, 1064 binding GridView to directory files, 938–942 Directory and DirectoryInfo classes, 931 enumerating file system directories, 931–935 manually enumerating directory files, 942–943 setting/displaying working directory, 936–937 static methods, 962–964 working with static methods of Directory class, 935–936 Directory class overview of, 931 working with static methods of, 935–936 DirectoryInfo class, 931, 947–948 DisplayMode attribute, BulletedList control, 126–127 displays controlling display aspects of Dynamic Data, 1147–1149 data display with FormView control, 298–300 DLR (Dynamic Language Runtime) dynamic lookup, 1422–1425 overview of, 1420–1422 DOCTYPES, CSS styles and, 684 Document Object Model. See DOM (Document Object Model) DOM (Document Object Model) accessing using managed code, 1412–1415 AJAX support for, 711 XML and, 429–430 domain names, restrictions on, 829 Don’t repeat yourself (DRY), for MVC frameworks, 1061 dotTrace profiler, from JetBrains, 1386

1442

www.it-ebooks.info

DPAPI (Data Protection API) – empty data conditions

DPAPI (Data Protection API) decrypting element, 1278 encrypting section, 1277–1278 encrypting section, 1275–1276 exposing connections string in web.config , 1275 overview of, 485 protecting configuration settings, 1274–1278 storing username and passwords in registry and referencing settings in machine.config, 1274–1275 DpapiProtectedConfigurationProvider, 485–486 DragPanelExtender control, 763–764 DriveInfo class, 928 drives displaying local drive information, 928–929 DriveInfo class, 928 enumerating local file system drives, 929–931 DropDownExtender control, 764–766 DropDownList control CascadingDropDown control and, 795–798 overview of, 96–98 selecting drop down list item, 279–281 validating drop-down lists, 164 visually removing items from a collection, 98–99 DropShadowExtender control, 766–768 DRY (Don’t repeat yourself), for MVC frameworks, 1061 DXCore, 1388–1389 Dynamic Data adding to existing pages, 1149–1151 controlling display aspects, 1147–1149 creating base application for, 1129–1130 Date.Time.ascx file example, 1132–1136 DynamicData folders and subfolders, 1131 incorporating database into application, 1137–1139 List.ascx page, 1136 overview of, 1129 registering data model within Global.asax, 1139–1141 running the sample application, 1141–1144 styles and layouts, 1141 summary, 1151 viewing core files in default application, 1130–1131 working with routes, 1144–1147 dynamic keyword, creating dynamic type using, 1422–1423 Dynamic Language Runtime (DLR) dynamic lookup, 1422–1425 overview of, 1420–1422

dynamic lookup, 1422–1425 dynamic pseudo class, CSS, 690 dynamic styles, Menu control and, 548 DynamicData folders, 1131 DynamicDataManager control, 1150–1151 DynamicObject class, 1423–1424 DynamicPopulateExtender control, 768–771 DynamicValidator control, 160

E early binding, .NET components, 1100 early-bound access, to personalization items, 574 Edit and Continue approach, to debugging, 916 Edit and Refresh approach, to debugging, 916 Edit Mode, Portal Framework, 644 editing data with FormView control, 298–300 row data in GridView, 275 EditItemTemplate GridView control, 279 ListView control, 341 Editor Zone, modifying Web Part settings, 657–660 EDM (Entity Data Model) adding to WCF Data Service, 1195–1196 building with ASP.NET page with, 1110–1113 changing WCF service to work with, 1198–1199 creating first model, 1107–1108 EDM Wizard for working with, 1108–1109 entity designer for working with, 1109–1110 incorporating database into Dynamic Data application, 1137 inheritance using, 1119–1122 many-to-one and many-to-many relationships, 1116–1119 one-to-one and one-to-many relationships, 1113–1116 overview of, 253, 1107 WCF Data Services framework and, 1194 working with relationships and, 1205 ELMAH (Error Logging Modules and Handlers), 1391–1392 e-mail e-mailing Web events, 1310–1314 sending, 977 SimpleMailWebEventProvider, 480–482, 1310–1311 SMTP E-mail section of IIS Manager, 1336 TemplatedMailWebEventProvider, 482, 1311–1314 validating text-box value in e-mail address, 171–172 empty data conditions, GridView control, 265–267

1443

www.it-ebooks.info

EmptyDataTemplate – extenders, ASP.NET AJAX

EmptyDataTemplate, 266 EmptyDataText property, GridView control, 265–266 EmptyItemTemplate, ListView control, 293, 341 enableAutoZoneZoom parameter, Silverlight 3, 1409 enableGPUAcceleration parameter, Silverlight 3, 1409 enablehtmlaccess parameter, Silverlight 3, 1408 enableNavigation parameter, Silverlight 3, 1409 EnablingTheming attribute, for page directives, 220–221 encoding formats, of files, 962 encryption changing ASP.NET 4 to use 3DES, 1372 decrypting section, 1278 encrypting section, 1277–1278 encrypting section, 1275–1276 forms-based authentication and, 1256 of ViewState, 863 Enterprise Services, 1178 Entity Data Model. See EDM (Entity Data Model) entity designer, for EDM (Entity Data Model), 1109–1110 Entity Framework. See ADO.NET Entity Framework EntityDataSource control configuring, 1126 creating base page for, 1125–1126 description of, 239 markup generated by, 254 overview of, 253–255, 1125 pulling customer table using, 1127–1128 EntityTemplates subfolder, of DynamicData folder, 1131 error handling configuring element, 1253–1254 database errors, 246–247 ErrorNotification.aspx page, 1312–1313 handling application exceptions, 923–924 handling page exceptions, 922–923 Http status codes, 924–925 in .NET, 1091–1093 .NET components, 1100–1102 overview of, 922 raising errors in VB6, 1091 summary, 925 when updating data, 278–279 Error Logging Modules and Handlers (ELMAH), 1391–1392 error notifications debugging and, 915–916 dialog box used for reporting validation errors, 179 ErrorNotification.aspx page, 1312–1313

using images and sounds for, 180–181 event attributes, for validating data, 1003–1005 event logs EventLogTraceListener, 906–907 EventLogWebEventProvider, 479–480, 1298–1299 health monitoring and, 479 overview of, 1285–1286 reading from, 1286–1288 writing to, 1288–1290 Event Viewer health monitoring and, 479 working with event logs, 1285, 1290 EventLogTraceListener, 906–907 EventLogWebEventProvider, 479–480, 1298–1299 element, 1299–1301 events creating control events in user controls, 981 GridView control, 264 LinqDataSource control, 253 menu events, 550 ordering in master pages, 212–213 Silverlight plug-in, 1409 SqlDataSource control, 246 triggering when TextBox changes, 89–90 utilizing wizard control events, 145–146 Web events, 478 writing Web events using SqlWebEventProvider, 483 Excel, interacting with from .NET, 1425 exception handling. See error handling ExpandoObject class, 1423–1425 expression builders, 303–307 expressions adding, 275 data binding and, 303–307 extender tools, for ASP.NET, 1390–1393 extenders, ASP.NET AJAX AlwaysVisibleControlExtender control, 751–753 AnimationExtender control, 753–755 AutoCompleteExtender control, 755–757 CalendarExtender control, 757–758 CollapsiblePanelExtender control, 758–760 ColorPickerExtender control, 760–761 ConfirmButtonExtender control, 761–762 DragPanelExtender control, 763–764 DropDownExtender control, 764–766 DropShadowExtender control, 766–768 DynamicPopulateExtender control, 768–771 FilteredTextBoxExtender control, 772

1444

www.it-ebooks.info

external style sheets, CSS – FileUpload control

HoverMenuExtender control, 772–773 ListSearchExtender control, 774 MaskedEditExtender control, 774–776 ModalPopupExtender control, 762–763 MutuallyExclusiveCheckBoxExtender control, 776–777 NumericaUpDownExtender control, 778 overview of, 751 PagingBulletedListExtender control, 778–779 PopupControlExtender control, 780–781 ResizableControlExtender control, 781–783 RoundedCornersExtender control, 783–784 SliderExtender and MultiHandleSliderExtender controls, 784–785 SlideShowExtender control, 785–787 TextBoxWatermarkExtender control, 788–789 ToggleButtonExtender control, 789–790 UpdatePanelAnimationExtender control, 790–791 ValidatorCalloutExtender control, 791–792 external style sheets, CSS, 685–686

F F5 debugging Edit and Refresh debugging, 916 overview of, 912 Fiddler sniffer, 528 FieldTemplates subfolder, of DynamicData folder, 1131 file authorization, configuring, 1259–1260 File class, 937 File I/O (input/output) ACLs (Access Control Lists), 948–953 binding GridView to directory files, 938–942 compressing streams, 964 DeflateStream class, 964–965 Directory and DirectoryInfo classes, 931 displaying local drive information, 928–929 DriveInfo class, 928 encoding formats of files, 962 enumerating file system directories, 931–935 enumerating local file system drives, 929–931 File and FileInfo classes, 937 file properties and attributes, 947–948 FileWebRequest and FileWebResponse, 976–977 FtpWebRequest and FtpWebResponse, 974–976 GZipStream class, 965–966 HttpWebRequest and HttpWebResponse, 971–974 IPC (interprocess communication) using pipes, 970 manually enumerating directory files, 942–943 memory-mapped files, 966–968 network communications, 970 reading from NetworkStream, 958–959

reading/writing binary data, 961–962 reading/writing files, 953 reading/writing with StreamReader, 960–961 security and, 927 sending e-mail, 977 serial ports and, 968–969 setting/displaying working directory, 936–937 shortcuts, 962–964 stream classes, 954 streams and, 953 summary, 977–978 System.IO namespace, 927 using FileStream to read system file, 954–956 using I/O enumerations to control file behavior when writing files, 957 using relative paths and setting/getting current directory, 936 Using statements for reading files, 955–956 working with paths, 943–946 working with static methods of Directory class, 935–936 writing to MemoryStream, 957–958 File System Editor, Visual Studio, 1360–1362 adding items to installer output, 1361 folder options, 1362 overview of, 1360 properties, 1360–1361 file system option, Copy Web Site tool, 1343 File Type Editor, Visual Studio, 1363–1364 FileInfo class file properties and attributes, 947–948 manually enumerating directory files, 942–943 overview of, 937 files. See also File I/O (input/output) authenticating specific, 822 encoding formats of, 962 formats of configuration files, 1244–1245 memory-mapped, 966–968 properties and attributes, 947–948 reading/writing, 953 security of file extensions, 829–831 size limitations in uploading, 133–137 static methods, 962–964 using FileWebRequest to write to remote file, 976 FileStream, 954–956 FileUpload control, 130–138 file size limitations, 133–137 moving file contents from Stream object to Byte array, 137–138 overview of, 130 permissions for file uploads, 132–133

1445

www.it-ebooks.info

FileWebRequest› – globalization, of applications

FileUpload control (continued) placing uploaded files into Stream objects, 137 uploading files, 130–132 FileWebRequest, 976–977 FileWebResponse, 976–977 FilteredTextBoxExtender control, 772 filtering text boxes to only use numbers, 772 WCF result sets, 1211–1212 filtering data LINQ to Objects and, 382–386 SelectParameters, 243–244 in traditional queries, 376 Where parameters and, 250 FilterParameters, DetailsView control, 287 Filters subfolder, of DynamicData folder, 1136 FindControl method, 984 Firebug debugging with, 1381–1382 YSlow extending, 1382–1383 Firefox AJAX compatibility and, 713 Firebug plug-in, 1381–1382 Web Developer Toolbar, 1384 first-child pseudo class, CSS, 690 Fjordén, Joel, 1388–1389 floating elements, CSS, 701 flushing Web events, 1309 Focus() method, TextBox control, 89 folders authenticating specific, 822 creating folder structure for themes, 222 DynamicData folders, 1131 upgrading ASP.NET reserved folders, 1372–1373 FOR XML AUTO clause queries returning XML, 446–448 retrieving XML from SQL Server 2000 using, 448– 450 FOR XML TYPE, 451 ForceCopyBuildProvider, 36 foreach loops, 376 form elements comparing two form elements with CompareValidator control, 165–166 showing form elements with Wizard control, 146–149 Format menu, Visual Studio Design view, 702–703 formats, file of configuration files, 1244–1245 encoding, 962 formatting features, GridView control, 283 forms authentication

adding to web.config, 599 HTTP client-side redirection and, 597 turning off Windows authentication, 580 element adding to web.config, 599–600 attributes, 814 FormsAuthentication class, 821 forms-based authentication authenticating against values in database, 818–819 authenticating against values in web.config file, 816–818 configuring, 1256–1257 FormsAuthentication class, 821 Login control and, 820–821 mixing versions of .NET Framework and, 1372 overview of, 813–816 FormView control displaying and editing data with, 298–300 overview of, 296–297 Framework Class Library, 849 FTP FTP option of Copy Web Site tool, 1343 Web server options, 3 FtpWebRequest, 974–976 FtpWebResponse, 974–976

G GAC (Global Assembly Cache), 1094 GenerateMachineKey tool, 862 GeneratePassword() method, 627 GetDirectories() method, 934 GetFiles() method, 941 GetSlides() method, 786–787 Global Assembly Cache (GAC), 1094 global resources, 1235–1237. See also resource files (.resx) overview of, 1235 programmatic access to, 1237 storing in App_GlobalResources folder, 31 using directly in server controls, 1235–1236 Global.asax file, 41–44 Application_End event in, 42–44 directives, 42 events, 42 overview of, 41–42 registering Dynamic Data data model within, 1139–1141 globalization, of applications changing culture of ASP.NET thread, 1220–1221 checking culture of ASP.NET thread, 1219–1220

1446

www.it-ebooks.info

element – HTMLDog.com, Reference Web site

client-side culture declarations, 1222–1223 cultures and regions, 1217–1218 currencies in different cultures, 1225–1227 date/time values in different cultures, 1223–1224 example setting culture to Finnish, 1229–1230 .NET Globalization section of IIS Manager, 1331 numbers in different cultures, 1224–1225 overview of, 1217 server-side culture declarations, 1221–1222 sorting strings in different cultures, 1227–1229 summary, 1238 translating values and behaviors, 1223 understanding culture types, 1218–1219 element, 1221–1222 globally unique identifier (GUID), 199–201 Google Chrome, 1251 GridView control adding ItemTemplate and EditItemTemplate to, 279 binding list of Movie objects to, 372–376 binding to directory files, 938–942 column sorting, 267–269 configuring for data updates, 275–278 customizing columns, 271–273 deleting data, 281–283 DetailsView control using in conjunction with, 285–287 DynamicDataManager control used with, 1150–1151 editing row data, 275 error handling when updating data, 278–279 events, 264 explicitly defined GridView columns, 263 formatting features, 283 handling null and empty data conditions, 265–267 overview of, 262 paging GridView data, 269–271 RowDataBound event, 264–265 TemplateField column, 273–275 using in ASP.NET pages, 262–263 using RowDataBound events to select drop down list item, 279–281 using with EDM, 1111–1112 group filters, LINQ to Objects, 384 group rendering, ListView control, 292–293 GroupBy clause, grouping LINQ queries using, 251 grouping data GroupBy clause, 251 LINQ to Objects, 384 LINQ to SQL, 396–397 in traditional queries, 377–378 groups adding group of personalization properties, 574–575

authenticating and authorizing, 810–811 providing view of particular group of users, 629 using grouped personalization properties, 575–576 working with validation groups, 181–185 GUID (globally unique identifier), 199–201 Guthrie, Scott, 1061 GZipStream class, 965–966

H Handler Mapper, IIS Manager, 829–831 Hansson, David Heinemeier, 1060 hash, salted hash, 861 hashed message authentication code (HMAC), 861 HeaderText attribute, Wizard control, 143–144 health monitoring. See also Web event providers configuring, 1299 element, 1299–1301 overview of, 1297–1298 element, 1304 provider model for, 1298 element, 1301–1302 element, 1302–1304 Web events for, 909–910 Helicon, ISAP_Rewrite from, 1392 hidden fields client-side state management options, 837 state management and, 458, 859–860 HiddenField control, 129–130, 859–860 history, working with page history in ASP.NET AJAX, 737–741 HMAC (hashed message authentication code), 861 Home tab, Web Site Administration Tool, 1316–1317 hot-keys, Label control providing hot-key functionality, 87 HoverMenuExtender control, 772–773 HTML (Hyperlink Markup Language) advantages of CSS over, 683 analyzing with Firebug, 1381–1382 development of, 683 overview of, 684–685 rendering HTML tag attributes with server controls, 996–997 rendering HTML tags with server controls, 994–996 styling with server controls, 998–1001 HTML Bridge, Silverlight 3, 1412 HTML control overview of, 85 Web server controls compared with, 85–86 HTML helper methods, 1078–1079 HTMLDog.com, Reference Web site, 1387

1447

www.it-ebooks.info

HtmlHelper class – IIS Manager

HtmlHelper class, 1078–1079 HtmlTextWriter class, 995 HTTP authenticating and authorizing HTTP transmission method, 811 controlling caching with HTTP header, 871–873 error handling status codes, 924–925 output caching HTTP responses, 865 setting anonymous cookie in HTTP header, 580–581 as stateless protocol, 835 HTTP request processing adding HttpHandlers configuration to web.config, 1057–1058 adding HttpModules configuration to web.config, 1051–1052 altering output of ASP.NET web page, 1049–1051 class based image HttpHandler, 1056–1057 core mechanisms in ASP.NET for, 1047–1048 events trigged by Init method, 1049 generic handlers, 1052–1053 HttpHandler page template, 1053–1054 HttpHandlers, 1052 HttpModules, 1048 IIS and, 1046–1047 implementing IHttpModule interface, 1048–1049 mapping file extension to IIS, 1056 outputting image from HttpHandler, 1054–1055 overview of, 1045, 1201–1202 sample Web page using HttpHandler as image source, 1056 summary, 1058 HttpApplication object, 838–839 HttpBrowserCapabilities class, 1251 HttpCachePolicy, 871–873 HttpContext.Current.Items, 863–864 HttpHandlers adding HttpHandlers configuration to web.config, 1057–1058 ASP.NET MVC built on, 1068 class based image HttpHandler, 1056–1057 core mechanisms in ASP.NET, 1047–1048 generic handlers, 1052–1053 outputting image from HttpHandler, 1054–1055 overview of, 1052 page template, 1053–1054 sample Web page using HttpHandler as image source, 1056 HttpModules adding HttpModules configuration to web.config, 1051–1052 altering output of ASP.NET web page, 1049–1051

ASP.NET MVC built on, 1068 core mechanisms in ASP.NET, 1047–1048 events trigged by Init method, 1049 implementing IHttpModule interface, 1048–1049 overview of, 1048 element, 1245 element, 1262 HttpSessionState object, 839 HttpWebRequest and HttpWebResponse, 971–974 HyperLink control adding HyperLinkField control to GridView, 273 overview of, 96

I IAsyncResult interface, 353 icons, specifying custom icons in TreeView control, 537–538 IController interface, 1073–1074 ID (identification) impersonation and, 827–828 server controls and, 998 ID selectors, CSS, 689–690 IE (Internet Explorer) configuring browser capabilities, 1251 IE8 Developer Tools, 1383–1384 If...Then statement for checking authentication, 823 determining if check boxes are checked, 103–104 IgnoreFileBuildProvider, 36 IHttpHandler interface, 1054 IHttpModule interface, 1048–1049 IIS (Internet Information Services) vs. ASP.NET Development Server, 911–912 implementing Basic authentication, 812–813 Local IIS option of Copy Web Site tool, 1343 mapping file extension to, 1056 metabase, 1239 for security, 828 using IIS 6 with ASP.NET, 1046 using IIS 7 with ASP.NET, 1046–1047 Web server options, 3 working with file extensions, 829–831 working with web.config and machine.config files, 832–833 IIS Manager Application Settings section, 1333 Connection Strings section, 1333 editing configuration files, 1278–1279 IP addresses and domain name restrictions, 829 managing configuration with, 1317

1448

www.it-ebooks.info

IIS MMC snap-in – IntelliTrace (Visual Studio)

migrating older ASP.NET projects and, 1371, 1375 .NET Compilation section, 1330–1331 .NET Globalization section, 1331 .NET Profile section, 1331 .NET Roles section, 1331–1332 .NET Trust Levels section, 1332 .NET Users section, 1332–1333 overview of, 1329 Pages and Controls section, 1334 Providers section, 1334–1335 Session State section, 1335 SMTP E-mail section, 1336 IIS MMC snap-in editing configuration files, 1278–1279 managing configuration with, 1239 IisTraceListener, 908 IisTraceWebEventProvider, 1298 Image control initiating drop-down list, 764–766 overview of, 109–110 using DropShadowExtender with, 766–767 ImageButton control, 95 causing validation, 160 ImageMap control, 150–152 images adding divider images to menu items, 549–550 creating slideshow from, 785–786 including in themes, 227–229 outputting image from HttpHandler, 1054–1055 sample Web page using HttpHandler as image source, 1056 server controls for accessing embedded resources, 1006 specifying custom icons in TreeView control, 537–538 using as path separator, 525 using for error notifications, 180–181 using images for Web Part verbs, 665–666 using ResizableControlExtender control with, 782–783 Images folder, within theme folder, 227 ImageUrl property, Image control, 109 Immediate window, running code in design mode, 897–898 impersonation, identity and, 827–828 @Implements page directive, 17 implicit types, 1419–1420 @Import page directive, 15–17 !important attribute, for controlling style override, 702 include files added to Web pages, 1006

for common sections, 188–189 configuring, 1262–1263 InfoSet, XML, 408–409 inheritance CSS (Cascading Style Sheets), 694–695 EDM and, 1119–1122 Init method, HttpModule, 1049 Initialize() method, XmlMembershipProvider class, 505–507 InitialValue property, RequiredFieldValidator control, 163 initParams parameter, Silverlight 3, 1407–1408 inline coding, 6–7 inline data-binding syntax, 302 inline styles, CSS, 687 in-place precompilation, 33 InProc mode providers for storing session state, 458 for storing session state, 476–477 in-process (InProc) session state, 840–846 choosing best option for maintaining state, 856 InProcSessionStateStore, 476–477 limitations of, 840 making sessions transparent, 843–845 optimizing session performance, 845–846 overview of, 840 storing data in Session object, 841–843 Web gardening and, 840–841 InProcSessionStateStore, 476–477 InsertCommand, SqlDataSource control, 287–288 inserting data ADO.NET tasks, 312 DetailsView control, 287–288 LINQ to SQL, 399–401 InsertItemTemplate, ListView control, 341–342 installation program, 1351–1354 installing, 749 instrumentation application tracing, 1296–1297 buffering Web events, 1308–1310 e-mailing Web events, 1310–1314 event logs. See event logs health monitoring. See health monitoring overview of, 1285 performance counters. See performance counters routing events to SQL Server, 1305–1308 summary, 1314 writing events via configuration, 1305 integers, validating, 168–169 Integrated Windows authentication, 811–812 IntelliTrace (Visual Studio), 917–919

1449

www.it-ebooks.info

interface – LinkButton control

interface creating WCF service interface, 1182–1183 implementing WCF service interface, 1183–1184 XML Web service customers dataset, 1160–1162 Interface attribute, @Implements directive, 17 internal style sheets CSS, 686–687 style overriding and, 695 Internet Explorer (IE) configuring browser capabilities, 1251 IE8 Developer Tools, 1383–1384 Internet Information Services. See IIS (Internet Information Services) interprocess communication (IPC), 970 invariant cultures, 1218 I/O (input/output). See File I/O (input/output) IP address restrictions, 829 IPC (interprocess communication), 970 iPhone, configuring browser capabilities, 1251 IronPython calling IronPython libraries from ASP.NET, 1421–1422 overview of, 1420 IronRuby, 1420 ISAP_Rewrite, from Helicon, 1392 IsCrossPagePostBack property, Page class, 26 IsPostBack property, Page class, 21 items adding to collections, 102 allowing users to select multiple, 100 checking value of item selected from RadioButtonList control, 108–109 rendering with ListView control, 291–292 Items collection, HttpContext, 863–864 ItemTemplate adding hover button to, 773 DataList control, 331–334 GridView control, 274, 279 ListView control, 340–341

J JavaScript accessing scriptable objects, 1414 AJAX and, 711 Ajax Minifier and, 1389 buttons working with client-side JavaScript, 93–94 Client Script Library from Microsoft, 713–714 client-side callback functions, 1009–1010 client-side scripts added to browsers, 1002–1003 creating managed types in, 1415

debugging client-side, 920–921 exposing Silverlight managed code in, 1412–1415 jQuery for working with, 1385 migrating ASP.NET projects and, 1375 server controls for accessing embedded resources, 1006 Silverlight 3 and, 1410–1412 JavaScript API, Silverlight 3, 1410–1412 JetBrains, dotTrace from, 1386 JIT Debugging, 911 joins Entity Framework performing, 1117–1119 LINQ to Objects, 385–386 LINQ to XML, 390–391 JPEG image, outputting from HttpHandler, 1055 jQuery, 1385 .js files, 1375. See also JavaScript Just My Code debugging, 916

K Kerberos, 812 Kotari, Nikhil, 862

L Label control, 86–88 building simple page with AJAX, 718 expanding or collapsing Panel control, 760 overriding attribute from, 204 Language Integrated Query. See LINQ (Language Integrated Query) language pseudo class, CSS, 691 languages adding resource file for additional language, 1232–1233 mixing on master pages, 196–197 late binding .NET components, 1100 support, 1419 late-bound access, to personalization items, 574 Launch Conditions Editor, 1366–1367 layers, of Entity Framework, 1106–1107 layouts Dynamic Data, 1141 ListView control, 290 of menu items, 548–549 LayoutTemplate, ListView control, 338–340 leaf nodes, TreeView control, 531, 535 LinkButton control causing validation, 160 overview of, 94

1450

www.it-ebooks.info

links – LoadControl method

links Login control, 616 managing CSS links in master pages, 706 LINQ (Language Integrated Query) as alternative to traditional queries, 378 extending, 403 filtering using Where parameters, 250 grouping using GroupBy clause, 251 overview of, 371 query parameters, 249 QueryExtender for complex filters, 251–252 sorting using OrderBy clause, 250 summary, 403 using with WCF data service, 1215 LINQ to Entities, 1129–1130 LINQ to Objects, 371–387 adding LINQ namespace, 378 controlling data ordering, 381–382 creating custom projections, 379–380 creating custom projections field names, 380–381 creating queries, 379 data grouping, 384 delayed execution, 382 group filters, 384–386 joins, 385–386 overview of, 371 paging using LINQ, 386–387 query operators, 385 where filters, 382–384 LINQ to SQL creating new data context, 394 deleting data, 402 Dynamic Data and, 1129–1130 grouping data, 396–397 incorporating database into Dynamic Data application, 1137–1139 inserting data, 399–401 overview of, 391–393 querying data from movie example, 394–395 querying data using a view, 397–398 stored procedures and, 398–399 updating data, 401 writing query to output window, 395–396 LINQ to XML, 387–391 bridging XmlSerializer and LINQ to XML, 428–429 creating CLR objects with, 423–424 creating XML with, 426–428 example of movie data as XML data file, 387 joins, 390–391

mapping XML element using LINQ, 389–390 overview of, 387 querying XDocuments, 433 querying XML data file, 388–389 validating XDocument against XML Schema, 418–419 LinqDataSource control, 247–253 data concurrency and, 252 description of, 239 events, 253 filtering queries using Where parameters, 250 grouping queries using GroupBy clause, 251 overview of, 247–249 query parameters, 249 QueryExtender for complex filters, 251–252 sorting queries using OrderBy clause, 250 Lipton, Eilon, 1061 list styles, BulletedList control, 125 List.ascx page, Dynamic Data, 1136 ListBox control adding items to collections, 102 allowing users to select multiple items, 100 example of use of, 100–102 extending with ListSearchExtender control, 774 overview of, 100 visually removing items from a collection, 98–99 ListItems bulleted lists and, 124 visually removing items from a collection, 98–99 ListSearchExtender control, 774 ListView control adding hover button to, 773 connecting to databases, 337–338 creating layout templates, 338–340 data binding and commands, 293–294 EditItemTemplate, 341 EmptyItemTemplate, 293, 341 enabling for Dynamic Data, 1151 getting started with, 289–290 group rendering, 292–293 InsertItemTemplate, 341–342 item rendering, 291–292 ItemTemplate, 340–341 layout templates in, 337 overview of, 289, 336–337 paging and DataPager control, 294–296 templates, 291 viewing defined elements, 342–344 Literal control, 88 LoadControl method, 983

1451

www.it-ebooks.info

local resources – @MasterType page directive

local resources, 1230–1235 adding resource file for additional language, 1232–1233 building basic page for localization, 1230–1231 finalizing Default.aspx page, 1233–1235 localization of sitemaps, 558–559 preference for neutral cultures, 1235 storing in App_LocalResources folder, 31 localization. See local resources LocalSqlServer, 468 location options, applications, 1–4 logging logging buffer mode, 1309 Web events, 479–480 logical layer, of Entity Framework, 1106, 1107 logical operators, filtering WCF data with, 1212 login credentials, 614–616 denying access by turning off element, 613–614 locking out users who provide invalid passwords, 617–620 login/logout features of LoginStatus control, 620–621 NoBot control for limiting login forms, 798–799 programmatic, 617 requesting login credentials, 613 Login control forms-based authentication and, 820–821 login credentials provided by, 614–616 rounding corners of Panel control containing, 783–784 Login.aspx, 815–816 LoginName control, 621–622 LoginStatus control, 620–621 LoginView control controlling information viewed via, 627–629 providing view for particular group, 629 LosFormatter, 860

M machine.config. See also configuration files applying configuration settings, 1243–1244 caching settings in, 874 element, 1250–1251 element in, 715–716 deleting configuration settings, 1244 GUI tool for editing, 832 element, 1245 include files supported by, 1262 manipulating, 1272–1273 membership provider settings in, 612–613

provider definitions in, 492 role management provider settings, 630–631 server configuration files, 1240–1241 storing usernames in registry and referencing settings in, 1274–1275 web.config and, 1315 managed code accessing DOM using, 1415–1417 exposing in JavaScript, 1412–1415 management. See administration many-to-many relationships, in Entity Framework, 1116–1119 many-to-one relationships, in Entity Framework, 1116–1119 mapping layer (data access layer), of Entity Framework, 1106, 1107 Mapping Specification Language (MSL), 1107 MARS (Multiple Active Result Set) asynchronous command execution and, 352 multiple wait handles used in conjunction with, 360–363 MaskedEditExtender control, 774–776 MaskedEditValidator control, 774–776 @Master page directive, 13 master pages AJAX and, 214–216 basics of, 189–190 caching with, 213–214 coding a content page, 192–196 coding a master page, 190–192 container-specific, 211–212 event ordering in, 212–213 example of, 191–192 managing CSS links in master pages, 706 mixing page types and languages, 196–197 nesting, 207–211 overview of, 187 programmatically assigning, 207 reasons for using, 187–189 specifying default content in, 205–206 specifying which page to use, 197–198 summary, 216 themes and, 221 Web Parts and, 681–682 working with controls and properties from, 199–205 working with page title, 198–199 MasterPageFile attribute page directives, 194–195 programmatically assigning master page, 207 specifying which master page to use, 197–198 @MasterType page directive, 18

1452

www.it-ebooks.info

mathematical functions› – vMutuallyExclusiveCheckBoxExtender control

mathematical functions, filtering WCF data with, 1212 Max operator, LINQ to Objects queries, 385 maximum request length, runtime settings for ASP.NET applications, 1264 MD5 (Message Digest 5), 817, 863 membership and role management service adding users to, 600–601 authentication. See authentication authorization. See authorization diagram of, 598 .NET Users section of IIS Manager, 1332–1333 overview of, 597 public methods of Membership API, 640–641 role management. See role management summary, 642 Web Site Administration Tool for, 640 Membership API CreateUser() method, 609 public methods of, 640–641 server controls and membership providers and, 492 ValidateUser() method, 617 membership providers ActiveDirectoryMembershipProvider, 468–469 building custom providers, 497–498 machine.config settings, 612–613 overview of, 466–467 provider architecture and, 492 ProviderBase and, 496–497 SqlMembershipProvider, 467–468 XmlMembershipProvider. See XmlMembershipProvider memory-mapped files, 966–968 MemoryStream, 957–958 Menu control adding divider images to menu items, 549–550 binding to XML files, 551–552 changing layout of menu items, 548–549 data binding and, 301 dynamic style, 548 menu events, 550 overview of, 545–546 pop-out symbol for menu items, 549 predefined styles, 546–547 static style, 547 MenuItemClick event, 550–551 merge tools, 1394–1395 merged styles, CSS, 692–694 Message Digest 5 (MD5), 817, 863 method overloading, 1166–1169 methods vs. files, ASP.NET MVC and, 1061

Microsoft Ajax Minifier, 1389 Client Script Library, 713–714 Excel, 1425 IE (Internet Explorer), 1251, 1383–1384 Silverlight 3. See Silverlight 3 Web Deployment Tool (MSDeploy), 1346 migrating ASP.NET projects from ASP.NET 2.0/3.5 to 4, 1378–1379 converting ASP.NET 1.x application in Visual Studio 2010, 1375–1378 forms-based authentication when mixing versions of .NET Framework, 1372 hard coded JavaScript files and, 1375 overview of, 1369 running multiple versions of .NET Framework side by side, 1370 summary, 1379 upgrading ASP.NET applications, 1370–1371 upgrading ASP.NET reserved folders, 1372–1373 XHTML-compliant pages and, 1373–1375 migrating anonymous users, 584–585 Min operator, LINQ to Objects queries, 385 mod_rewrite, Apache Web server, 1392 ModalPopupExtender control, 762–763 models in ASP.NET MVC, 1060 in MVC, 1059 personalization model, 570 Model-View-Controller. See MVC (Model-ViewController) monitoring application execution. See tracing month, selection from calendar, 114 Mozilla Firebug. See Firebug Firefox. See Firefox MSDeploy (Web Deployment tool), 1346–1349 MSDN style, TreeView control, 530–531 .msi file, 1357 MSL (Mapping Specification Language), 1107 MSMQ, 1178 msvsmon.exe (Remote Debug Monitor), 914 MultiHandleSliderExtender controls, 784–785 Multiple Active Result Set (MARS) asynchronous command execution and, 352 multiple wait handles used in conjunction with, 360–363 multiple columns, 336 MultiView control, 138–141 MutuallyExclusiveCheckBoxExtender control, 776–777

1453

www.it-ebooks.info

MVC (Model-View-Controller) – operators, LINQ to Objects query operators

MVC (Model-View-Controller). See also ASP.NET MVC overview of, 1059–1060 on Web today, 1060–1061

N Name attribute, @Assembly directive, 18 named pipes, sending messages using, 970 Namespace attribute, @Import directive, 15 namespaces adding LINQ, 378 ADO.NET, 314–315 defining for Web services, 1193–1194 Provider namespace, 496 NameTable, for optimizing XmlReader, 419–420 NameValueFileSectionHandler object, configuration handlers, 1280–1281 navigation. See site navigation nesting master pages, 207–211 sitemap files, 566–567 .NET Compilation section, of IIS Manager, 1330–1331 .NET components deploying via private assemblies, 1102 deploying via public assemblies, 1103 early vs. late binding, 1100 error handling for, 1100–1102 using within COM objects, 1097–1100 .NET Globalization section, of IIS Manager, 1331 .NET Profile section, of IIS Manager, 1331 .NET Reflector tool, 750 .NET Remoting, 1178 .NET Roles section, of IIS Manager, 1331–1332 .NET Trust Levels section, of IIS Manager, 1332 .NET Users section, of IIS Manager, 1332–1333 Netscape, 1251 network communications FileWebRequest and FileWebResponse, 976–977 FtpWebRequest and FtpWebResponse, 974–976 HttpWebRequest and HttpWebResponse, 971–974 overview of, 970 sending e-mail, 977 network traffic, analyzing with Firebug, 1381–1382 NetworkStream, reading from, 958–959 neutral cultures overview of, 1218 preference for, 1235 New Style dialog box, Visual Studio 2010, 703–704 Nielsen, Jakob, 1069 NoBot control, 798–799

nodes, SiteMapDataProvider ShowStartingNode property, 552–553 StartFromCurrentNode property, 553–554 nodes, SiteMapDataSource control StartingNodeOffset property, 554 StartingNodeUrl property, 554 nodes, TreeView control adding programmatically, 543–545 expanding/collapsing programmatically, 540–543 overview of, 531 specifying lines for connecting, 538–540 Normal Mode, Portal Framework, 644 normal positioning, CSS elements, 698–699 NTLM authentication, 812 null data conditions, GridView control, 265–267 NullDisplayText property, 267 numbers in different cultures, 1224–1225 styles in bulleted lists, 125 NumericaUpDownExtender control, 778

O OAEP (Optional Asymmetric Encryption and Padding), 487 Obasanjo, Dare, 451 Object Relation designer. See O/R (Object Relation) designer ObjectDataSource control code generated by configuration wizard, 258 creating Customer class and CustomerRepository class, 256 description of, 239 overview of, 256–259 object-oriented languages, 1166 ODP.NET, 329
    (ordered list) element, 124 OnClick events, Button control, 91 one-to-many relationships, in Entity Framework, 1113–1116 one-to-one relationships, in Entity Framework, 1113–1116 Onion, Fritz, 862 online resources, 1427–1428 OnSelectionChange attribute, Calendar control, 113 OnUpdated events, animations and, 790–791 Opera AJAX compatibility and, 713 configuring capabilities of, 1251 Operator property, 167 operators, LINQ to Objects query operators, 385

    1454

    www.it-ebooks.info

    optimistic concurrency, in LINQ to SQL – page postbacks

    optimistic concurrency, in LINQ to SQL, 402 Optional Asymmetric Encryption and Padding (OAEP), 487 O/R (Object Relation) designer creating entities and, 1107 incorporating database into Dynamic Data application, 1137 in LINQ to SQL, 392 working with Entity Framework, 1109–1110 Oracle deprecated support in ADO.NET, 329 SqlDataSource control used with, 247 using as database with ASP.NET, 329–330 OracleClient object, 330 Oracle.DataAccess.Client namespace, 330 OrderBy clause LINQ to Objects, 381–382 sorting LINQ queries using, 250 out-of-process session state, 846–851 adding strongly typed property to base page class, 848–849 aspnet_state.exe, 846–847 best practices for session management, 850–851 choosing option for maintaining state, 856 OutOfProcSessionStateStore, 477 serializable objects and, 847–848 setting/retrieving objects from session, 849–850 OutOfProcSessionStateStore, 477 output caching, 865–869 extending element, 868–869 overview of, 865–866 post-cache substitution, 870–871 runtime settings for ASP.NET applications, 1265 VaryByControl attribute, 867 VaryByCustom attribute, 867–868 VaryByParam attribute, 866 Output File Name, deployment projects, 1357 @OutputCache page directive caching with master pages, 213–214 overview of, 18–19 element, 868–869 overloading Web methods, XML Web services, 1166–1169

    P package files, in deployment projects, 1357–1358 packaging/deploying ASP.NET applications building ASP.NET package, 1346–1349 creating basic installation program, 1351–1354 Custom Actions Editor and, 1366

    deploying precompiled Web applications, 1344–1346 File System Editor and, 1360–1362 File Type Editor and, 1363–1364 installing deployed applications, 1354–1355 Launch Conditions Editor and, 1366–1367 methods of deployment, 1339 overview of, 1337 pieces of deployment package, 1338 Registry Editor and, 1363 steps to take prior to deployment, 1338–1339 summary, 1367 uninstalling deployed applications, 1355–1357 User Interface Editor and, 1364–1365 Visual Studio Copy Web Site approach, 1341–1344 Windows Installer approach, 1349–1351 working with deployment project properties, 1357–1360 XCOPY approach, 1339–1341 @Page defining culture at page level, 1222 overview of, 10–12 Page class IsCrossPagePostBack property, 26 IsPostBack property, 21 page directives applying themes from within, 218 @Assembly, 18 @Control, 14 Global.asax file, 42 @Implements, 17 @Import, 15–17 list of, 9 @Master, 13 MasterPageFile attribute, 194–195 @MasterType, 18 @OutputCache, 18–19 overview of, 9–10 @Page, 10–12 @PreviousPageType, 18 @Reference, 19 @Register, 17 StyleSheetTheme attribute, 221 @WebSevice page directive, 1156 page events, 19–20 in ASP.NET 1.0/1.1, 19 in ASP.NET 4.0, 19 firing order of, 19 Global.asax file, 42 lifecycle of, 993–994 Page_Load event, 180 page postbacks. See postbacks

    1455

    www.it-ebooks.info

    page recycling – personalization

    page recycling, Edit and Refresh debugging, 916 page structure options, 4–9 code-behind model, 7–9 inline coding, 6–7 overview of, 4–6 page template, HttpHandlers, 1053–1054 Page_PreInit, programmatically assigning master page, 207 PageCatalogPart control adding to Web Parts to pages, 655–656 combined with DeclarativeCatalogPart control, 662–663 PageerSetting property, GridView control, 270–271 PageerStyle property, GridView control, 270–271 pages, ASP.NET. See also web pages adding Dynamic Data to, 1149–1151 altering output of, 1049–1051 applying themes to, 217–219 in ASP.NET AJAX, 737–741 building with EDM, 1110–1113 configuring, 1260–1262 cross-page posting, 21–26 embedding Silverlight plug-in into, 1402–1404 error handling at page-level, 922–923 events, 19–20 GridView control in, 262–263 Pages and Controls section of IIS Manager, 1334 postbacks, 20–21 programmatically assigning themes to, 231 removing themes from, 220–221 summary, 47 tracing at page-level, 899 without themes applied to, 218 working with page title, 198–199 Pages and Controls section, of IIS Manager, 1334 PageTemplates subfolder, of DynamicData folder, 1136 paging bulleted lists, 778–779 enabling on DetailsView control, 285 GridView control and, 269–271 ListView control and, 294–296 using LINQ for, 386–387 PagingBulletedListExtender control, 778–779 Panel control CollapsiblePanelExtender control, 758–760 dragging around page, 763–764 expanding or collapsing, 760 populating, 768–769 rounding corners of, 783–784 scrollbars and, 122–123 using DropShadowExtender with, 767–768

    parameters Controllers, in MVC, 1074–1075 FilterParameters, 287 LINQ query parameters, 249–250 SelectParameters, 243–244, 287 Silverlight 3, 1406–1409 SqlParameter class, 322–325 XCOPY command, 1340–1341 parent node, TreeView control, 531 ParentLevelsDisplayed property, SiteMapPath control, 525–526 ParseExpression method, 305 partial page (user control) caching, 869 ValidationSummary control, 177 Passport authentication configuring, 1255 overview of, 821 PasswordRecovery control, 625–626 passwords adding password values to web.config, 816–817 allowing users to change, 623–624 generating random, 626–627 locking out users who provide invalid, 617–620 retrieving forgotten, 625–626 simplifying with SqlMembershipProvider, 492–495 storing in registry and referencing settings in machine.config, 1274–1275 storing in XML user data store, 502 strong passwords required in ASP.NET 4, 603 stronger structures with SqlMembershipProvider, 495–496 PasswordStrength control, 799–800 PathDirection property, SiteMapPath control, 525 paths, working with, 943–946 PathSeparator property, SiteMapPath control, 523–525 PDBs (program database or debug), in Visual Studio, 910 Pederick, Chris, 1384 performance counters building browser-based Administrative Tool, 1292 overview of, 1290 viewing with Administrative Tool, 1290–1292 working with in ASP.NET, 1292–1296 Performance Monitor, 1290 permissions, for file uploads, 132–133 personalization adding group of personalization properties, 574–575 adding simple personalization property, 570–571

    1456

    www.it-ebooks.info

    personalization providers – profiles

    anonymous options, 583 anonymous profile storage, 583–584 application profiles, 590–591 auto-saving feature of profiles, 586–587 building ProfileManager.aspx page, 592–594 custom types, 576–579 default values, 579 defining types for personalization properties, 576 enabling anonymous identification, 579–582 examining ProfileManager.aspx page, 594–595 making personalization properties read-only, 579 migrating anonymous users, 584–585 model for, 570 .NET Profile section of IIS Manager, 1331 overview of, 569 ProfileManager class methods, 591–592 ProfileManager class properties, 591 of profiles, 585–586 programmatic access to, 584 in registration process, 605–608 running ProfileManager.aspx page, 595–596 summary, 596 using grouped personalization properties, 575–576 using personalization properties, 571–574 working with anonymous identification, 582 personalization providers SQL Server data provider, 587 SQL Server Express Edition, 587–588 SQL Server versions and, 588–590 SqlProfileProvider, 473–474 using multiple providers, 590 Personalization Services layer, 570 PIAs (Primary Interop Assemblies), 1425 PlaceHolder control, 124 poll approach, to asynchronous processing, 355–357 polymorphism, 1166 pop-out symbol, for menu items, 549 PopupControlExtender control, 780–781 Portal Framework. See also Web Parts building pages with multiple Web Parts, 644–645 classes, 666–669 creating custom Web Parts, 669–674 modifying zones, 660 Web Part classes, 666–669 zones, 645 ports, serial, 968–969 positioning elements, CSS, 698–701 absolute positioning, 700 floating elements, 701 normal positioning, 698–699 overview of, 698

    relative positioning, 699–700 PositionIsEverything, Reference Web site, 1387 postbacks accessing postback data in server controls, 1018–1021 capability added to server controls, 1015–1017 handling postback events in server controls, 1017–1018 overview of, 20–21 state management and, 857–859 tracking user controls across, 986 PostBackUrl property, 94, 858 post-cache substitution, 870–871 precompilation for deployment deploying Web applications and, 1344–1346 overview of, 33 precompiled business objects creating, 1082–1084 using, 1084–1085 @PreviousPageType, 18 Primary Interop Assemblies (PIAs), 1425 private assemblies deploying COM components with .NET via, 1093–1094 deploying .NET components via, 1102 Process Explorer (Russinovich), 1396–1397 processes, debugging, 913 element, 1265–1268 processor affinity, Web farms and, 840 element, of UpdateProgress control, 733 Professional IIS7 and ASP.NET Integrated Programming (Khosravi), 1047 ProfileBase class, 584 ProfileManager class methods, 591–592 properties, 591 ProfileManager.aspx page building, 592–594 examining, 594–595 running, 595–596 profilers, in Ultimate Tools list, 1386 profiles application profiles, 590–591 auto-saving feature, 586–587 building ProfileManager.aspx page, 592–594 examining ProfileManager.aspx page, 594–595 .NET Profile section of IIS Manager, 1331 overview of, 585–586 ProfileManager class methods, 591–592 ProfileManager class properties, 591 running ProfileManager.aspx page, 595–596 storing, 583–584

    1457

    www.it-ebooks.info

    element – provider Web Parts

    element, 1304 program database or debug (PDBs) files, in Visual Studio, 910 programmatic approaches to adding users, 609–612 assignment of master pages, 207 to authentication, 823–826 to caching, 873 expanding/collapsing nodes of TreeView control, 540–543 to login, 617 modifying provider behavior via attribute-based programming, 492 to personalization, 584 providing text to Label control, 86–87 to themes, 231 projections, LINQ to Objects, 379–381 properties accessing XAML properties in JavaScript, 1411 CSS Properties tool window, 706 deployment project, 1357–1360 disabling for themes, 233–235 exposing control properties, 982, 1022–1023 file, 947–948 File System Editor, Visual Studio, 1360–1361 GridView control, 283 populating control properties, 983 ProfileManager class, 591 SqlCommand class, 317–318 SqlDataAdapter class, 320 SqlParameter class, 322–323 working with from master pages, 199–205 XmlMembershipProvider class, 504 properties, personalization adding group of, 574–575 adding simple property, 570–571 default values, 579 defining types for, 576 making personalization properties read-only, 579 in registration process, 605–608 using, 571–574 using grouped properties, 575–576 using with CreateUserWizard control, 606–608 provider model ActiveDirectoryMembershipProvider, 468–469 in ASP.NET 4, 459–460 AuthorizationStoreRoleProvider, 472–473 building custom providers, 497–498 building ReadUserFile() method, 508–510 configuration providers, 485 configuring providers, 489

    connecting default provider to new SQL Server instance, 465–466 constructing class skeleton for custom provider, 499–501 creating CustomProviders application, 498 defining ApplicationName property, 504–505 DpapiProtectedConfigurationProvider, 485–486 EventLogWebEventProvider, 479–480 examining ProviderBase class, 496–497 extending Initialize() method, 505–507 extending pre-existing providers, 511 for health monitoring, 1298 InProcSessionStateStore and, 476–477 LimitedSqlRoleProvider, 511–518 membership providers, 466–467 modifying provider behavior via attribute-based programming, 492 OutOfProcSessionStateStore and, 477 overview of, 457–459 personalization provider, 473–474 providers as one tier in larger architecture, 491–492 Providers section of IIS Manager, 1334–1335 role providers, 469–470 RsaProtectedConfigurationProvider, 486–487 session state providers, 476 setting up provider to work with SQL Server, 461 SimpleMailWebEventProvider, 480–482 SiteMapProvider, 474–475 SQL Server Setup Wizard command-line tool, 461–463 SQL Server Setup Wizard GUI tool, 463–465 SqlMembershipProvider, 467–468, 492–496 SqlRoleProvider, 470–471 SqlSessionStateStore and, 477–478 SqlWebEventProvider, 482–483 summary, 489, 518 TemplatedMailWebEventProvider, 482 third-party providers extending session state, 854 TraceWebEventProvider, 483–484 validating users, 507–508 web event providers, 478–479 Web Parts provider, 487–489 WindowsTokenRoleProvider, 471–472 WmiWebEventProvider, 484–485 XmlMembershipProvider, 502–504, 510–511 Provider tab, ASP.NET Web Site Administration Tool, 1328–1329 provider Web Parts building, 675–677 connecting Web Parts and, 674

    1458

    www.it-ebooks.info

    ProviderBase class – Relational-Object-XML (ROX)

    ProviderBase class, 496–497 Providers section, of IIS Manager, 1334–1335 element, 1301–1302 pseudo classes, CSS, 690–691 pseudo elements, CSS, 691 public assemblies deploying COM components with .NET applications via, 1094–1095 deploying .NET components via, 1103 public methods of Membership API, 640–641 of Roles API, 641 Publish Web dialog, Visual Studio 2010, 1347 publishing applications, 1345 pull-style API, XmlReader as, 413

    Q queries, LINQ. See LINQ (Language Integrated Query) queries, traditional creating Movie class for examples, 372 filtering list of Movie objects, 376 generating list of Movie objects and binding to GridView, 372–376 grouping and sorting list of Movie objects, 377–378 overview of, 371 replacing with LINQ, 378 Query Builder, 346–347 QueryExtender, 251–252 querying WCF data service, 1201–1202 expanding on associations, 1207–1210 filtering content, 1211–1212 moving around in result sets, 1211 ordering result sets, 1210 overview of, 1201–1202 reading specific item from table, 1203–1205 reading table of data, 1202–1203 working with relationships and, 1205–1207 QueryStrings client-side state management, 837, 857 methods for storing state, 458 QuirksMode.org, Reference Web site, 1387

    R RadioButton control, 106–108 RadioButtonList control, 98–99, 108–109 random password generation, 626–627 RangeValidator control description of, 160 overview of, 167

    testing integer value with, 168–169 testing string date value with, 169–171 Rating control, 800–801 RCW (Runtime Callable Wrapper), 1086 Reader class, File I/O (input/output), 960 reading/writing binary data, 961–962 reading/writing files overview of, 953 Reader and Writer classes, 960 reading from NetworkStream, 958–959 stream classes, 954 with StreamReader, 960–961 streams and, 953 using FileStream to read system file, 954–956 using I/O enumerations to control file behavior when writing files, 957 Using statements for reading files, 955–956 writing to MemoryStream, 957–958 ReadOnly property adding to BoundField, 277–278 making personalization properties read-only, 579 ReadSubtree, XmlSerializer and, 422–423 ReadUserFile() method, XmlMembershipProvider class, 508–510 Red Gate Software ANTS profiler, 1386 Reflector from, 1395–1396 Refactor! for ASP.NET, from DevExpress, 1388 @Reference page directive, 19 reference sites, online, 1386–1387 Reflector, from Red Gate, 1395–1396 regions, 1217–1218 @Register page directive, 17 registration process changing how users register with applications, 612–613 managing user registration with CreateUserWizard control, 601–602 personalization in, 605–608 registry, storing username and passwords in, 1274–1275 Registry Editor, Visual Studio, 1363 RegularExpressionValidator control checking text-box value in e-mail address, 171–172 description of, 160 overview of, 171 relational databases mapping application objects to relational database schemas, 1106 XML and, 451 Relational-Object-XML (ROX), 451

    1459

    www.it-ebooks.info

    relationships – routes

    relationships expanding on associations, 1207–1210 WCF Data Services framework, 1205–1207 relationships, in Entity Framework many-to-one and many-to-many relationships, 1116–1119 one-to-one and one-to-many relationships, 1113–1116 relative paths, 936 relative positioning, CSS, 699–700 release configuration, in Visual Studio vs. debug configuration, 911 overview of, 910 Remote Debug Monitor (msvsmon.exe), 914 remote debugging, 913–914 Remote Site option, connecting to remote or local servers, 4 RepeatColumns property, of DataList control, 336 RepeatDirection property, of DataList control, 336 RepeatLayout property, of DataList control, 333–334 Request for Comments (RFC) 1766, culture standards, 1217–1218 request object, attaching cache dependencies to, 890 request/response process, in AJAX, 710–712, 721–722 requests, HTTP, 836 RequiredFieldValidator control, 160–164 description of, 160 disallowing blank entries and requiring changes, 163–164 generated by RequiredFieldValidator control, 162–163 InitialValue property, 163 looking at results generated, 162–163 simple example, 160–162 validating drop-down lists with, 164 ResizableControlExtender control, 781–783 resource editor, in Visual Studio 2010, 1237 resource files (.resx) creating, 560–561 global resources, 1235–1237 local resources, 1230–1235 localization of ASP.NET applications, 558 overview of, 1230 resource editor in Visual Studio 2010, 1237 server controls for accessing embedded resources, 1006 storing global and local, 31 ResourceExpressionBuilder, 304 result sets moving around in result sets, 1211 ordering WCF, 1210 RFC (Request for Comments) 1766, culture standards, 1217–1218

    RIAs (Rich Internet Applications), 1399 Roeder, Lutz, 750 role management. See also membership and role management service adding users to roles, 635 adding/retrieving application roles, 632–634 for administrators, 562–564 caching roles, 639–640 checking user roles, 824 checking users in roles, 638–639 deleting roles, 634–635 denying roles using verbs, 1259 getting all roles of a particular user, 637–638 getting all users of a particular role, 635–637 granting access to Admin role, 1259–1260 making changes to element of machine.config, 630–631 making changes to web.config, 632 .NET Roles section of IIS Manager, 1331–1332 overview of, 630 public methods of Roles API, 641 removing users from roles, 638 from Security tab of Web Site Administration Tool, 1323–1324 WindowsBuiltInRole enumeration, 825 role providers AuthorizationStoreRoleProvider, 472–473 creating LimitedSqlRoleProvider, 511–514 overview of, 469–470 settings in machine.config.comments, 630–631 SqlRoleProvider, 470–471 using LimitedSqlRoleProvider, 515–518 WindowsTokenRoleProvider, 471–472 element, 629 element, 630–631 Roles API, 641 root node of sitemap file, 552–553 TreeView control, 531 RoundedCornersExtender control, 783–784 route matching, 1073 RouteData, 1073 routes ASP.NET MVC and, 1068–1069 defining routes, 1070 high-level requests and, 1072 how routes tie URLs to actions, 1072 route matching and RouteData, 1073 routing compared to URL rewriting, 1069 routing Dynamic Data, 1144–1147 setting route URLs, 1070–1072

    1460

    www.it-ebooks.info

    RowDataBound events – SelectionMode attribute

    RowDataBound events GridView control, 264–265 selecting drop down list item, 279–281 rows dynamically adding to tables, 110–111 editing row data in GridView, 275 RowState property, GridView control, 280 RowUpdated event, 278 ROX (Relational-Object-XML), 451 RsaProtectedConfigurationProvider, 486–487 RSS feeds, 255 RssCacheDependency class, 876–879 rules, ACLs adding, 951–952 removing, 952–953 rules, CSS merged styles, 692–694 overview of, 687–688 pseudo classes, 690–691 pseudo elements, 691 selector combinations, 691–692 selector grouping, 691 selectors, 688–690 element, 1302–1304 Runtime Callable Wrapper (RCW), 1086 runtime settings, configuring, 1263–1265 Russinovich, Mark, 1396

    S Safari, 1251 salted hash, 861 scaffolding, 1130 Scooter Software, 1395 screen scraping, 971 script combining overview of, 741–742 ScriptManager control and, 744–745 ScriptReferenceProfiler control and, 742–744 ScriptableMember attribute, exposing Silverlight class properties with, 1414–1415 ScriptableType attribute, exposing Silverlight class with, 1412–1413 ScriptControl class, 988 ScriptManager control adding to from content page, 726 AJAX server-side controls included in ASP.NET, 723–725 description of, 723 script combining and, 744–745 ScriptManagerProxy control

    AJAX server-side controls included in ASP.NET, 725–726 description of, 723 overview of, 216 ScriptReferenceProfiler control, 742–744 scripts. See also JavaScript client-side. See client-side scripts including on master pages, 215 using SQL scripts to install personalization features, 589 scrollbars, Panel control and, 122–123 search engine optimization (SEO), 1069 Secure Sockets Layer (SSL) Basic authentication used in conjunction with, 812 connecting to remote servers using, 1343 security applying authentication measures, 806 ASP.NET MMC snap-in, 832 element of web.config, 806–807 event log, 1288 file extensions, 829–831 File I/O (input/output) and, 927 forms-based authentication. See forms-based authentication identity and impersonation, 827–828 IIS for, 828 IIS Manager, 832–833 IP addresses and domain name restrictions, 829 overview of, 805–806 programmatic authentication. See authentication, programmatic protecting configuration settings, 1274–1278 summary, 833 Windows-based authentication. See Windows-based authentication Security Setup Wizard, 1318–1322 Security tab, ASP.NET Web Site Administration Tool creating new users, 1322 managing access rules, 1324–1325 managing roles, 1323–1324 managing users, 1323 overview of, 1317 Security Setup Wizard, 1318–1322 security trimming enabling, 564–566 overview of, 561 role management for administrators, 562–564 SelectionMode attribute Calendar control, 114 ListBox control, 100

    1461

    www.it-ebooks.info

    selectors, CSS– Session object

    selectors, CSS combining, 691–692 grouping, 691 types of, 688–690 SelectParameters DetailsView control, 287 filtering using, 243–244 SEO (search engine optimization), 1069 SeparatorTemplate, DataList control, 334 serial ports, File I/O and, 968–969 serializable classes, in Framework Class Library, 849 Serializable objects in out-of-process sessions, 848, 850 ViewState and, 860 serialization bridging XmlSerializer and LINQ to XML, 428–429 ReadSubtree and XmlSerializer, 422–423 server configuration files, 1240–1243 server controls. See also Web server controls accessing embedded resources, 1006 accessing global resources, 1235–1236 adding Web Control Libraries to Web pages, 989–990 asynchronous callbacks, 1007–1010 .browser files, 1010–1011 class attributes, 992 class template, 990–992 client-side script include files added to Web pages, 1006 client-side scripts added to browser, 1002–1003 client-side scripts and event attributes for validating data, 1003–1005 composite controls, 1021–1023 creating, 987–988 defined, 979 designer actions added to control designer, 1040–1042 designer attribute added to control class, 1031–1032 designer regions defined by custom designer classes, 1034–1040 designer regions used to create composite control, 1032–1033 design-time experiences and, 1027 detecting browser capabilities in, 1011–1012 exposing server control from master page as public property, 204 IDs, 998 membership controls, 492 overview of, 987 page event lifecycle and, 993–994 postback capability added to, 1015–1017 postback data accessed in, 1018–1021 postback events handled in, 1017–1018

    programmatically assigning control’s SkinID, 231 property/event attributes, 992–993 removing themes from, 220 rendering, 993 rendering HTML tag attributes, 996–997 rendering HTML tags, 994–996 SiteMapPath. See SiteMapPath control styling HTML with, 998–1001 summary, 1042 templated controls, 1023–1027 themes and skins, 1001–1002 TypeConverter class, 1028–1031 types of, 85 UI Type editor added to control property, 1042 using ControlState in, 1014–1015 using ViewSate in, 1012–1014 server controls, ASP.NET AJAX Accordion control, 793–795 CascadingDropDown control, 795–798 NoBot control, 798–799 overview of, 722–723, 793 PasswordStrength control, 799–800 Rating control, 800–801 ScriptManager control, 723–725 ScriptManagerProxy control, 725–726 TabContainer control, 801–802 TabPanel control, 801 Timer control, 726–727 UpdatePanel control, 727–731 UpdateProgress control, 731–733 using multiple UpdatePanel controls, 733–736 Server Explorer, 1285 server-side culture declarations, 1221–1222 detecting browser capabilities in server-side code, 1011–1012 state management options, 837 technologies in ASP.NET AJAX, 714 server-side validation vs. client-side, 158–159 combining client-side and server-side validation, 176 with CustomValidator control, 174–176 service reference, adding for WCF service, 1187–1189 service-oriented architecture (SOA), 1178–1179 services. See Web services Session object configuring session state management, 839 cookies compared with, 857 optimizing session performance, 845–846 overview of, 838 storing data in, 841–843

    1462

    www.it-ebooks.info

    session state – site navigation

    session state choosing best option for maintaining state, 856 configuring, 1240, 1246–1247 configuring management of, 839–840 configuring with connection string, 1249 cookieless, 855 custom state store, 1249 implementing custom providers, 854 in-process session state. See in-process (InProc) session state methods for storing, 457 options for managing, 835–837 out-of-process session state, 846–851 providers for storing, 458, 476–477 server-side state management options, 837 Session State section of IIS Manager, 1335 SQL-backed session state, 851–853 SQLServer, 1248–1249 StateServer, 1247–1248 third-party providers extending, 854 Session State section, of IIS Manager, 1335 session timeout, 1335 sessions and event model, 838–839 element, 839 SessionStateModule, 854 SetFocusOnError property, 184–185 SGML, 1154 SHA1, 817, 863 shortcuts, File I/O, 962–964 ShowCheckBoxes property, TreeView control, 534–535 ShowLines property, TreeView control, 538–540 ShowStartingNode property, SiteMapDataSource control, 552–553 ShowToolTips property, SiteMapPath control, 526 Silverlight 3 accessing DOM using managed code, 1415–1417 client-side API, 1409–1410 creating new application, 1400–1402 embedding Silverlight plug-in into ASP.NET page, 1402–1404 enableAutoZoneZoom parameter, 1409 enableGPUAcceleration parameter, 1409 enablehtmlaccess parameter, 1408 enableNavigation parameter, 1409 exposing managed code in JavaScript, 1412–1415 HTML Bridge, 1412 initParams parameter, 1407–1408 JavaScript API, 1410–1412 overview of, 1399 providing custom content when plug-in not installed, 1404

    splashScreenSource parameter, 1406–1407 summary, 1417–1418 windowless parameter, 1404–1406 Silverlight 3 Programmer’s Reference (Little, Beres, Hinkson, Rader, and Croney), 1399 Simple Object Access Protocol. See SOAP (Simple Object Access Protocol) SimpleMailWebEventProvider, 480–482, 1298, 1310 SingleTagSectionHandler object, 1281–1282 site navigation adding divider images to menu items, 549–550 adding nodes to TreeView control programmatically, 543–545 binding menu controls to XML files, 551–552 binding TreeView control to XML file, 532–534 changing layout of menu items, 548–549 child elements of SiteMapPath control, 526–527 creating assembly resource (.resx) files, 560–561 culture detection added to web.config, 559–560 dynamic style for Menu control, 548 enabling security trimming, 564–566 examining parts of TreeView control, 531–532 expanding/collapsing nodes of TreeView control programmatically, 540–543 identifying TreeView control’s built-in styles, 530–531 menu control and, 545–546 menu events and, 550 nesting sitemap files, 566–567 overview of, 519 ParentLevelsDisplayed property, 525–526 PathDirection property, 525 PathSeparator property, 523–525 pop-out symbol for menu items, 549 role management for administrators, 562–564 security trimming, 561 selecting multiple options in TreeView control, 534–537 ShowLines property of TreeView control, 538–540 ShowStartingNode property of SiteMapDataSource control, 552–553 ShowToolTips property, 526 SiteMap API, 555–557 sitemap localization, 558, 561 SiteMapDataSource control, 552 SiteMapPath control, 521–523 SiteMapProvider, 474–475 specifying custom icons in TreeView control, 537–538 StartFromCurrentNode property of SiteMapDataSource control, 553–554

    1463

    www.it-ebooks.info

    site navigation – SQL Server

    site navigation (continued) StartingNodeOffset property of SiteMapDataSource control, 554 StartingNodeUrl property of SiteMapDataSource control, 554 static style for Menu control, 547 summary, 567 TreeView control, 527–530 URL mapping, 557–558 Web.sitemap structured for localization, 558–559 working with TreeView control programmatically, 540 XML-based sitemaps, 520–521 SiteMapDataSource control description of, 239 overview of, 259, 552 ShowStartingNode property, 552–553 StartFromCurrentNode property, 553–554 StartingNodeOffset property of SiteMapDataSource control, 554 StartingNodeUrl property, 554 TreeView control and, 300–301 using Menu control with, 301 SiteMapPath control child elements of SiteMapPath control, 526–527 overview of, 521–523 ParentLevelsDisplayed property, 525–526 PathDirection property, 525 PathSeparator property, 523–525 ShowToolTips property, 526 SiteMapProvider, 474–475 sitemaps defined, 519 nesting sitemap files, 566–567 SiteMap API, 555–557 XML-based, 520–521 sitemaps, localization of creating assembly resource (.resx) files, 560–561 culture detection added to web.config , 559–560 overview of, 558 testing, 561 Web.sitemap structured for localization, 558–559 .skin file, implementing themes via, 31 SkinID, programmatically assigning control’s ID, 231 skins creating, 222–224 multiple skin options, 229–231 server controls, 1001–1002 uses of .skin files, 220–221 using CSS class in server controls defined in .skin file, 228–229

    using images with themes, 227 Skip method, paging using LINQ, 386–387 SliderExtender control, 784–785 SlideShowExtender control, 785–787 smart tags, in VB editor, 897 SMTP managing SMTP configuration, 1326 setting up in web.config, 1311 SMTP E-mail section of IIS Manager, 1336 sniffers, 528 SOA (service-oriented architecture), 1178–1179 SOAP (Simple Object Access Protocol) building Web services using SOAP headers, 1170–1172 consuming Web services using SOAP headers, 1172–1173 exposing custom datasets as, 1157–1158 requesting Web services using SOAP 1.2, 1174–1175 request/response process, 1154–1155, 1173 XML and, 1154 SOAP body, 1170 SOAP envelope, 1170 SOAP headers, 1170–1173 Solution Explorer Create Package option, 1348 incorporating database into Dynamic Data application, 1137 overview of, 1130–1131 sorting columns, in GridView, 267–269 sorting data OrderBy clause, 250 in traditional queries, 377–378 sorting strings, in different cultures, 1227–1229 sounds, using for error notifications, 180–181 SourceGear, 1395 SourceSwitch, for diagnostics, 909 specific cultures neutral cultures preferred, 1235 overview of, 1218 reverting to specific culture when displaying currencies, 1228 splashScreenSource parameter, Silverlight 3, 1406–1407 SQL databases connecting to, 316 querying to return XML using FOR XML AUTO clause, 446–448 reading data from, 311–312 SQL Server adding column of typed XML, 455 adding column of untyped XML, 452–453

    1464

    www.it-ebooks.info

    SQL Server 2000 – SqlMembershipProvider

    adding XML Schema, 453–455 authenticating against values in a database, 818–819 cache dependencies. See SQL Server Cache Dependency command-line tool for setting up providers to work with, 461–463 configuring cache invalidation, 886–887 configuring personalization provider for, 589 connecting default provider to new server instance, 465–466 connecting role management system to, 468 data providers, 587–588 deleting data, 313–314 extracting XML from using System.Data.DataSet, 434–435 generating custom XML from, 451–452 GUI tool for setting up providers to work with, 463–465 inserting data into, 312 inserting XML data into XML column, 455–456 personalization providers, 588–590 retrieving XML from, 448–450 routing Web events to, 1305–1308 updating data, 313 XML data type and, 451 SQL Server 2000, 883–884 SQL Server 2005, 885–886 SQL Server 2008, 885–886 SQL Server Cache Dependency attaching cache dependencies to cache object, 890–893 attaching cache dependencies to request object, 890 configuring server for cache invalidation, 886–887 disabling databases for cache invalidation, 885 disabling tables for cache invalidation, 885 enabling databases for cache invalidation, 883 enabling tables for cache invalidation, 883–884, 890 overview of, 882 SQL Server 2000, 883–884 SQL Server 2005 and 2008 cache invalidation, 885– 886 testing cache invalidation, 887–890 SQL Server Express Edition adding users to data store, 600–601 personalization providers, 587–588 SQL Server Setup Wizard command-line tool for setting up providers to work with SQL Server, 461–463 GUI tool for setting up providers to work with SQL Server, 463–465 SQL statements parameterization of, 322–325

    TableAdapter wizard and, 346 SQL Stored Proc, 921–922 SQL*Plus, 330 SQL-backed session state aspnet_regsql.exe, 851–852 choosing best option for maintaining state, 856 options for state support, 852 overview of, 851–853 SqlSessionStateStore, 477–478 SqlCacheDependency object, 890–892 SqlCacheDependency property, 260 SqlCommand class, ADO.NET AsyncCallback class and, 354 asynchronous methods of, 352–353 IAsyncResult interface and, 353 properties of, 317–318 working with, 318–320 SqlConnection class, ADO.NET asynchronous connections, 370 overview of, 315–316 working with, 318–320 SqlDataAdapter class, ADO.NET, 320–322 filling DataTable with object from, 321–322 properties of, 320 SqlDataReader class, ADO.NET, 318–320 SqlDataSource control, 239–247 adding delete functionality to, 281–282 configuring data connection, 239–242 configuring data updates, 275–276 ConflictDetection property, 244–246 DataSourceMode property, 242 description of, 239 DetailsView control bound to, 284 enabling caching on, 259–260 events, 246 filtering using SelectParameters, 243–244 handling database errors, 246–247 InsertCommand, 287–288 overview of, 239 using AdRotator control with, 301 using with Oracle, 247 SqlMembershipProvider attributes, 613 building custom providers, 497–498 defining named instance of, 495 modifying behavior of, 492 ProviderBase and, 496–497 simplifying password structures with, 492–495 for storing details about registered users, 598 stronger password structures with, 495–496 System.Web.Security, 467–468

    1465

    www.it-ebooks.info

    SqlParameter class, ADO.NET – StyleSheetTheme attribute, page

    SqlParameter class, ADO.NET, 322–325 properties of, 322–323 using parameterized SQL statement, 323–325 SqlProfileProvider connection to SQL Server 2008, 589–590 overview of, 473–474 SqlRoleProvider creating LimitedSqlRoleProvider, 511–514 in System.Web.Security, 470–471 using LimitedSqlRoleProvider, 515–518 SQLServer providers for storing session state, 458 session state, 1248–1249 SqlSessionStateStore, 477–478 SqlWebEventProvider, 482–483, 1298, 1307, 1309 Src attribute, @Assembly directive, 18 SSDL (Store Schema Definition Language), 1107 SSL (Secure Sockets Layer) Basic authentication used in conjunction with, 812 connecting to remote servers using, 1343 StartFromCurrentNode property, SiteMapDataSource control, 553–554 StartingNodeOffset property, SiteMapDataSource control, 554 StartingNodeUrl property, SiteMapDataSource control, 554 state management Application object, 856–857 choosing best option for maintaining state, 856 configuring session state management, 839–840 ControlState, 863 cookieless session state, 855 cookies and, 857 hidden fields, 859–860 HttpContext.Current.Items for short-term storage, 863–864 methods for storing, 457 out-of-process session state, 846–851 overview of, 835 postbacks and cross-page postbacks and, 857–859 in process session state. See in-process (InProc) session state provider model. See provider model providers extending session state, 854 QueryStrings and, 857 Session object, 838 session state options, 835–837 sessions and event model, 838–839 SQL-backed session state, 851–853 summary, 864

    ViewState, 860–863 StateServer, 458, 1247–1248 static styles, Menu control, 547 status codes, Http, 924 StepType attribute, Wizard control, 143 Store Schema Definition Language (SSDL), 1107 stored procedures Entity Framework and, 1122–1125 inserting data with, 400–401 LINQ to SQL and, 398 selecting data from, 399 SQL Stored Proc, 921–922 Stream class moving file contents from Stream object to Byte array, 137–138 overview of, 953–954 placing uploaded files into stream object, 137 reading from NetworkStream, 958–959 using FileStream to read system file, 954–955 writing to MemoryStream, 957–958 StreamReader class encoding formats supported, 962 reading/writing files with, 960–961 streams compressing, 964 DeflateStream class, 964–965 GZipStream class, 965–966 overview of, 953 reading from NetworkStream, 958–959 stream classes, 954 using FileStream to read system file, 954–956 writing to MemoryStream, 957–958 strings filtering WCF data with, 1212 sorting strings in different cultures, 1227–1229 testing string date value with RangeValidator control, 169–171 styles. See also CSS (Cascading Style Sheets) applying to watermarks, 788–789 built-in to TreeView control, 530–531 bullet styles, 125 Dynamic Data, 1141 Login control, 616 modifying style and behavior of calendars, 116–119 predefined in Menu control, 546–547 static and dynamic in Menu control, 547 style properties in GridView control, 283 styling HTML with server controls, 998–1001 Wizard navigation, 144 StyleSheetTheme attribute, page directives, 221

    1466

    www.it-ebooks.info

    subpages – tables

    subpages master pages and, 189 nesting master pages and, 209–210 SupportsEvaluate, expression builder, 306 syntax notifications, at design-time, 896–897 system event log, 1288 System.Configuration classes, 1269 configuration handlers, 1279–1283 DpapiProtectedConfigurationProvider, 485–486 Provider namespace, 496 RsaProtectedConfigurationProvider, 486–487 System.Data, 314, 434 System.Data.Common, 314 System.Data.DataSet, 434–435 System.Data.Odbc, 315 System.Data.OleDb, 314 System.Data.SqlClient, 315 System.Data.SqlTypes, 315 System.Diagnostics.Trace, 899, 902–903 System.Globalization, 1219 System.IO. See also File I/O (input/output) BinaryReader and BinaryWriter, 961–962 Directory and DirectoryInfo classes, 931 DriveInfo class, 928 File and FileInfo classes, 937 namespace, 927 Path class, 943–946 reader and writer classes, 960 Stream class, 953–954 StreamReader, 960 System.IO.Compression DeflateStream class, 964–965 GZipStream class, 965–966 overview of, 964–965 System.IO.MemoryMappedFiles, 966–968 System.IO.Pipes, 970 System.IO.Ports, 968–969 System.Net FileWebRequest and FileWebResponse, 976–977 FtpWebRequest and FtpWebResponse, 974–976 HttpWebRequest and HttpWebResponse, 971–974 overview of, 970 System.Runtime.Caching, 879–881 System.UI.WebControls.WebParts, 672 System.Web, 1048–1049 System.Web.Caching.Cache, 873–874 System.Web.Configuration, 1269 System.Web.Mail, 977 System.Web.Management

    EventLogWebEventProvider, 479–480 SimpleMailWebEventProvider, 480–482 SqlWebEventProvider, 482–483 TemplatedMailWebEventProvider, 482 TraceWebEventProvider, 483–484 WmiWebEventProvider, 484–485 System.Web.Profile.SqlProfileProvider, 473–474 System.Web.Security ActiveDirectoryMembershipProvider, 468– 469 AuthorizationStoreRoleProvider, 472–473 SqlMembershipProvider, 467–468 SqlRoleProvider, 470–471 WindowsTokenRoleProvider, 471–472 System.Web.SessionState, 846 InProcSessionStateStore, 476–477 OutOfProcSessionStateStore, 477 SqlSessionStateStore, 477–478 System.Web.UI.Page, 1062, 1076 System.Web.UI.ScriptControl, 988 System.Web.UI.WebControls.WebControl, 988 System.Web.UI.WebControls.WebParts .SqlPersonalizationProvider, 487–489 System.Web.XmlSiteMapProvider, 474–475 System.Xml, 434

    T TabContainer server control, 801–802 Table menu, Visual Studio Design view, 702–703 Table per Type (TPT) models, in EDM, 1113 Table server control, 110–112 TableAdapter class, 346–348 TableAdapter Configuration Wizard, 346 TableCell object, 111 TableRow object, 111 tables disabling cache invalidation, 885 enabling cache invalidation, 883–884, 890 Entity Framework performing joins between, 1117–1119 opening WCF service to read data from available, 1199–1201 pulling customer table using EntityDataSource control, 1127–1128 reading specific item from WCF table, 1203–1205 reading table of WCF data, 1202–1203 removing specific tables from views, 1148 viewing tables enabled for cache invalidation, 884 working with Dynamic Data routes, 1147

    1467

    www.it-ebooks.info

    TabPanel server control – time-out settings

    TabPanel server control, 801 tags rendering HTML tag attributes, 996–997 rendering HTML tags with server controls, 994–996 Take method, paging using LINQ, 386–387 Task list (Visual Studio), 898 tasks, ADO.NET data deletion, 313–314 data insertion, 312 data selection, 310–312 data updates, 312–313 Telerik, 1393 templated controls, 1023–1027 adding to Web pages, 1026 creating container class of, 1024–1025 creating default template class for, 1027 creating inner control class of, 1023–1024 overview of, 1023 TemplatedMailWebEventProvider, 482, 1298, 1311–1312 TemplateField column, GridView control, 273–275 templates. See also master pages server control class, 990 Silverlight, 1399 Web user control file template, 980 templates, DataList control ItemTemplate, 331–334 list of available, 331 multiple columns, 336 styles, 335 working with other layout templates, 334–335 templates, ListView control creating EditItemTemplate, 341 creating EmptyItemTemplate, 341 creating InsertItemTemplate, 341–342 creating ItemTemplate, 340–341 creating layout templates, 338–340 list of layout templates, 337 overview of, 291 viewing defined layout elements, 342–344 testing cache invalidation, 887–890 text aligning text around check boxes, 104 programmatically providing text to Label control, 86–87 UpdatePanelProgress control displaying text message to client, 731–732 Text attribute, RequiredFieldValidator control, 162 Text property ValidationSummary control, 178 ViewSate and, 1013

    TextAlign property, CheckBox control, 104 TextBox control, 88–91 AutoCompleteType attribute, 90–91 AutoPostBack attribute, 89–90 extending with MaskedEditExtender control, 774–775 extending with NumericUpDownExtender control, 778 filtering text boxes with FilteredTextBoxExtender control, 772 Focus() method, 89 forms-based authentication and, 816 overview of, 89–90 PasswordStrength control and, 799–800 SliderExtender control and, 784–785 using calendar control from, 758 validating text-box value in e-mail address, 171–172 TextBoxWatermarkExtender control, 788–789 TextWriterTraceListener, 907 Theme attribute, adding to page directive, 218 themes applying to entire application, 219 applying to single ASP.NET page, 217–219 ASP.NET page without themes, 218 creating folder structure for, 222 creating skins, 222–224 disabling for custom controls, 232 disabling for properties in custom controls, 233–234 image included in, 227–229 including CSS files in, 224–227 master pages and, 221 multiple skin options, 229–231 overview of, 217 programmatically assigning page theme, 231 removing from server controls, 220 removing from web pages, 220–221 server controls and, 1001–1002 storing in App_Themes folder, 31 StyleSheetTheme attribute, 221 summary, 235 thick-clients, 709–710 thin-clients, 709–710 threads checking culture of ASP.NET thread, 1219–1220 cultures, 1220–1221 debugging multiple, 919 runtime settings for ASP.NET applications, 1265 3DES, 863, 1372 time values, in different cultures, 1223–1224 time-out settings, runtime settings for ASP.NET applications, 1264

    1468

    www.it-ebooks.info

    Timer control – UpdateProgress control

    Timer control AJAX server-side controls included in ASP.NET, 726–727 description of, 723 ToggleButtonExtender control, 789–790 tools, Ultimate Tools list, 1381 tooltips, 526 TPT (Table per Type) models, in EDM, 1113 TraceListeners, 904–908 configuring, 905 EventLogTraceListener, 906–907 other useful listeners, 907–908 overview of, 904 WebPageTraceListener, 905–906 tracepoints (breakpoints), 917 TraceSwitch, in diagnostics, 908–909 TraceWebEventProvider, 483–484, 1298 Trace.Write, 903–904 tracing application tracing, 1296–1297 from components, 902–904 diagnostic switches, 908–909 managing from Application tab of Web Site Administration Tool, 1326–1327 monitoring application execution, 898–899 page-level and application tracing, 899 sections and descriptions, 901 trace forwarding, 904 TraceListeners, 904–908 viewing trace data, 900–902 Web events, 483–484 TreeView control adding nodes programmatically, 543–545 binding to XML file, 532–534 built-in styles, 530–531 data binding and, 300 examining parts of, 531–532 expanding/collapsing nodes programmatically, 540–543 overview of, 527–530 selecting multiple options in, 534–537 ShowLines property of, 538–540 specifying custom icons in, 537–538 using images with themes, 227 working with programmatically, 540 triggers building, 729–730 using to cause asynchronous page postback, 729–730 trust levels .NET Trust Levels section of IIS Manager, 1332 web.config settings, 1241–1243

    try/catch error handling, 922 Twitter, online resources, 1428 two-way data binding, 302 Type converters, server controls and applying TypeConverter attribute, 1028 creating custom, 1028–1031 design-time behaviors of server controls and, 1027 Type property, 167 Type selectors, CSS, 688 TypeName attribute, page directives, 18

    U UI Type editor added to control property, 1042 design-time behaviors of server controls and, 1027
      (unordered list) element, 124 Ultimate Tools list, 1381 code management, 1388–1390 debugging tools, 1381–1383 developer tools, 1383–1385, 1393–1397 extenders for ASP.NET, 1390–1393 jQuery/jQuery UI, 1385 profilers, 1386 reference sites, 1386–1387 summary, 1393–1397 UNICODE, 962 Uniform Resource Locators. See URLs (Uniform Resource Locators) unique identifiers, 582 UniqueID, server controls, 998 Universal Resource Identifiers (URIs), 407 Universal selectors, CSS, 688 unmanaged code, using .NET from, 1095 UpdateCommand, configuring SqlDataSource for data updates, 275–276 UpdatePanel control AJAX server-side controls included in ASP.NET, 727 element, 727–728 description of, 723 element, 728–730 using multiple UpdatePanel controls, 733–736 UpdatePanelAnimationExtender control, 790–791 UpdateParameters, configuring SqlDataSource for data updates, 276 UpdateProgress control adding image to element, 733 AJAX server-side controls included in ASP.NET, 731 controlling when message appears, 733 description of, 723 displaying text message to client, 731–733

      1469

      www.it-ebooks.info

      updating data – validation controls

      updating data ADO.NET tasks, 312–313 configuring GridView for data updates, 276–278 configuring SqlDataSource for data updates, 275–276 DetailsView control, 287–288 error handling, 278–279 LINQ to SQL, 401 upgrading ASP.NET applications, 1370–1371 ASP.NET reserved folders, 1372–1373 uploading files. See FileUpload control URIs (Universal Resource Identifiers), 407 URL authorization applied to specific files and folders, 822 configuring, 1258–1259 URL mapping, 557–558 URL Rewrite Module, IIS7, 1393 URL rewriting, 1069 URLs (Uniform Resource Locators) fully qualified redirect URLs, 1264 guidelines for high-quality URLs, 1069 installation URL for deployment project, 1358 QueryStrings, 857 relative CSS links in master pages and, 706 rewriters, 1392–1393 session ID in, 855 StartingNodeUrl property of SiteMapDataSource control, 554 working with Dynamic Data routes, 1144–1147 XML namespace and, 407 user action, confirming with ConfirmButtonExtender control, 761–762 user controls casting to native type, 985 communicating between two instances of same control on same page, 863 creating, 980–982 defined, 979 vs. include files, 189 interacting with, 982–983 loading dynamically, 983–987 overview of, 980 Pages and Controls section of IIS Manager, 1334 partial page (user control) caching, 869 User Interface Editor, Visual Studio, 1364–1365 user login, XmlMembershipProvider, 510–511 User Tasks view, Visual Studio, 898 User.Identity property, in authentication, 823 User.IsInRole method, in authentication, 824 usernames adding username values to web.config, 816–817

      displaying name of logged-in user, 823 displaying names of authenticated users, 621–622 storing in registry and referencing settings in machine.config, 1274–1275 storing in XML user data store, 502 users adding programmatically, 609–612 adding to membership service, 600–601 adding users to roles, 635 authenticating and authorizing, 809–810 changing how users register with applications, 612–613 checking authentication status of, 823 checking user roles, 824 checking users in roles, 638–639 creating from Security tab of Web Site Administration Tool, 1322 creating in Windows-based authentication, 807–808 displaying username of authenticated user, 621–622 getting all roles of a particular user, 637–638 getting all users of a particular role, 635–637 managing from Security tab of Web Site Administration Tool, 1323 .NET Users section of IIS Manager, 1332–1333 Rating server control used by, 800–801 removing users from roles, 638 showing number of users online, 622–623 viewing where users are stored, 603–604 Using statements, 955–956 UTC (coordinated universal time), 113–114 UTF-8, 962

      V valid XML documents, 409 ValidateUser() method, Membership API, 507–508, 617 validation adding asynchronous callback for data validation, 1007–1009 causing, 160 client-side scripts for, 1003–1005 client-side vs. server-side, 158–159 creating validation callouts, 792 understanding validation, 157–158 ValidateUser() of XmlMembershipProvider class, 507–508 XDocument against XML Schema, 417–419 XmlReader against XML Schema, 416–417 Validation and More (VAM), 159 validation controls

      1470

      www.it-ebooks.info

      ValidationGroup property – Visual Studio 2010

      CompareValidator, 164–167 CustomValidator, 172–176 overview of, 157, 159–160 RangeValidator, 167–171 RegularExpressionValidator, 171–172 RequiredFieldValidator, 160–164 summary, 185 turning off client-side validation, 179–180 using images and sounds for error notifications, 180–181 ValidationSummary, 176–179 working with validation groups, 181–185 ValidationGroup property, 182–183 ValidationSummary control, 176–179 description of, 160 dialog box used for reporting validation errors, 179 overview of, 176 partial page example, 177 Text property of validation control, 178 ValidatorCalloutExtender control, 791–792 value-required rules, 160 VAM (Validation and More), 159 var keyword, for implicit types, 1420 VaryByControl attribute, output caching, 867 VaryByCustom attribute, output caching, 867–868 VaryByParam attribute, output caching, 866 VB (Visual Basic) converting to/from C#, 1393–1394 Edit and Continue feature, 916 error handling, 1091 smart tags, 897 syntax notifications, 896 verbs, HTTP, 1202 verbs, Web Parts denying users and roles by, 1259 graying out close link in Web Parts, 664–665 list of, 663–664 removing close link in Web Parts, 665 using images for Web Part verbs, 665–666 View in ASP.NET MVC, 1060 in MVC pattern, 1059 view, querying data using a, 397–398 View control, 138–141 ViewDataDictionary, 1076 Views, ASP.NET MVC defined, 1060 overview of, 1076 specifying, 1076–1077 strongly typed, 1077–1078 ViewSate

      data types and, 1014 storing ViewState information in hidden form field, 1012–1013 Text property’s use of, 1013 ViewState client-side state management options, 837 methods for storing state, 458 state management, 860–863 ViewStateDecoder tool, 862 VirtualPath attribute, page directives, 18–19 Visibone, 1387 Visual Basic. See VB (Visual Basic) Visual Studio 2010 ADO.NET Entity Framework and, 1106 application location options, 1–2 ASP.NET AJAX and, 712–713 ASP.NET Web Package, 1337 ASP.NET Web Site Administration Tool accessed via, 1316 building WCF services with, 1179–1180 building XML Web service with, 1155–1156 client-side JavaScript debugging, 920–921 connection to data source using, 344–345 converting ASP.NET 1.x application in, 1375–1378 Copy Web Site utility, 1341–1344 creating ASP.NET MVC application, 1062–1064 creating base application for working with Dynamic Data, 1129–1130 creating class skeleton for XmlMembershipProvider, 499–501 creating master pages in, 191–192 Custom Actions Editor and, 1366 data visualizers, 915 DataTips feature, 914–915 debug and release configurations in, 910–911 editing configuration files, 1278–1279 editing XML with, 410–413 error notifications, 915 event logs, 1285 File System Editor, 1360–1362 File Type Editor, 1363–1364 IntelliTrace, 917–919 Launch Conditions Editor, 1366–1367 Publish Web dialog, 1347 Registry Editor, 1363 resource editor in, 1237 Silverlight templates, 1399 Solution Explorer. See Solution Explorer Threads dialog, 919 triggers, 729–730 User Interface Editor, 1364–1365

      1471

      www.it-ebooks.info

      Visual Studio 2010 – Web pages

      Visual Studio 2010 (continued) using for ADO.NET tasks, 344 working with classes through, 44–47 XSLT Debugger Data Breakpoints, 445 Visual Studio 2010, working with CSS Apply Styles tool window, 705 CSS Properties tool window, 706 Manage Styles tool window, 704–705 overview of, 702–704 Visual Studio Installer (VSI), 1350 visually removing items from a collection, 98–99 VSI (Visual Studio Installer), 1350

      W wait approach, to asynchronous processing multiple wait handles used in conjunction with MARS, 360–363 overview of, 357–360 WaitAny method for multiple asynchronous process, 364–367 WaitAny method, 364–367 WaitHandle class multiple wait handles in asynchronous processing, 360–367 overview of, 354–355 watermarks, applying styles to, 788–789 WCF (Windows Communication Foundation) ABCs of WCF service, 1180–1181 consumer application. See WCF consumer application creating WCF service framework, 1181–1182 creating WCF service interface, 1182–1183 Data Services .NET framework. See WCF Data Services framework hosting WCF service in console application, 1184 implementing WCF service interface, 1183–1184 overview of, 1178–1179 SOA and, 1178–1179 WSDL file for, 1185–1186 WCF consumer application adding service reference to WCF service, 1187–1189 building WCF service with a data contract, 1190–1192 defining namespaces, 1193–1194 overview of, 1192–1193 WCF Data Services framework adding EDM for, 1195–1196 changing service to work with EDM, 1198–1199 consuming in ASP.NET, 1213–1215 creating first service, 1201

      creating service, 1196–1198 expanding on associations, 1207–1210 filtering content, 1211–1212 moving around in result sets, 1211 opening service to read data from available tables, 1199–1201 ordering result sets, 1210 overview of, 1194 querying, 1201–1202 reading specific item from table, 1203–1205 reading table of data, 1202–1203 working with relationships, 1205–1207 Web Control Libraries, adding to Web pages, 989–990 Web Deployment Tool (MSDeploy), 1346–1349 Web Developer Toolbar, Firefox, 1384 web event providers EventLogWebEventProvider, 479–480 overview of, 478–479 SimpleMailWebEventProvider, 480–482 SqlWebEventProvider, 482–483 TemplatedMailWebEventProvider, 482 TraceWebEventProvider, 483–484 WmiWebEventProvider, 484–485 Web events application monitoring and health monitoring tools, 909 buffering, 1308–1310 creating custom, 909–910 e-mail events, 480–482, 1310–1314 health monitoring and, 1297 logging, 479–480 overview of, 478 routing to SQL Server, 1305–1308 tracing, 483–484 WMI events, 484–485 writing events via configuration, 1305 Web farms configuring session state for, 1247–1249 processor affinity and, 840 ViewState and, 862 Web forms ASP.NET MVC compared with, 1061–1062 check boxes on, 102 release of .NET platform and, 1060 Web gardening, 840–841 Web pages. See also pages, ASP.NET analyzing with YSlow, 1382–1383 external style sheets in, 686 history of, 1060 include files added to, 1006

      1472

      www.it-ebooks.info

      Web Parts – web.config

      inline styles in, 687 internal style sheets in, 686–687 sample Web page using HttpHandler as image source, 1056 sending e-mail from, 977 using HttpWebRequest to post data to remote pages, 973 Web Control Libraries added to, 989–990 working with page history in ASP.NET AJAX, 737–741 Web Parts adding to pages, 654–656 allowing users to change page mode, 651–654 building consumer Web Parts, 677–679 building provider Web Parts, 675–677 connecting, 674–675 connecting on ASP.NET page, 679–681 creating custom, 669–674 DeclarativeCatalogPart combined with PageCatalogPart, 662–663 DeclarativeCatalogPart control, 662 default control elements, 650 graying out close link in, 664–665 list of Web Part verbs, 663–664 making connections between, 660 master pages and, 681–682 modifying settings, 657–660 moving, 656–657 overview of, 643–645 removing close link in, 665 summary, 681–682 using images for Web Part verbs, 665–666 WebPartManager control, 645–646 WebPartZone control, 649–650 zone layouts with, 646–649 Web Parts provider, 487–489 Web reference, adding for XML Web service, 1163–1164 Web server controls. See also server controls AdRotator control, 119–121 BulletedList control, 124–128 Button control, 91–94 Calendar control, 112–119 Chart control, 152–155 CheckBox control, 102–104 CheckBoxList control, 104–106 DropDownList control, 96–98 FileUpload control, 130–138 HiddenField control, 129–130 HyperLink control, 96 Image control, 109–110

      ImageButton control, 95 ImageMap control, 150–152 Label control, 86–88 LinkButton control, 94 ListBox control, 100–102 Literal control, 88 MultiView control, 138–141 overview of, 85–86 Panel control, 122–123 PlaceHolder control, 124 RadioButton control, 106–108 RadioButtonList control, 108–109 summary, 155 Table control, 110–112 TextBox control, 88–91 View control, 138–141 visually removing items from a collection, 98–99 Wizard control, 141–149 Xml control, 121 Web servers built-in with Visual Studio 2010, 2–3 Remote Site option, 4 using FTP, 3 using IIS, 3 Web Service Enhancement (WSE), 1178 Web services. See also WCF Data Services framework; XML Web services App_WebReferences and, 31 communicating between disparate systems, 1153–1155 overview of, 1153 Web Services Description Language. See WSDL (Web Services Description Language) Web Setup Project, 1351 Web Site Administration Tool editing configuration files, 1278–1279 performing membership management functions with, 640 Web sites building modular with Web Parts, 643–644 changing name of, 1330 Copy Web Site tool in Visual Studio, 1341–1344 list of online resource sites, 1427–1428 navigating. See site navigation running multiple sites with multiple versions of .NET Framework, 1267–1268 setting up for membership, 599–600 web.config. See also configuration files adding HttpHandlers configuration to, 1057–1058 adding HttpModules configuration to, 1051–1052 adding username/password values to, 816–817 application configuration file, 1243

      1473

      www.it-ebooks.info

      web.config – Windows Installer

      web.config (continued) applying configuration settings, 1243–1244 applying themes to entire application, 219 element of, 1325 authenticating against values in, 816–818 authenticating and authorizing users, 809–810 element added to, 599 node of, 806–807 caching settings in, 874 changing attribute values in, 494 changing file-size restrictions in, 134 changing password structures, 495–496 element, 1250–1251 configuring cache invalidation, 886–887 configuring for role management service, 632 connection strings in, 316–317 creating personalization groups in, 575 creating personalization properties in, 571, 605 culture detection added to, 559–560 decrypting element, 1278 defining InProc mode for session state management in, 476–477 defining SqlSessionStateStore in, 477–478 defining XmlMembershipProvider in, 502–503 deleting configuration settings, 1244 encrypting element, 1277–1278 encrypting element, 1276 enumerating connection strings in, 1270–1271 element, 1299–1301 element added to, 599–600 forms-based authentication and, 813–816 element, 1221–1222 GUI tool for editing, 832 IIS Manager and, 1330 @Import directive and, 15–17 include files supported by, 1262 making reference to build provider in, 40 manipulating from remote servers, 1273–1274 element, 1304 provider definitions in, 492 element, 1301–1302 element, 1302–1304 server configuration files, 1240–1241 sessionState element, 839 SMTP settings, 1311 specifying master page in, 198 storing application-specific configuration settings, 1268 storing connection information in, 260 structuring so classes in App_Code folder can use different languages, 30 tracing settings added to, 899

      trust levels, 1241–1243 turning off debugging prior to deploying applications, 1338–1339 URL authorization, 822 Web Parts Behavior section in, 659–660 XML-based configuration file, 1315 WebControl class, 988 WebMethod attribute, 1159–1160 WebPageTraceListener, 905–906 WebPart class creating custom Web Parts, 669–674 properties and methods, 668–669 WebPartManager class, 667 WebPartManager control adding to ASP.NET page, 645–646 allowing users to change page mode, 651–654 working with zone layouts, 646–649 WebPartZone class, 668 WebPartZone control LayoutOrientation attribute, 649 overview of, 649 turning off capability for zone modifications, 660–661 using images from theme folder in, 227–228 ZoneTemplate element, 649–650 WebRequest/WebResponse FileWebRequest and FileWebResponse, 976–977 FtpWebRequest and FtpWebResponse, 974–976 HttpWebRequest and HttpWebResponse, 971–974 WebService attribute, 1159 WebSevice page directive, 1156 Web.sitemap file sitemap localization and, 558–559 using with SiteMapPath control, 522–523 week, selecting from calendar, 114 where parameters filtering LINQ queries using, 250 query filters in LINQ, 382–384 WinDiff, 1394 windowless parameter, Silverlight 3, 1404–1406 Windows 7, 1329 Windows CAPI (Cryptographic API), 487 Windows DPAPI (Data Protection API). See DPAPI (Data Protection API) Windows Forms, 710 Windows Installer actions of, 1350–1351 creating basic installation program, 1351–1354 installing applications, 1354–1355 overview of, 1349–1350 uninstalling application, 1355–1357 working with deployment project properties, 1357–1360

      1474

      www.it-ebooks.info

      Windows Management Instrumentation (WMI) – XML (eXtensible Markup Language)

      Windows Management Instrumentation (WMI), 484–485 Windows NT Challenge/Response authentication, 812 Windows SharePoint Services (WSS), 644 Windows XP ASP.NET MMC snap-in, 1317 debugging and, 914 Windows-based authentication and nodes, 810 authenticating and authorizing groups, 810–811 authenticating and authorizing HTTP transmission method, 811 authenticating and authorizing users, 809–810 Basic authentication, 812–813 configuring, 1255 creating users, 807–808 Digest authentication, 813 Integrated Windows authentication, 811–812 overview of, 807 WindowsTokenRoleProvider, 471 WindowsIdentity object, 824–826 WindowsTokenRoleProvider, 471–472 WinMerge, 1394–1395 Wizard control, 141–149 adding header to, 143–144 AllowReturn attribute, 143 customizing site navigation, 142 overview of, 141–142 showing form elements with, 146–149 StepType attribute, 143 utilizing wizard control events, 145–146 working with wizard’s navigation system, 144–145 WMI (Windows Management Instrumentation), 484–485 WmiWebEventProvider, 484–485, 1298 worker processes, configuring, 1265–1268 Writer class, 960 Wrox.master file, 195 WSDL (Web Services Description Language) for HelloCustomer service, 1193 imported types in, 1189 linking to Web service dataset, 1160 reviewing WSDL file of a Web service, 1185–1186 WSE (Web Service Enhancement), 1178 WSS (Windows SharePoint Services), 644 www.asp.net, Reference Web site, 1387 WYSIWYG, in Visual Studio 2010 design surface, 702

      X XAML, 1411 XAP files, 1400

      XCOPY deploying applications with, 1339 parameters of XCOPY command, 1340–1341 syntax of, 1340 XDocument processing XML with, 415–416 validating against XML Schema, 417–419 XHTML-compliant pages, 1373–1375 XLINQ. See LINQ to XML XML (eXtensible Markup Language) adding column of typed XML with SQL Server, 455 adding column of untyped XML with SQL Server, 452–453 adding XML Schema with SQL Server, 453–455 advertisement file, 120–121 binding XML data to bulleted list, 127–128 bridging XmlSerializer and LINQ to XML, 428–429 creating CLR objects with LINQ to XML, 423–424 creating XML with LINQ to XML, 426–428 creating XML with XmlWriter, 424–426 data binding, 303 databases and, 446 DataSet class based on, 328 DataSets, 434 DOM problems and, 429–430 editing in Visual Studio, 410–413 generating custom XML from SQL 2008, 451–452 history of, 1154 improvements to XmlReader and XmlWriter, 429 InfoSet, 408–409 inserting XML data into XML column with SQL Server, 455–456 movie data as XML data file, 387 NameTable for optimizing XmlReader, 419–420 overview of, 405–408 persisting DataSets to XML, 434–435 querying. See LINQ to XML ReadSubtree and XmlSerializer, 422–423 retrieving .NET CLR types from, 420–422 SQL Server and XML data type, 451 summary, 455–456 validating XDocument against XML Schema, 417–419 validating XmlReader against XML Schema, 416–417 XDocument, 415–416 FOR XML AUTO clause, 446–450 XML configuration file vs. binary metabase, 1240 XML-based sitemaps, 520–521 XmlDataDocument, 435–437

      1475

      www.it-ebooks.info

      XML (eXtensible Markup Language)e – XPathNodeIterator

      XML (eXtensible Markup Language) (continued) XmlDataSource control, 437–441 XmlReader and XmlWriter, 413–415 XPath, XPathDocument, and XmlDocument, 430–433 XSD (XML Schema Definition), 409–410 XSLT, 441–442 XSLT debugging, 445–446 XslTransform class, 442–445 XML data type, 451 XML documents displaying, 121 syntax vs. semantics, 408–409 XSD (XML Schema Definition), 409–410 XML files binding Menu control to, 551–552 binding TreeView control to, 532–534 XML Schema Definition. See XSD (XML Schema Definition) XML Schemas adding with SQL Server, 453–455 associating XML typed column with, 455 editing in Visual Studio, 410–413 XmlDocument validated against, 417–419 XmlReader validated against, 416–417 XSD (XML Schema Definition), 409–410 Xml server control, 121 XML Web services adding Web reference for, 1163–1164 base class file for, 1156–1157 building using SOAP headers, 1170–1172 caching responses, 1169–1170 consuming, 1162–1163 consuming asynchronously, 1175–1177 consuming using SOAP headers, 1172–1173 exposing custom datasets as SOAP, 1157–1158 interface for, 1160–1162 invoking from client applications, 1164–1166 overloading Web methods, 1166–1169 overview of, 1155–1156 requesting using SOAP 1.2, 1174–1175 WCF and. See WCF (Windows Communication Foundation) WebMethod attribute, 1159–1160 WebService attribute, 1159 WebSevice page directive, 1156 XmlDataDocument, 435–437 XmlDataSource control, 437–441 XmlDataSource control consuming RSS feed, 255 description of, 239

      dynamically populating bulleted list, 128 overview of, 255–256 XmlDocument overview of, 430 querying/editing XML using XmlDocument and XpathNodeIterator, 432 XmlReader compared with, 413 XMLHttpRequest object AJAX dependence on, 711–712 building applications using, 709 XmlMembershipProvider creating class skeleton for, 499–501 creating custom providers, 498 creating data store for, 502 defining in web.config, 502–503 implementing methods and properties of, 504 not implementing methods and properties of, 503–504 ReadUserFile() method, 508–510 user login using, 510–511 ValidateUser() method, 507–508 XmlReader improvements to, 429 NameTable for optimizing, 419–420 processing XML with, 413–415 retrieving CLR types from, 420–422 validating against XML Schema, 416–417 XML serialization and ReadSubtree and, 422–423 XmlSerializer bridging XmlSerializer and LINQ to XML, 428–429 ReadSubtree and, 422–423 XmlSiteMapProvider, 474–475 XmlWriter creating XML with, 424–426 improvements to, 429 XmlWriterTraceListener, 907 XPath query language, 430 querying XDocuments, 433 querying XML using XPathDocument and XpathNodeIterator, 431 querying/editing XML using XmlDocument and XpathNodeIterator, 432 XPathDocument overview of, 430–431 querying XML using XPathDocument and XpathNodeIterator, 431 XSLT and, 442 XPathNodeIterator querying XML using XpathDocument and XpathNodeIterator, 432

      1476

      www.it-ebooks.info

      XSD (XML Schema Definition) – zones

      querying/editing XML using XmlDocument and XpathNodeIterator, 432 XSD (XML Schema Definition) defining required types in, 1190 for HelloCustomer service, 1193 overview of, 409–410 XSD editor, in Visual Studio, 411 XslCompiledTransform class, 443, 445 XSLT command-line compiler for, 443–445 debugging, 445–446 overview of, 441–442 XslTransform class, 442–443 XSLT Debugger Data Breakpoints, Visual Studio 2010, 445 XSLTC.exe, 443–445 XslTransform class, 442–443

      Y Yahoo!, 1382 YSlow, from Yahoo!, 1382

      Z .zip files building ASP.NET package and, 1346 XAP files as, 1400 zones creating multiple, 646–649 layouts, 646 moving Web Parts between, 656–657 Portal Framework, 645 turning off capability for zone modifications, 660–661

      1477

      www.it-ebooks.info

      Related Wrox Books Beginning ASP.NET 4: in C# and VB

      Programmer to Programmer™

      ISBN: 978-0-470-50221-1 This introductory book offers helpful examples in a step-by-step format and has code examples written in both C# and Visual Basic. With this book you will gradually build a web site example that takes you through the processes of building basic ASP.NET web pages, adding features with pre-built server controls, designing consistent pages, displaying data, and more.

      Beginning Microsoft Visual C# 2010

      Get more out of wrox.com Interact

      Join the Community

      Take an active role online by participating in our P2P forums @ p2p.wrox.com

      Sign up for our free monthly newsletter at newsletter.wrox.com

      Wrox Online Library

      Browse

      Hundreds of our books are available online through Books24x7.com

      Ready for more Wrox? We have books and e-books available on .NET, SQL Server, Java, XML, Visual Basic, C#/ C++, and much more!

      Wrox Blox Download short informational pieces and code to keep you up to date and out of trouble!

      ISBN: 978-0-470-50226-6 Using this book, you will first cover the fundamentals such as variables, flow control, and object-oriented programming and gradually build your skills for web and Windows programming, Windows forms, and data access. Step-by-step directions walk you through processes and invite you to “Try it Out” at every stage. By the end, you’ll be able to write useful programming code following the steps you’ve learned in this thorough, practical book. If you’ve always wanted to master Visual C# programming, this book is the perfect one-stop resource.

      Professional Visual Basic 2010 and .NET 4 ISBN: 978-0-470-50224-2 If you’ve already covered the basics and want to dive deep into VB and .NET topics that professional programmers use most, this is your guide. You’ll explore all the new features of Visual Basic 2010 as well as all the essential functions that you need, including .NET features such as LINQ to SQL, LINQ to XML, WCF, and more. Plus, you’ll examine exception handling and debugging, Visual Studio features, and ASP.NET web programming.

      Professional C# 4 and .NET 4 ISBN: 978-0-470-50225-9 After a quick refresher on C# basics, the author dream team moves on to provide you with details of language and framework features including LINQ, LINQ to SQL, LINQ to XML, WCF, WPF, Workflow, and Generics. Coverage also spans ASP.NET programming with C#, working in Visual Studio 2010 with C#, and more. With this book, you’ll quickly get up to date on all the newest capabilities of C# 4.

      Visual Studio 2010 and .NET 4 Six-in-One ISBN: 978-0-470-49948-1 This comprehensive resource offers all you need to know to become productive with .NET 4. Experienced author and .NET guru Mitchel Sellers reviews all the important features of .NET 4, including .NET charting and ASP.NET charting, ASP.NET dynamic data and jQuery, and F#. The coverage is divided into six distinctive parts for easy navigation and offers a practical approach and complete examples.

      Professional Visual Studio 2010 ISBN: 978-0-470-54865-3 Written by an author team of veteran programmers and developers, this book gets you quickly up to speed on what you can expect from Visual Studio 2010. Packed with helpful examples, this comprehensive guide examines the features of Visual Studio 2010 and walks you through every facet of the Integrated Development Environment (IDE), from common tasks and functions to its powerful tools.

      WPF Programmer’s Reference: Windows Presentation Foundation with C# 2010 and .NET 4 ISBN: 978-0-470-47722-9 Written by a leading expert on Microsoft graphics programming, this richly illustrated book serves as an introduction to WPF development and explains fundamental WPF concepts. It is packed with helpful examples and progresses through a range of topics that gradually increase in their complexity.

      Contact Us. We always like to get feedback from our readers. Have a book idea? Need community support? Let us know by e-mailing [email protected]

      Visual Basic 2010 Programmer’s Reference ISBN: 978-0-470-49983-2 Visual Basic 2010 Programmer’s Reference is a language tutorial and a reference guide to the 2010 release of Visual Basic. The tutorial provides basic material suitable for beginners but also includes in-depth content for more advanced developers.

      www.it-ebooks.info

      Prepared for TOM ZASKI/ email0 [email protected] Order number0 57462538 This PDF is for the purchaser’s personal use in accordance with the Wrox Terms of Service and under US copyright as stated on this book’s copyright page. If you did not purchase this copy/ please visit www.wrox.com to purchase your own copy.

      Evjen, Hanselman, Rader

      Take your web development to the next level using ASP.NET 4

      Professional ASP.NET 4: • Demonstrates ASP.NET built-in systems such as the membership and role management systems • Covers everything you need to know about working with and manipulating data • Discusses the plethora of server controls that are at your disposal • Explores new ways to build ASP.NET, such as working with ASP.NET MVC and ASP.NET AJAX • Examines the full life cycle of ASP.NET, including debugging and error handling, HTTP modules, the provider model, and more • Features both printed and downloadable C# and VB code examples

      is one of the most active proponents of .NET technologies. He is the H founder of the International .NET Association (INETA), author or coauthor of more than two dozen books, and Global Head of Platform Architecture at Thomson Reuters, Lipper.

      Scott Hanselman is a pr principal program manager prog lead working in the th Server and Tools Online Division at Microsoft. He has a popular blog p and d weekly podcast at www.computerzen.com and speaks worldwide on ASP.NET.

      De Devin Ra Rader

      wrox.com w P Programmer Forums Join our Programmer to Programmer forums tto ask and answer programming questions about this book, join discussions on the hottest topics in the industry, and connect d with fellow programmers from around the world. Code Downloads Take advantage of free code samples from this book, as well as code samples from hundreds of other books, all ready to use. Read More Find articles, ebooks, sample chapters, and tables of contents for hundreds of books, and more reference resources on programming topics that matter to you. Wrox Professional guides are planned and written by working programmers to meet the real-world needs of programmers, developers, and IT professionals. Focused and relevant, they address the issues technology professionals face every day. They provide examples, practical solutions, and expert education in new technologies, all designed to help programmers do a better job.

      Devin Rader D works at Infragistics where he focuses on delivering d great experiences to developers using their controls. He’s also a former INETA board member.

      $59.99 USA $71.99 CAN www.it-ebooks.info

      Web Development / ASP.NET

      Prepared for TOM ZASKI/ email0 [email protected] Order number0 57462538 This PDF is for the purchaser’s personal use in accordance with the Wrox Terms of Service and under US copyright as stated on this book’s copyright page. If you did not purchase this copy/ please visit www.wrox.com to purchase your own copy.

      Join the discussion @ p2p.wrox.com

      Wrox Programmer to Programmer™

      Professional

      ASP.NET 4

      ASP.NET is about making you as productive as possible when building fast and secure web applications. Each release of ASP.NET gets better and removes a lot of the tedious code that you previously needed to put in place, making common ASP.NET tasks easier. With this book, an unparalleled team of authors walks you through the full breadth of ASP.NET and the new and exciting capabilities of ASP. NET 4. The authors also show you how to maximize the abundance of features that ASP.NET offers to make your development process smoother and more efficient.

      Bill Bill Evjen Ev

      in C# and VB

      Professional

      ASP.NET 4 in C# and VB

      Bill Evjen, Scott Hanselman, Devin Rader