Deployment

This chapter is excerpted from Programming ASP.NET 3.5, Fourth Edition by Jesse Liberty, Dan Maharry, Dan Hurwitz, published by O'Reilly Media

Programming ASP.NET 3.5, Fourth Edition

Logo

Buy Now

Perhaps one of the most useful features in Visual Studio that goes mostly unheralded is the ease with which you can deploy ASP.NET websites. Indeed, with two types of Visual Studio projects for a website-a Web Site Project and a Web Application Project-there are now even more options than before with respect to deployment. ASP.NET derives all of this deployment bliss by virtue of being part of the .NET Framework and being implemented on Visual Studio. This means that:

  • If you use Web Site Projects, source code files need only be located in a specific directory (App_Code) to be automatically compiled and available.

  • Precompiled .dll files only have to be located in a specific directory (bin) to be visible to an application.

  • You don't need to register objects, either in the Registry or elsewhere, for an application to use the contents of a .dll. Installation does not require any registering of components with regsrvr32 or any other utility, though globally available components may be placed in the Global Assembly Cache.

  • Websites can be deployed with installer files (.msi) or by using XCOPY or a Web Deployment Project. You can also use Visual Studio 2008 (VS2008) to deploy your website with a single click of the Publish button.

  • Websites can be updated without stopping the web server or the application.

  • There are no versioning issues with conflicting .dll files.

  • Web Application Projects can be integrated into automated builds and given pre- and post-compilation tasks.

Understand that with all the different deployment scenarios, the fundamental requirement is that you are deploying your site to a web server (or to servers in the case of a web farm) which is running Internet Information Services (IIS), as described in Chapter 18, Application Logic and Configuration. Even if you developed the application in VS2008 using a filesystem location on a machine without IIS installed, you must deploy to a virtual directory on a server running IIS if you want your site to be visible to other users.

Warning

A warning for Vista Standard users: at the time of this writing, building Web Setup and Web Deployment projects required Visual Studio to be running with administrative privileges to succeed. Likewise, using XCOPY to deploy files to folders that are not owned by your account will also see the User Account Control (UAC) dialog pop up to get admin permission for your actions.

Assemblies

An assembly is the .NET unit of versioning and deploying code modules. An assembly consists of Portable Executable (PE) files. PE files can be either dynamic link library (.dll) files or .exe files. These PE files are in the same basic format as normal Windows PE files but with a slight difference; they use the PE header, but the contents are in Microsoft Intermediate Language (MSIL) rather than machine binaries.

Assemblies contain versioning information. An assembly is the minimum unit for a single version of a piece of code. Multiple versions of the same code can run side by side in different applications, with no conflicts, by packaging the different versions into separate assemblies and specifying in the configuration files which version is current.

Assemblies are self-describing because they contain metadata that fully describes the assembly and the classes, methods, and types the assemblies contain. One of the files in the assembly contains a manifest as part of the metadata, which details exactly what is in the assembly. This includes identification information (e.g., name, version), a list of the types and resources in the assembly, a map to connect public types with the implementing code, and a list of other assemblies referenced by this assembly.

Sites created in both Web Site Projects and Web Application Projects consist of all the files and resources in their virtual root directory and its subdirectories. One standard subdirectory is the bin directory. Any assemblies placed in this directory are considered private assemblies and are automatically made available to the application.

Another standard subdirectory within Web Site Projects only is App_Code. Any source code placed in this folder is automatically compiled at runtime, and the resultant private assembly is copied to the Temporary ASP.NET Files directory on the server and made available to the site.

Any classes generated and compiled automatically by ASP.NET before the site is up and running (such as the profile class mentioned in Chapter 14, Personalization) are also saved to the Temporary ASP.NET Files directory (henceforth referred to as TempAspFiles). Whether in the site's bin directory or the server's TempAspFiles directory, the physical location of the assembly files for the site is unimportant because the CLR handles all aspects of managing these assemblies. It just works.

If an assembly file is placed in either location, all the classes contained in that assembly are automatically available to the application. No developer or user action is required for this to occur. Any class, method, or type defined in these folders or the Global Assembly Cache (described later in this chapter) is available to the rest of the application, subject to the access modifiers in Table 20.1, "VB.NET and C# access modifiers".

Table 20.1. VB.NET and C# access modifiers

C#

VB.NET

public

Public

protected

Protected

private

Private

internal

Friend

internal protected

Protected Friend

Tip

ASP.NET is configured to prohibit web access to the bin and App_Code subdirectories. This prevents web users from examining or tampering with your source code or assemblies. Likewise, the TempAspFiles directory is not available through any website.

When the CLR loads an assembly from its disk file (a DLL) into memory, the file is not locked. This allows a new version of the file to be copied in at any time. When that happens, any web requests using the old version at that moment will run to completion on the old version. New requests will use the new version, even if the new requests come in while an old request is still running. Eventually, all requests that were using the old version will finish, and the old version of the assembly will be "unloaded"-the CLR will reclaim its memory.

The CLR constantly monitors the bin and TempAspFiles folders and the Global Assembly Cache to see whether any new assemblies have been added, or any of the existing assemblies have changed. If a new or updated source code file in App_Code is detected, and there is a new web request for something in that file, it will be recompiled and the resultant assembly file (a DLL) will be copied to TempAspFiles. If a new or updated assembly file is detected, the classes it contains are automatically available to the application. In either case, all pending requests to the old version of the assembly are allowed to complete, but all new requests are handled by the new version. When the last request to the old version is finished, the copy of that version in memory is allowed to expire and the transition is complete.

There are two broad categories of deployment: local and global:

  • With local deployment, the entire application is self-contained within a virtual directory. All of the content and assemblies (described shortly) that make up the application are contained within the virtual directory and are available to this single application.

  • With global deployment, assemblies are made available to every application running on the server.

Within the category of local deployment, several scenarios are available for the preparation of website files for deployment:

  • Automatic runtime compilation of all content and code
    This provides the most convenience and flexibility but the least security and slowest performance on first load. It is the default model used by Web Site Projects.

  • Manual precompilation of assemblies
    Assemblies are compiled and then placed in the bin or TempAspFiles folder. This is the compilation model for Web Application Projects and provides better performance the first time the assembly is called than automatic runtime compilation.

  • Full precompilation of all content and code
    This provides the best performance and security at the expense of convenience and flexibility.

  • Precompilation of all code only
    The content files are not precompiled, and they remain available for update after deployment. This provides nearly the performance of full precompilation, but it retains the ability to modify the content files after deployment, without a full redeploy.

Once the files are ready, four different techniques are available for their actual deployment: XCOPY, Web Deployment Project, Microsoft Installer, and the Publish Website option in VS2008. Your choice of which method to use will be driven by the requirements of your situation. We will describe all of these, with the pros and cons of each, later in this chapter.

Microsoft Intermediate Language (MSIL)

When a .NET application is compiled from C# or another .NET language into an executable file, that file does not contain machine code (the managed C++ compiler can produce assemblies containing native machine code, but this is the exception). Instead, the compiler output is in a language known as Microsoft Intermediate Language (MSIL), or IL for short. When the program is run, the .NET Framework calls a Just-In-Time (JIT) compiler to compile the IL code into machine code, which is then executed.

In theory, a program can be written in any .NET-compliant language to produce the same IL code. Though this is not always precisely true in practice (managed C++ is the exception), it is fair to say that for all practical purposes, all the .NET languages are equivalent.

The use of IL offers several advantages. First, it allows the JIT compiler to optimize the output for the platform. As of this writing, the .NET platform is supported on Windows environments running on Intel Pentium-compatible processors, and as an open source platform named Mono (http://go-mono.org) on some flavors of Linux. It is not a stretch to imagine the framework being ported to other operating environments, such as other flavors of Linux or Unix, the Mac OS, or other hardware platforms. Even more likely is that as new generations of processor chips become available, Microsoft could release new JIT compilers that detect the specific target processor and optimize the output accordingly. The Silverlight 2.0 platform, meanwhile, is a browser-based cross-platform subset of the .NET Framework for those who can't wait for it to be ported completely.

The second major advantage of an IL architecture is that it enables the framework to be language-neutral. To a large degree, language choice is no longer dictated by the capabilities of one language over another, but rather by the preferences of the developer or the team. You can even mix languages in a single application. A class written in C# can be derived from a VB.NET class, and an exception thrown in a C# method can be caught in a VB.NET method.

A third advantage is that the CLR analyzes the code to determine compliance with requirements such as type safety. Things like buffer overflows and unsafe casts are caught at compile time, greatly reducing maintenance headaches and security risks.

ILDASM

You can examine the contents of a compiled .NET EXE or DLL using Intermediate Language Disassembler (ILDASM), a tool provided as part of the Windows SDK for Windows Server 2008 and .NET Framework 3.5. ILDASM parses the contents of the file, displaying its contents in human-readable format. It shows the IL code, as well as namespaces, classes, methods, and interfaces.

Tip

For details on IL programming, we recommend Expert .NET 2.0 IL Assembler by Serge Lidin (APress).

To access ILDASM, click Start → Programs → Microsoft Windows SDK v6.1 → Tools → IL Disassembler.

Or, you can click Start → Programs → Microsoft Visual Studio 2008 → Visual Studio Tools → Visual Studio Command Prompt. When the command prompt appears, enter ildasm to open the program.

Once the program is open, click File → Open to open the file you wish to look at, or drag the file from Windows Explorer onto the ILDASM window.

Alternatively, at the command prompt enter:

ildasm <full path>\<appname.exe>

where the full path (optional if the .exe or .dll is in the current directory) and name of the .exe or .dll will be given as an argument. In either case, you will get something similar to that shown in Figure 20.1, "ILDASM in action". You can click the plus sign next to each node in the tree to expand that node and so drill down through the file.

Figure 20.1. ILDASM in action

ILDASM in action

In Figure 20.1, "ILDASM in action", the file being examined is CustomControls.dll, the assembly containing the custom controls created in Chapter 15, Custom and User Controls. You can see that it has a manifest (which we will describe shortly) and it contains a class called CustomControls.BookCounter which contains several properties, fields, and methods.

The icons used in ILDASM are listed in Table 20.2, "ILDASM icons". Because this is a monochrome book, the colors in which the icons are displayed are mentioned.

Table 20.2. ILDASM icons

Icon

Represents

Description

Namespace

Blue icon with red top edge

Class

Blue icon

Interface

Blue icon with yellow letter I

Value class

Brown icon

Enum

Brown icon with purple letter E

Method

Pink icon

Static method

Pink icon with yellow letter S

Field

Aqua icon

Static field

Aqua icon with dark blue letter S

Event

Green icon

Property

Red icon

Manifest or class info item

Red icon

Manifests

Assemblies in .NET are self-describing: they contain metadata, which describes the files contained in the assembly and how they relate to each other (e.g., references to types), version and security information relevant to the assembly, and dependencies on other assemblies. This metadata is contained in the assembly manifest. Each assembly must have exactly one assembly manifest.

Looking back at Figure 20.1, "ILDASM in action", you can see that a manifest is in the file. Double-clicking the manifest in ILDASM will bring up a window that displays the contents of the manifest, as shown in Figure 20.2, "An assembly manifest".

Looking at the manifest displayed in Figure 20.2, "An assembly manifest", you can see that three external assemblies are referenced: System.Web, mscorlib, and System. All are part of the .NET Framework. This assembly itself is referred to with the following section:

.assembly CustomControls

All of these assemblies have version attributes, and the framework assemblies also have public key token attributes. We will discuss both of these attributes shortly.

Figure 20.2. An assembly manifest

An assembly manifest

Versioning

Every assembly can have a four-part version number assigned to it, of the following form:

<major version>.<minor version>.<build number>.<revision number>

Each part of the version can have any meaning you wish to assign. There is no enforced meaning to the first number as opposed to the second, for example. The generally recommended meanings are that the major version represents a distinctly new release that may not be backward-compatible with previous versions, the minor version represents a significant feature enhancement that probably is backward-compatible, the build number represents a bug fix or patch level, and the revision number represents a specific compilation. Of course, your marketing department may have other ideas, and you are free to assign any versioning meaning you wish to the parts of the version number.

Though there is no enforced meaning to the four parts of the version number, the fact that they are ordered from most significant to least significant is used in the assembly binding process if you specify a range of versions to redirect.

Looking back at the manifest shown in Figure 20.2, "An assembly manifest", every assembly has a version associated with it. For example, in that figure System.Web has the following version attribute:

.ver 2:0:0:0

This corresponds to a major version of 2, a minor version of 0, a build number of 0, and a revision number of 0.

Version numbers are part of the identity of an assembly. The CLR considers two assemblies that differ only in version number to be two distinctly different assemblies. This allows for multiple versions of the same assembly to reside side by side in the same application, not to mention on the same machine.

Tip

Though it is possible to have side-by-side versions of the same assembly in the same application, this is rarely a good idea as a practical matter. You must go out of your way to make this work, and it can be a maintenance headache.

As you will see shortly, the CLR differentiates between two different types of assemblies: private (those assemblies located in the application assembly cache, described earlier) and shared. The CLR ignores the version number of private assemblies. Adding a version number to a private assembly is a form of self-documentation, for the benefit of people examining the source code or the manifest. However, if an assembly is shared, which we will explain in detail shortly, the CLR will be cognizant of the version and can use it to allow or disallow the assembly to load, depending on which version is called for.

In Web Site Projects, you assign versions to an assembly with assembly attributes, either at the top of your main source file or at the top of a separate source file compiled into the assembly.

Any source file that is going to include attributes must make reference to the System.Reflection namespace (unless you type in fully qualified attribute names). In C#, include the following using statement:

using System.Reflection;

The attribute, or attributes, must be at the top of the source file, after the using statements but before any class definitions. In C#, it looks something like this:

[assembly: AssemblyVersion("1.1.*")]

Version syntax in manifests uses colons to separate the numbers, and attributes in source code use periods.

The argument provided to the attribute is a string. Though the four parts of the version number have the meanings described earlier (major, minor, build, and revision), you can use any values you want. To the extent that the CLR checks the version number, it does not enforce any meaning other than to compare whether the total version number is equal to, greater than, or less than a specified value or falls within a specified range.

With that said, the framework does impose some rules, and it also provides some shortcuts for automatically generating meaningful version numbers:

  • If you specify the version, you must specify at least the major revision number. That is, specifying "1" will result in version 1.0.0.0.

  • You can specify all four parts of the version. If you specify fewer than four parts, the remaining parts will default to zero. For example, specifying "1.2" will result in version 1.2.0.0.

  • You can specify the major and minor numbers plus an asterisk for the build. The build will then be equal to the number of days since January 1, 2000 and the revision will be equal to the number of seconds since midnight local time, divided by 2 and truncated to the nearest integer. For example, specifying "1.2.*" will result in version 1.2.1963.28933 if the file was compiled May 17, 2005 at 4:04:27 P.M.

  • You can specify the major, minor, and build numbers plus an asterisk for the revision. The revision will then be equal to the number of seconds since midnight local time, divided by 2 and truncated to the nearest integer. For example, "1.2.3.*" will result in version 1.2.3.28933 if the file was compiled at 4:04:27 P.M.

In Web Application Projects, you use a file called AssemblyInfo.cs (or AssemblyInfo.vb) which is automatically added to the project when it is created. You can also use the project's Properties page to set assembly properties by clicking the Assembly Information button on the Application tab. Figure 20.3, "The Web Application Project Assembly Information dialog" shows the resultant dialog. The changes made there will also be saved in AssemblyInfo.cs.

Figure 20.3. The Web Application Project Assembly Information dialog

The Web Application Project Assembly Information dialog

Private Versus Shared Assemblies

Broadly speaking, there are two types of assemblies: private and shared.

  • A private assembly is one that is used by only a single application.

  • A shared assembly is one that can be used by more than one application.

A private assembly is located in one of two locations. If you are using full runtime compilation, where source files are located in the App_Code directory, the assemblies compiled from that source code and the content files will be located in a system folder somewhere on the machine, managed by the CLR. In addition, any assembly files located in the bin directory will be private assemblies.

Any public member (such as a method, field, or property) contained in a private assembly will be available to any application in that directory by virtue of its presence in the directory. There is no need to register the assembly with the Registry, for example, as is the case with COM.

Private assemblies make no provision for versioning. The CLR does not check the version of private assemblies, and it cannot make load decisions based on version number. From this, it follows that it is not possible to have multiple versions of the same assembly in the same directory. However, it also follows that different directories can each have their own copy of a given assembly regardless of their respective versions. Be careful with this: it is easy to find yourself with inexplicable results when more than one version of an assembly is in use at the same time.

COM allows only a single copy of a given DLL on a machine, to be used by all the applications requiring that DLL. (Support for side-by-side COM DLLs has been added to Windows XP, but this is a relatively new feature.) Back in the days when hard-disk space was a precious commodity, single copies of each DLL was a laudable, if imperfectly implemented, goal. Now, with large hard drives, it makes more sense to allow multiple copies of DLLs, one for each application that needs it. The benefits of this approach are the elimination of DLL Hell, and simplified installation and management. We will discuss the deployment ramifications of private assemblies in "Local Deployment," later in this chapter.

Tip

DLL Hell is the following phenomenon: the user installs a new program (A) and suddenly a different program (B) stops working. As far as the user is concerned, A has nothing to do with B, but unbeknownst to the user, A and B share a DLL. Unfortunately, they require different versions of that same DLL. This problem goes away with .NET; each application can have its own private version of the DLL, or the application can specify which version of the DLL it requires.

In contrast, a shared assembly is one that can be made available to multiple applications on the machine. Typically, shared assemblies are located in a special area of the drive called the Global Assembly Cache (GAC). We'll cover the GAC in more detail shortly.

Tip

Technically, you don't need to put shared assemblies in the GAC because you can specify an alternative location with a <CodeBase> element in a configuration file. There are often reasons for creating a shared assembly other than to share an assembly between applications. For example, to take advantage of Code Access Security (CAS), an assembly must have a strong name (described shortly), which effectively makes it shared.

Shared assemblies also eliminate DLL Hell because the version of the assembly is part of its identity. An application will use the version of the assembly it was originally compiled with, or the version specified by the version policy contained in a controlling configuration file.

Tip

Of course, nothing prevents a developer from releasing a new assembly with the same version numbers as a previous release. In this circumstance, you will have replaced DLL Hell with Assembly Hell.

Shared assemblies in the GAC offer some benefits over shared assemblies not in the GAC, and shared assemblies in general offer several benefits over private assemblies, though they are more of a bother to prepare, install, and administer. These benefits include the following:

  • Performance

    • The CLR looks for an assembly first in the GAC and then in the application assembly cache.

    • Assemblies stored in the GAC do not need to have their public key signature verified every time they are loaded, but shared assemblies not in the GAC do. However, private assemblies never have their signatures verified because they do not have a strong name (described shortly).

    • The files in a shared assembly in the GAC are verified to be present and neither tampered with nor corrupted when the assembly is installed in the GAC. For shared assemblies not in the GAC, this verification step is performed every time the assembly is loaded.

  • Versioning

    • An application, or different applications, can use different versions of the same assembly. This feature is known as side-by-side execution. Private assemblies in different application directories can be different versions.

    • An application will use the same version of an assembly that it was originally compiled with unless overridden by a binding policy specified in a configuration policy.

    • Applications can be redirected to use a different version of an assembly (allowing for easy updating).

  • Robustness

    • Files cannot be deleted except by an administrator.

    • All the files in a shared assembly are verified to be present and neither tampered with nor corrupted.

    • The shared assembly, whether in the GAC or another location, is signed with a public key to ensure that it has not been tampered with.

Strong Names

For an assembly to be shared, it must have a strong name. A strong name uniquely identifies a particular assembly. It is composed of a concatenation of the following:

  • The text name of the assembly (without any file extension)

  • The version

  • The culture

  • A public key token

A strong name with all four parts is fully qualified, whereas one with fewer than all four components is partially qualified. If the culture is omitted, it can be specified as neutral. If the public key token is omitted, it can be specified as null.

A typical fully qualified name might look something like this:

myAssembly,Version=1.0.0.1,Culture=en-US,PublicKeyToken=9e9ddef18d355781

The public key identifies the developer or organization responsible for the assembly. Functionally, it replaces the role of Globally Unique Identifiers (GUIDs) in COM, guaranteeing the uniqueness of the name. It is the public half of a public key encryption scheme. The token listed as part of the strong name is a hash of the public key.

A public key encryption scheme, also called asymmetric encryption, relies on two numbers: a public key and a private key. They are mathematically related in such a way that if one key is used to encrypt a message, that message can be decrypted only by the other key, and vice versa. Furthermore, it is computationally infeasible, though possible, to determine one key given only the other. (Given enough time with a supercomputer, any encryption scheme can be broken.)

Many algorithms are available for calculating hashes. The only two directly supported by the .NET Framework at this time are the MD5 and SHA-1 algorithms. The algorithm used for an assembly is indicated in the manifest by the keywords .hash algorithm, followed by 0x00008003 for MD5 or 0x00008004 for SHA-1.

The general principle is this: you generate a pair of keys, one of which you designate as private and one as public. You keep your private key very safe and very secret.

A hash code is generated for the assembly using the specified encryption algorithm, commonly SHA-1. That hash code is then encrypted using RSA encryption and the private key. The encrypted hash code is embedded in the assembly manifest along with the public key. (The spaces where the encrypted hash code and the public key will go in the manifest are set to zeros before the encryption and are taken into account by the encryption program.)

The CLR decrypts the hash code included in the manifest using the public key. The CLR also uses the algorithm indicated in the manifest, again typically SHA-1, to hash the assembly. The decrypted hash code is compared to the just-generated hash code. If they match, the CLR can be sure the assembly has not been altered since it was signed.

Creating a strong name

Two steps are required to generate a strong name for an assembly.

The first step is to create the public/private pair of keys. The .NET Framework provides a command-line tool for this purpose, sn.exe. Generate a pair of keys by executing sn with the -k option and the name of the file to hold the keys:

sn -k KeyPair.snk

Warning

The options passed to sn.exe are case-sensitive.

Save this file and guard it carefully if you are going to use the keys to provide proof of origin. Make a copy and put it in a secure place, such as a safe deposit box. (If you are using the keys for testing purposes or as a guaranteed unique identifier, there is no need for this level of security.) This file contains the private key which you should use for all the assemblies created by your organization.

In a large organization where it is not feasible for all the developers to have access to the private key, you can use a procedure known as delayed signing. We will explain this in the next section.

The second step is to compile the source code, including the key file, into an assembly. We will cover the specifics of doing so shortly, in "Local Deployment." For now, note the name and location of the key file for use in that process.

Delayed signing

As we mentioned, the private key must be a closely guarded item. However, this presents a quandary: access to the private key is necessary to create a strong name for an assembly. Creating a strong name is necessary to develop and test a shared assembly. Yet it may be imprudent to provide the firm's private key to all the developers working on the project who legitimately need to create strong names.

To get around this quandary, you can use delayed signing, sometimes called partial signing. In this scenario, you create the strong-named assembly using only the public key, which is safe to disseminate to anybody who wants it. You do all your development and testing. Then when you are ready to do the final build, you sign it properly with both the private and public keys.

The first step in delayed signing is to extract the public key from the key file, which contains both the private and public keys. This is done from the command line using the sn tool again, passing it the -p option, the name of the key file, and the name of a file to hold the public key. In the following command, only the public key is contained in PublicKey.snk:

sn -p KeyPair.snk PublicKey.snk

During the publish process, described shortly, you can check the Delay Signing checkbox and include the key file with only the public key.

Local Deployment

Strictly speaking, all you need to do to deploy most .NET applications, including ASP.NET, is to copy the new files to the proper directories on the proper machine, overwriting any previous versions of files if they exist. This is referred to as XCOPY deployment.

XCOPY deployment is so simple as to cause experienced developers to ask, "Is that all there is to it?" It provides all the deployment benefits of .NET except for the ability to deploy assemblies globally (i.e., to use application code modules for multiple applications) and to precompile the application. To implement globally available code modules, you will use global deployment, described later in this section. We will cover precompilation scenarios of local deployment shortly.

Tip

XCOPY is a command-prompt command that originated in the DOS days and has been enhanced for use in modern networks. It is used to copy files and directories from one location to another. The basic syntax is:

XCOPY source destination switches
Both source and destination can be either filenames or directories. There is full support for wildcards. A multitude of switches are available that control such things as resetting (or not) the archive bit, copying (or not) any empty subdirectories, controlling the screen display during copying, and copying (or not) security information about the files. For a full list of the switches available, go to a command prompt and enter:
XCOPY /?
All command-prompt commands (known colloquially as DOS commands, though DOS is no more) are case-insensitive, unless otherwise noted.

You can, of course, copy the files in any manner you wish, including dragging and dropping in Windows Explorer or FTP over the Internet. It is called XCOPY deployment to convey the essential fact that all that is required for deployment is to copy the application virtual root and all its subdirectories.

The CLR automatically handles any changes to application files seamlessly and invisibly to the user. If the Global.asax or web.config file changes, the application will be automatically restarted. If a page, web service, or custom or user control file changes, the next request to come in to the application will get the new version. If an assembly file changes, the CLR will handle the transition from the old version to the new one for any pending requests. It doesn't get much easier than this.

Because all the files necessary to the application are contained within the application virtual root and its child directories, this implies that if two different applications on a server use a .dll of the same name, they will be two independent copies of the file. They may be identical copies, but they don't have to be. It is possible to have two or more different versions of a .dll on the same machine, each in its own application directory structure, with no conflict between applications. This relegates DLL Hell to something that old programmers will tell war stories about, like 64 KB boundaries or running out of conventional memory in DOS.

Within the category of local deployment, several scenarios are available, as discussed in the next several sections. You can use .NET's full compilation of all content and code at runtime, you can compile the assemblies manually, and you can completely precompile all the content and code, or just the code.

Full Runtime Compilation

Compiling all the content and code at runtime is the easiest and most convenient way to compile applications, because no work is required on the part of the developer other than to place any source code files requiring compilation in the App_Code directory under the application root.

This is how Web Site Projects operate in VS2008. When you right-click a site's root directory in the Solution Explorer, you will see a menu item for adding a folder. One of the choices is App_Code. Further, if you attempt to add a class file to a web application, you will be prompted to place it in the App_Code directory. You can decline and place it wherever you wish, but VS2008 tries to guide you to this approach.

If a source file is located outside the App_Code directory, you will have to take active steps to compile the file and make the resultant assembly available to the app, using one of three scenarios described here.

Full runtime compilation is independent of VS2008. Even if you create all the application files in Notepad, as long as the source files are in the App_Code directory under the application virtual root, they will be compiled by the CLR at runtime.

The big advantage to this technique is convenience and automatic synchronicity between content and code files. If you have to remember to do a complete recompile before deploying, it is possible for deployment errors to slip in.

There are two downsides to this compilation scenario:

  • Performance
    A page or class is not compiled until it is first called. The lag time on first call is noticeable, sometimes significant, but it occurs only the first time a page or class is called. There is no performance penalty after the first hit because the compiled assemblies are cached on the server and are immediately available on subsequent calls.

  • Security
    Deploying an app in this manner means the source code and content files are all present on the server as plain text files. Though ASP.NET and IIS are configured to prohibit access to these files, there is always the possibility that a hacker might penetrate security and gain access to the server's filesystem (or that a disgruntled or unscrupulous employee will steal or corrupt the code). In a hosting scenario, where your website is hosted by a commercial hosting service, your source files will be available for any prying eyes with sufficient access rights.

Manual Compilation of Assemblies

In Web Application Projects, any class files in the project, or indeed any class library projects included in the solution and referenced by the web application, will be compiled, added to the site's bin folder, and automatically made available to the application. The key is that they must be manually compiled first either in Visual Studio or by using the command-line compiler, as described shortly. If you choose to use Visual Studio, once they are compiled, you can right-click the project's root folder in the Solution Explorer and click Publish to quickly copy the site to the desired location. The Publish Web dialog shown in Figure 20.4, "The Web Application Project Publish Web dialog" allows you to copy the site locally or to enter an FTP or HTTP URL to deploy to a remote server.

Aside from the inconvenience of manual compilation (which you can automate with batch files, a make system, or MSBuild), this approach provides good performance and security (at least for the compiled source code files; the content files are still present in plain text). In some cases, such as when using custom configuration sections (covered in Chapter 19, Tracing, Debugging, and Error Handling), having a manually compiled assembly is a requirement.

To compile a class file manually, go to Start → All Programs → Microsoft Visual Studio 2008 → Visual Studio Tools → Visual Studio 2008 Command Prompt, to open a command prompt window.

Figure 20.4. The Web Application Project Publish Web dialog

The Web Application Project Publish Web dialog

Change the current directory of the command window to be the directory containing the source file. The command to do this looks something like this:

cd \websites\MyWebApp

The generic syntax for the C# compiler is:

csc [parameters] inputFile.ext

For example, the following command (the command is wrapped here for readability; in reality, it would be on a single line) will output an assembly file called MyClasses.dll in the bin directory, using \websites\MyWebApp\MyClasses.cs as the input source code file:

csc /out:bin\MyClasses.dll /t:library
    /r:system.dll,system.web.dll,MyClasses.cs

The command-line compiler has a large number of parameters available to it, three of which are used here. To see the complete list of parameters available, enter the following command at the command prompt:

csc /?

Table 20.3, "C# compiler parameters" lists the parameters used in the preceding command lines.

Table 20.3. C# compiler parameters

Parameter

Short form

Description

/out:<filename>

Output filename. If not specified, the output filename will be derived from the first source file.

/target:<type>

/t:<type>

Specifies the type of file to build. Possible values are library, exe, winexe, and module.

/reference:<file list>

/r:<file list>

References the specified assembly files. If more than one file exists, include either multiple reference parameters or separate filenames with commas within a single reference parameter. Do not include any spaces between filenames.

Full Precompilation

If you're using a Web Site Project rather than a Web Application Project and you do not want to subject the website users to any compilation delay the first time a page or class is hit, you can precompile the entire site, including the content files. This allows you to deploy only compiled assembly files, which will have no delay the first time they are called and will keep them resistant to prying eyes. No plain text source code files will be deployed.

Tip

For greater security, you can obfuscate the compiled assemblies to make them more difficult to decompile and reverse-engineer. An obfuscator, called the Dotfuscator Community Edition, is included with VS2008 under the Tools menu. It is an option in the VS2008 installer, though, so if you can't see it, check to see whether it is installed.

You can accomplish this full precompile from the command line using the aspnet_compiler.exe utility. However, the easier way to do it in VS2008 is by right-clicking the Web Site Project in the Solution Explorer and choosing Publish Web Site. This integrates in the MSBuild build engine.

After clicking that command, you will get the dialog box shown in Figure 20.5, "The Web Site Project: Publish Web Site dialog box". The default target location, if you're using a File System location, will be a folder called PrecompiledWeb under the VS2008 project file location. You can enter an FTP or HTTP URL to deploy to a remote server.

For a full precompile with maximum security, that is, including the content files, uncheck the checkbox labeled "Allow this precompiled site to be updatable."

To use strong names with a key file, as described previously, check the checkbox labeled "Enable strong naming on precompiled assemblies" and enter or browse to the key file location. There is a checkbox to implement Delay signing as described earlier. The Key container controls allow you to use RSA Key Containers (a topic beyond the scope of this book) rather than a key file.

Figure 20.5. The Web Site Project: Publish Web Site dialog box

The Web Site Project: Publish Web Site dialog box

The result of this precompile will be a directory structure, suitable for XCOPY deployment to a production server, in the location specified in the dialog box. In that directory structure will be a bin directory containing all the compiled .dlls, plus compiled versions of the content files. The content files will not be present, although stub files named identically to the original content files will be there. These stub files provide a target for browser requests but are used only to redirect the request to the compiled version in the bin directory.

If you want to make any changes to any file, you must do so in VS2008 and recompile.

Precompilation of Code Only

Even though the full recompile is often what you need, sometimes you want the flexibility to make minor modifications to the content files after they have been deployed, without the hassle of a full recompile and redeploy. In this case, check the checkbox in Figure 20.5, "The Web Site Project: Publish Web Site dialog box" labeled "Allow this precompiled site to be updatable."

The result will be the same as with a full precompile, except that the content files will be the original version rather than stub versions. These content files can be edited after deployment, and those changes will take effect with any subsequent requests, transparently to any users of the site.

Global Deployment

In the preceding section, we stated that most applications are deployed by copying files to the proper directory. The exception occurs when you wish to use the same assembly in more than one application. In this case, you use global deployment.

There are many scenarios in which you might want to have a common assembly file accessible to multiple applications. A firm might have two different websites on a server, both providing access to the same database. One website is free of charge and open to the public, but is of limited functionality; the other is fully functional, requiring a paid subscription. Because both sites access the same database, they will have common database query routines. They may also have common login routines. Using the same assembly to contain those common routines will enhance maintainability. Another scenario might be a web-hosting firm that has many websites running on a server. It may want to offer some functionality to all of its client websites. Encapsulating this functionality in a globally available assembly would make this easy to offer and maintain.

Another consideration is versioning. When assemblies are local to an application, each application can have its own version of common assemblies. The .NET Framework allows for global assemblies to have multiple versions. Each application making use of the global assembly can either specify the version it wants to use or take the latest version. By specifying the version, an application will not break if a newer version of the assembly introduces signature changes or bugs.

To provide global availability of an assembly, it must be installed to the GAC. The GAC is a machine-wide location for code to be shared among multiple applications on that machine. Typically, it is physically located at C:\windows\assembly.

To make an assembly file suitable for inclusion in the GAC, it must have assembly information compiled into it. You can do this using Assembly attributes. These Assembly attributes either can be included in the same source code file as the class or classes being compiled into the assembly, or can be in a separate source code file that is compiled into the assembly along with the class source code file(s). The format of the Assembly attributes looks like this:

[Assembly:attributeName(attributeValue)]

attributeName is the name of the Assembly attribute, and attributeValue is the string value assigned to the attribute. So, for example, if you're assigning the AssemblyVersionAttribute, it would look like the following:

[Assembly: AssemblyVersionAttribute ("1.0.3.101")]

Table 20.4, "Assembly attributes" lists the available Assembly attributes with a brief description.

Table 20.4. Assembly attributes

Attribute

Description

AssemblyAlgorithmIdAttribute

Sets the hash algorithm for the files in the assembly.

AssemblyCompanyAttribute

A string containing the company name.

AssemblyConfigurationAttribute

A string configuration, such as Retail or Debug. Not used by the CLR.

AssemblyCopyrightAttribute

A string containing copyright information.

AssemblyCultureAttribute

A field indicating the culture supported by the assembly.

AssemblyDefaultAliasAttribute

A string containing the default alias for the assembly. It can contain a friendly name.

AssemblyDelaySignAttribute

A Boolean indicating delayed application of the digital signature.

AssemblyDescriptionAttribute

A string containing a short description of the assembly.

AssemblyFileVersionAttribute

A string containing the Win32 file version number. Defaults to the assembly version.

AssemblyFlagsAttribute

A flag indicating the kind of side-by-side execution allowed.

AssemblyInformationalVersionAttribute

A string containing version information not used by the CLR.

AssemblyKeyFileAttribute

A string containing the name of the file with either a public key signature if using delayed signing, or both public and private keys. The filename is relative to the output file path and not to the source file path.

AssemblyKeyNameAttribute

A string containing the key container.

AssemblyName

A string containing an assembly's unique name.

AssemblyNameProxy

A string containing a version of the assembly's unique name that can be used to access it remotely.

AssemblyProductAttribute

A string containing product information.

AssemblyTitleAttribute

A string containing the friendly name for the assembly.

AssemblyTrademarkAttribute

A string containing trademark information.

AssemblyVersionAttribute

A numeric version representation, in the form major.minor.build.revision.

If you are using Assembly attributes in a source file, you must reference the System.Reflection namespace with the using keyword in C#. If you're using AssemblyAlgorithmIdAttribute, you must also include System.Configuration.Assemblies.

For an assembly to be included in the GAC, it must have a strong name.

Once all of this is in place, you can drag the assembly file into C:\windows\assembly using Windows Explorer to add the assembly to the GAC, or you can use GacUtil.exe. The syntax is:

gacutil /i pathToDLL\myDLL.DLL

where pathToDLL is the path to the directory containing the assembly file, and myDLL.DLL is the name of the assembly file.

The GacUtil.exe utility has several command-line switches. For a complete list, enter the following at a command prompt:

gacutil /?

Some of the more commonly used switches are described in Table 20.5, "Some common GacUtil.exe switches".

Table 20.5. Some common GacUtil.exe switches

Switch

Description

/i

Installs an assembly to the GAC.

/u

Uninstalls an assembly from the GAC. If the name of the assembly to be uninstalled has no qualifying information, such as version, all assemblies of that name will be uninstalled.

/l

Lists all the assemblies installed in the GAC.

To use a global assembly in applications, it must be registered in the machine.config file or the machine-level web.config file. To add the preceding assembly to the configuration file, add the following line to the <configuration><system.web><compilation><assemblies> section:

<add assembly="myDLL, Version=1.0.3.101, Culture=neutral,
   PublicKeyToken=nnnnnnnn"/ >

where nnnnnnnn is obtained from GacUtil by running:

gacutil /l

from the command line, finding myDLL in the listing, and copying the public key token into place.

Windows Installer

XCOPY deployment works well for many websites. However, it falls short in some situations. For example, XCOPY does not automate the installation of assemblies into the GAC, nor does it make Registry edits. Further, if you need to install to multiple servers, such as a web farm, or if you have a precisely scripted and repeatable installation process, XCOPY may get tedious and error-prone. For all of these scenarios, you need an installation tool with more robust capabilities. Several third-party installation tools are available, such as InstallShield, InstallAnywhere, and Wise.

Windows has its own installation technology, known as Windows Installer, which has been included with all of the Windows operating systems starting with Windows 2000.

Windows Installer provides installation, removal, and management of applications. It also supports features such as automatic repair of existing installations, transactional operations (a set of operations performed by the installer can be undone if installation does not complete successfully), installation on demand (application features are not installed until the first time a user tries to use that feature), and installation in locked-down environments if an administrator approves an installation package by means of Group Policy.

The Windows Installer is based on a database. Each application to be installed has a file associated with it, with an extension of .msi, which contains the data for that application, including rules for controlling the installation.

You can open an .msi file in several ways. Double-clicking the file will open the Windows Installer for that application. If the application is not currently installed on the machine, you will be presented with a series of dialog boxes for installing the application. Depending on how the installation package was customized (described shortly), these dialogs will allow the user to select a target destination, offer installation for the current user or all users, present software license information, and so on.

If the application is already installed on the machine, you will be presented with a dialog box offering the choice to repair or remove the installation.

If you right-click an .msi file in Windows Explorer, the context menu will include three relevant menu items: Install, Repair, and Uninstall. These options perform the same operations you might access by double-clicking the file.

Tip

You can execute the Windows Installer from a command prompt. To install an application, use the following command:

    msiexec /i MyApp.msi
To uninstall the app, use the following command:
    msiexec /x MyApp.msi
To repair an installation, use this line:
    msiexec /f MyApp.msi
Interestingly, msiexec.exe is one of the few command-line tools provided by Microsoft that does not display a list of parameters when executed with the /? switch. However, executing the command with no command-line switches will bring up a dialog box with a list of all the command-line switches.

Probably the easiest way to run the Installer is to execute setup.exe, the Installer Bootstrapper, which VS2008 creates in a process that we will describe shortly. Do so by double-clicking the file in Windows Explorer.

The Windows Installer automatically logs installations and removals in the Application Log of the Event Viewer found in Control Panel → Administrative Tools. Each entry in the log will have the MsiInstaller as the value for Source.

The Windows Installer is integrated into VS2008. You create installation packages for your application by adding one or more setup projects to the web application. By having more than one setup project as part of an application, you can easily deploy the same application with different configurations.

VS2008 uses MSBuild, the Microsoft build engine, to perform the builds that get packaged into the .msi files. You can create powerful and flexible MSBuild projects. MSBuild is also available outside VS2008, from the command line, with the MSBuild.exe utility. Going into all the detail of MSBuild is beyond the scope of this book.

To demonstrate using VS2008 to build deployment packages, you will first create a three-page Web Site Project called C20_Deployment, with the pages named Default.aspx, FirstPage.aspx, and SecondPage.aspx. Each page will consist of two buttons to navigate to the other two pages. In addition, you will create a class file, Class1.cs, in the App_Code directory, which will have a single static public method called GetTime that returns a string representing the current time.

All three content files along with their associated code-behind files and the class file are listed in Examples Example 20.1, "C20_Deployment\Default.aspx" through Example 20.7, "C20_Deployment\App_Code\Class1.cs". (You might want to download these code examples from http://www.oreilly.com, because the code itself is not the focus of this section, but rather how to deploy the code.)

Example 20.1. C20_Deployment\Default.aspx

<%@ Page Language="C#" AutoEventWireup="true"
   CodeFile="Default.aspx.cs" Inherits="_Default" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
   <title>Deployment Example</title>
</head>
<body>
   <form id="form1" runat="server">
   <div>
      <h1>Deployment Example</h1>
      <h2>Home Page</h2>
      <asp:Label ID="lblTime" runat="server" />
      <br />
      <asp:Button ID="btn1stPage" runat="server"
         Text="Go To First Page" OnClick="btn1stPage_Click" />
      <asp:Button ID="btn2ndPage" runat="server"
         Text="Go To Second Page" OnClick="btn2ndPage_Click" />
   </div>
   </form>
</body>
</html>

Example 20.2. C20_Deployment\Default.aspx.cs

using System;
using System.Web.UI;

public partial class _Default : Page
{
   protected void Page_Load(object sender, EventArgs e)
   {
      lblTime.Text = Class1.GetTime();
   }

   protected void btn1stPage_Click(object sender, EventArgs e)
   {
      Response.Redirect("FirstPage.aspx");
   }

   protected void btn2ndPage_Click(object sender, EventArgs e)
   {
      Response.Redirect("SecondPage.aspx");
   }
}

Example 20.3. C20_Deployment\FirstPage.aspx

<%@ Page Language="C#" AutoEventWireup="true"
   CodeFile="FirstPage.aspx.cs" Inherits="FirstPage" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
   <title>Deployment Example</title>
</head>
<body>
   <form id="form1" runat="server">
   <div>
      <h1>Deployment Example</h1>
      <h2>First Page</h2>
      <asp:Button ID="btnHomePage" runat="server"
         Text="Go To Home Page" OnClick="btnHomePage_Click" />
      <asp:Button ID="btn2ndPage" runat="server"
         Text="Go To Second Page" OnClick="btn2ndPage_Click" />
   </div>
   </form>
</body>
</html>

Example 20.4. C20_Deployment\FirstPage.aspx.cs

using System;
using System.Web.UI;

public partial class FirstPage : Page
{
   protected void btnHomePage_Click(object sender, EventArgs e)
   {
      Response.Redirect("Default.aspx");
   }
   protected void btn2ndPage_Click(object sender, EventArgs e)
   {
      Response.Redirect("SecondPage.aspx");
   }
}

Example 20.5. C20_Deployment\SecondPage.aspx

<%@ Page Language="C#" AutoEventWireup="true"
   CodeFile="SecondPage.aspx.cs" Inherits="SecondPage" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
   <title>Deployment Example</title>
</head>
<body>
   <form id="form1" runat="server">
   <div>
      <h1>Deployment Example</h1>
      <h2>Second Page</h2>
      <asp:Button ID="btnHomePage" runat="server"
         Text="Go To Home Page" OnClick="btnHomePage_Click" />
      <asp:Button ID="btn1stPage" runat="server"
         Text="Go To First Page" OnClick="btn1stPage_Click" />
   </div>
   </form>
</body>
</html>

Example 20.6. C20_Deployment\SecondPage.aspx.cs

using System;
using System.Web.UI;

public partial class SecondPage : Page
{
   protected void btnHomePage_Click(object sender, EventArgs e)
   {
      Response.Redirect("default.aspx");
   }

   protected void btn1stPage_Click(object sender, EventArgs e)
   {
      Response.Redirect("FirstPage.aspx");
   }
}

Example 20.7. C20_Deployment\App_Code\Class1.cs

using System;

public class Class1
{
   public static string GetTime(  )
   {
      return DateTime.Now.ToLongTimeString(  );
   }
}

Build Configurations

By default, websites created in VS2008 are configured not to enable debugging. As soon as you press F5, however, you will be prompted to run them in debug mode so that if any unhandled errors occur, the user will automatically be brought into the debugger. The price for being in debug mode is reduced performance.

You can configure a website to disable debugging. In this mode, all breakpoints will be ignored and any unhandled errors will result in an error page being displayed to the user. The nondebug (release) mode is more performant than the debug mode and is typically used when a web application is deployed.

You can select debug or nondebug mode with an entry in the <compilation> element in web.config. (We covered configuration in Chapter 18, Application Logic and Configuration.) You can configure this by manually editing web.config or by running the Web Site Administration Tool (WAT).

To edit the file manually, set the debug attribute of the <compilation> tag to true or false, as in the following code snippet:

<compilation debug="true" />

To use the WAT, click the Website → ASP.NET Configuration menu item. When the tool opens, click the Application tab, then the "Configure debugging and tracing" link. On that page, check or uncheck the "Enable debugging" checkbox. Doing so will automatically make the correct entry in web.config.

Warning

Visual Studio provides two default build configurations for Web Application Projects: Debug and Release. A Release build produces different, smaller binaries than a Debug build. You can run either with Debug=true in web.config. A Release-mode application can still have breakpoints on method exits and possibly other places, but not on individual lines of code. Web Site Projects have only one build configuration by default: Debug.

Adding a Setup Project with the Setup Wizard

You are now ready to add a project to the website that will take advantage of the Setup Wizard to walk you through the steps of creating an installation package. This installation package will install a version of the application with the debug mode set to the value currently indicated in web.config, as described in the preceding section. For this example, verify that debug mode is set to true.

With the website root directory highlighted in the Solution Explorer click the File → Add → New Project menu item. From the tree view on the left, select Setup and Deployment under Other Project Types. From the list on the right, select Setup Wizard. Name the project Setup-Debug and leave the default location. The dialog box will look something like that shown in Figure 20.6, "The Setup Wizard project dialog box".

Figure 20.6. The Setup Wizard project dialog box

The Setup Wizard project dialog box

By using the default location, you ensure that the setup project will be located in its own folder under the root location for all your project files (set using Tools → Options). You might want to consider changing the location of this setup project to be in a subdirectory of the project it is setting up.

The wizard will take you through five screens:

  • The first is a splash screen.

  • The second screen asks you to choose a Project Type. Select "Create a setup for a web application."

  • The third screen asks you to select the outputs from all the projects in the solution. Check the only checkbox available, next to "Content Files from C:\<directory>\C20_Deployment." (Your directory structure may be different from those shown here.) Click the Next button.

  • The fourth screen allows you to include other files, such as READMEs, of which there are none.

  • The fifth and final screen displays a summary of all the settings for this setup project. Click Finish.

A project will be added to the Solution Explorer and the main design window will now show a File System editor for the setup project, similar to the screenshot in Figure 20.7, "The Setup Project, open in VS2008".

Figure 20.7. The Setup Project, open in VS2008

The Setup Project, open in VS2008

Build the setup project by right-clicking the project name in the Solution Explorer and selecting the Build menu item.

Build will build the application, taking all dependencies into account but not building any components that are up-to-date. In a large solution where current development work is being done on only one or two projects, it makes for a faster build process.

The Rebuild menu item first deletes all intermediary files and previous build outputs and then builds the entire app from scratch. It may take longer, but it is smart to do a rebuild before testing the final build.

You can open the Output window to view a log of the build process by clicking View → Other Windows → Output. At the end of the build process, it should say:

Build: 2 succeeded or up-to-date, 0 failed, 0 skipped

The number 2 refers to the two projects in this application, that is, the Web Site Project and the setup project.

Two files are created by this build process, which actually need to be deployed: setup.exe and Setup-Debug.msi. Copying these files to the deployment target (the server) and double-clicking the setup file will cause the contents of the .msi file to be installed on the local machine.

The output files from the build process will be located in the Setup-Debug directory created for the Setup-Debug project. This will contain a .vdproj file which contains information VS2008 uses to properly handle the project, and two other subdirectories: Debug and Release. These folder names correspond to the two possible build configurations for the setup project. The deployment output files, setup.exe and Setup-Debug.msi, will be contained in the folder called Debug, because that is the name of the current active build configuration. When you switch the current build configuration to Release (using Build → Configuration Manager), the files will be saved in the Release folder.

Adding a Setup Project Manually

You can create a setup project manually without using the Setup Wizard. This gets you to the same place as using the wizard, trading convenience for greater control.

To begin, repeat the process of clicking the File → Add → New Project menu item. This time select the Web Setup Project template, and name the project Setup-Release, as this time you'll create a nondebug (release) version.

Edit web.config for the C20_Deployment website to set the debug attribute of the <compilation> tag to false, or open the WAT, go to the Application page, click "Configure debugging and tracing," and uncheck "Enable debugging".

You must manually add the output files to the setup project this time, so right-click the setup project in the Solution Explorer and select Add → Project Output. Select Content Files from the list at the top of the dialog box, as shown in Figure 20.8, "Adding project output manually".

You can select multiple outputs using standard Windows techniques with the Ctrl or Shift key.

As soon as the output is added, any dependencies are detected and the class library is automatically included in the build.

The new project with that name will display in the Solution Explorer with the primary outputs, similar to that shown in Figure 20.9, "The Solution Explorer and the Web Setup projects".

Figure 20.8. Adding project output manually

Adding project output manually

Figure 20.9. The Solution Explorer and the Web Setup projects

The Solution Explorer and the Web Setup projects

Further Customizations

Whether the setup project came from the wizard or not, a number of customizations are available to you. Specifically, right-clicking a setup project in the Solution Explorer and selecting View or clicking the View → Editor menu item will bring up six different editor choices, as shown in Figure 20.10, "Further setup project customizations in the Solution Explorer".

Figure 20.10. Further setup project customizations in the Solution Explorer

Further setup project customizations in the Solution Explorer

Clicking any of these editors will display that editor for that project in the main pane. (When you first add a setup project, the File System editor is what you're looking at.)

File System editor

The File System editor, shown in Figure 20.11, "The File System editor", lets you control where files are added to the end-user's machine. The items in the leftmost pane are named folders on the target machine, such as the Web Application folder, the directory where the application is installed. Clicking any of the named folders displays its contents in the right pane.

Figure 20.11. The File System editor

The File System editor

Tip

Web Setup Projects present only one named folder on the left side of this editor: the Web Application folder.

Right-clicking an item displays a context menu. Select Add to add a folder, a file, an assembly, or a project output. You can add shortcuts to the desktop or to the Start → Programs menu by right-clicking the appropriate item.

Warning

Before adding any files to a named folder, you must first set the AlwaysCreate property of that folder to true before building the setup project. To do so, click the relevant named folder in the left pane, and set the AlwaysCreate property in the Properties window.

Use this editor to add shared assemblies to the GAC on the target machine. To do so right-click the root of the left pane, File System on Target Machine, and select Add Special Folder. You will see a plethora of special folders, many of which should be familiar to you. Click Global Assembly Cache Folder to add this to the left pane. Right-click it and select Add → Assembly to add an assembly to the GAC. For an assembly to be added to the GAC, it must first have a strong name.

Registry editor

The Registry editor allows your setup program to make entries in the Registry of the target machine. The screenshot in Figure 20.12, "The Setup Project Registry editor" shows a new key called TestValue inserted in HKEY_LOCAL_MACHINE\Software\<Manufacturer>, where <Manufacturer> will be replaced with the value of the Manufacturer property of the setup project. (It defaults to the organization entered when VS2008 was installed.)

Figure 20.12. The Setup Project Registry editor

The Setup Project Registry editor

You can add new keys to the Registry by right-clicking a node in the left pane and selecting New → Key. If you want to add a value to a key, right-click that key and select New. Then select one of the following:

  • String Value

  • Environment String Value

  • Binary Value

  • DWORD Value

You can name the new value in the right pane or in the Properties window. The value is set in the Properties window.

File Types editor

The File Types editor allows you to associate file extensions with the application. If an associated file type has been double-clicked in Windows Explorer, the application will open with that filename passed in as an argument.

To add a file type to the project right-click File Types on Target Machine and select Add File Type. A default document type will appear with the &Open command below it. In the Properties window, change the name to something meaningful-say, MyApp Data File-and enter the extension in the Extensions property-say, abc-as shown in Figure 20.13, "Associating a file extension with your project".

Now if a file on the target machine with an extension of .abc-say, SomeData.abc-is double-clicked in Windows Explorer, the application will open with that file.

Figure 20.13. Associating a file extension with your project

Associating a file extension with your project

User Interface editor

The User Interface editor allows you to customize the dialog boxes that are displayed during the installation process. The process is divided into two categories: Install and Administrative Install. The first is for normal installation by users on their local machine, and the latter is for installation to a network for use by members of a workgroup.

Each category is further divided into three phases: Start, Progress, and End. The default configuration looks like that shown in Figure 20.14, "The User Interface editor".

Right-clicking any item in the window and selecting the Add Dialog menu item brings up a selection of standard dialog boxes which can be added and further customized, such as dialogs with radio buttons, checkboxes, or text boxes, a customer information screen, a splash screen, a license agreement, and so on.

Again, because this example is based on a Web Setup Project, the middle node under Start is Installation Address. If you had based the project on a Setup Project, those nodes would be Installation Folder.

Any dialog box added in this way will have properties for text files or bitmaps to display, executables to run, and so on.

Figure 20.14. The User Interface editor

The User Interface editor

Custom Actions editor

The Custom Actions editor displays the four phases of the installation process: Install, Commit, Rollback, and Uninstall. You can assign an executable or script file to execute at the conclusion of any of these phases.

Launch Conditions editor

The Launch Conditions editor allows you to create conditional installs. For example, you can specify that a certain version of Windows be installed, a certain file is present, or a certain Registry entry has the correct value.

By default, Web Setup Projects verify that IIS has been installed with a version greater than or equal to 4, as shown in Figure 20.15, "Setting up a conditional install".

Deploying the Website

To deploy the website created by building a setup project, copy the two output files, setup.exe and setup.msi, to the target machine and double-click setup.exe in Windows Explorer. The installation program will run, opening the Setup Wizard. If you're running Vista, you'll get a UAC dialog asking for administrative permission to run the install.

Figure 20.15. Setting up a conditional install

Setting up a conditional install

Clicking the Next button will bring up a dialog box showing all the websites available on that machine in the Site drop-down. You can enter the name of the virtual directory you wish to use.

Clicking the Disk Cost button will bring up a dialog box detailing the drive space required and available on the local machine.

After looking at the drive space, click the Next button on the Setup Wizard dialog box to go to a confirmation page.

Click Next one more time to start the installation.

Assuming that the installation succeeds, you will see the new virtual directory created in IIS. You can then run the website by opening a browser and navigating to the new URL, with a web address similar to the following:

https://localhost/setup-debug/default.aspx

Web Deployment Projects

One of several out-of-band releases for VS2005 and VS2008 has been the Web Deployment Project, an additional project type to add to your solution, which will automatically compile and deploy your Web Site Project for you according to the settings you've set for it. It also works with Web Application Projects, but is targeted at Web Site Projects to give them a build script to automate. Web Application Projects already have a build script-their .csproj project file-of which the Web Deployment Project will work independently. Web Site Projects do not have a .csproj file.

It fits in rather neatly between XCOPY deployment and using a Web Setup Project, automating the deployment of your website, but performing when you build the website in VS2008 rather than when a user runs an installer. This makes the Web Deployment Project excellent for deploying the latest build of a website to a staging server for testing, either manually or through some sort of automated integration server such as Cruise Control or Team System.

Tip

The VS2008 Web Deployment Project was released to the Web in January 2008; it is available for download from https://www.microsoft.com/downloads/details.aspx?familyId=0AA30AE8-C73B-4BDD-BB1B-FE697256C459.

Once you've installed the Web Deployment Project (admin rights required, Vista users!), right-click a Web Site Project in the Solution Explorer and select Add Web Deployment Project from the context menu to add one to your current solution. A dialog will appear asking you to name the project and where to save it, as shown in Figure 20.16, "Adding a Web Deployment Project to your solution".

Figure 20.16. Adding a Web Deployment Project to your solution

Adding a Web Deployment Project to your solution

The deployment project actually maps to a single MSBuild file which you can read and alter by right-clicking the new entry in the Solution Explorer and selecting Open Project File.

If you're not entirely convinced that editing an MSBuild file directly is a good idea, you'll be relieved to know that just double-clicking the deployment project file in the Solution Explorer will bring up a dialog which is much simpler to deal with, as shown in Figure 20.17, "The Web Deployment Project edit dialog".

Figure 20.17. The Web Deployment Project edit dialog

The Web Deployment Project edit dialog

The dialog has four screens to deal with different options:

  • Compilation
    Where to save the compiled website and how much of it should be compiled

  • Output Assemblies
    How the compiler output should map to the structure of your website

  • Signing
    Whether the website assemblies should be strongly named and signed with an .snk file

  • Deployment
    Once compiled, where the built website should be deployed to along with a couple of additional tasks

We'll look at each of these in turn.

Unless you have a good reason to deploy your site each time you create a Debug build of your site, you'll want to disable the deployment project for Debug builds. To do this, open the Configuration Manager in VS2008, select Debug in the Active Solution Configuration drop-down list, and then uncheck the checkbox for the deployment project in the Build column, as shown in Figure 20.18, "Disabling the deployment project for Debug builds".

Figure 20.18. Disabling the deployment project for Debug builds

Disabling the deployment project for Debug builds

Compilation

The Compilation tab, shown in Figure 20.17, "The Web Deployment Project edit dialog", lets you specify four things related to the actual compilation of the website:

  • Output Folder
    This indicates where to save the compiled files.

  • Generate debug information
    This indicates whether debug symbols should be included in the compiled assemblies. By default, this is on for Debug builds and off for Release builds. This will also switch debugging on or off in web.config.

  • Use IIS metabase path for source input
    If you're working with a website hosted by IIS rather than on the filesystem, you can choose to specify the website for compilation to the deployment project by its virtual directory name in the IIS metabase, rather than leaving it to Visual Studio. This more precise approach deals with the problem that Visual Studio will compile the website in the virtual directory and any other websites in subdirectories by default, which will cause a crash. By using the metabase's directory name, however, you ensure that this inclusion of other websites will not occur.

  • Allow this precompiled site to be updatable
    The same option as the checkbox of the same name in the Publish Website dialog (see Figure 20.5, "The Web Site Project: Publish Web Site dialog box"), this allows you to specify whether all pages, code, and content should be compiled into a (group of) assemblies or whether the .aspx and .ascx markup pages should be left out of the assembly so that they can be updated on the server without the need for the site to be recompiled. We covered this choice in more depth in "Precompilation of Code Only," earlier in this chapter.

Output Assemblies

The purpose of the Output Assemblies screen, shown in Figure 20.19, "The Output Assemblies dialog for Web Deployment Projects", is to determine how the structure of your website should be reflected, if at all, by the assemblies into which it is compiled.

Figure 20.19. The Output Assemblies dialog for Web Deployment Projects

The Output Assemblies dialog for Web Deployment Projects

The options are to compile the entire contents of the website given the settings in the Compilation dialog into (from top to bottom):

  • A single assembly

  • One assembly per folder in the website

  • One assembly for all the pages and controls in the website and another for the special folders, such as App_Code and App_WebReferences

  • One assembly for each page and control in the website and also one each for the special folders, such as App_Code and App_WebReferences

The final option on this page is to add a version number to your assemblies.

Tip

The Web Deployment Project will take this number from assemblyinfo.cs if you are using a Web Application Project or have added one to your App_Code folder, or you can generate one automatically in MSBuild if required.

Signing

The Signing dialog, shown in Figure 20.20, "The Signing dialog for Web Deployment Projects", allows you to give your website assemblies a strong name for sharing among applications, as described earlier in "Strong Names."

Figure 20.20. The Signing dialog for Web Deployment Projects

The Signing dialog for Web Deployment Projects

You have the option to enable strong naming and to sign it during the build with a key file which you specify, or to delay signing it for later.

The other option here is to mark the website assemblies with the AllowPartiallyTrustedCallers attribute, which means that when placed in the GAC, this assembly can be called by any (possibly malevolent) code. This attribute overrides the default in which this is not the case. A discussion of why you would want to enable this is beyond the scope of this book (yet is easily discoverable on Google or Live Search), but leaving this option unchecked is fairly wise.

Deployment

The Deployment screen, shown in Figure 20.21, "The Deployment dialog", allows you to customize your website post-deployment.

Figure 20.21. The Deployment dialog

The Deployment dialog

The dialog allows you to enable three actions to occur once your website has been deployed:

  • Enable Web.config file section replacement
    This addresses the common scenario whereby database connection strings and other website settings are most likely to differ in a live environment from those on your development box. Rather than having to remember to go in and edit your web.config file and put in the right settings once the site is deployed, you can use the Web Deployment Project to replace named sections of your web.config file. Simply add into the box a semicolon-separated list of sections to replace along with the name of the XML file containing the settings to replace them with and you're on your way.

    By default, these settings will be copied into web.config, but checking the "Use external configuration source file" box will instead set the configSource attribute for that setting to use the replacement file directly. (See Chapter 18, Application Logic and Configuration for more on configSource.) Checking the "Enforce matching section replacements" box will force the deployment project to make sure that the settings replacing those currently in web.config have the same number of elements as those being replaced or else an error will occur.

  • Create an IIS virtual directory for the output folder
    If checked, the deployment project will create a new virtual directory with the specified name pointing to the directory containing the new website assemblies, if one does not exist. If one does exist, it will be deleted and re-created if the "Replace the existing virtual directory" box is checked.

  • Remove the App_Data folder from output location
    This removes the App_Data folder from the compiled version of the website. This might come in handy if, for example, you're using SQL Server Express for development and SQL Server Standard on a staging or live server.

Tip

Because a Web Deployment Project is just another MSBuild file, your website's deployment can be customized beyond the options that these four dialogs offer. However, it also means working directly with MSBuild, which is beyond the scope of this book. If you'd like to look into this more, visit https://msdn2.microsoft.com/en-us/library/aa479568.aspx for more on the specifics of the MSBuild file behind the Web Deployment Project, and https://msdn2.microsoft.com/en-us/library/ms171452.aspx for Microsoft's guide to the MSBuild syntax.