20.4 Deploying the Application
It is very simple to deploy an ASP.NET application, especially when compared to classic ASP. There is no registering of components in the Registry, no need to stop and start the server or the operating system, no problem with multiple versions of the same dll for different applications, and no more DLL hell.
That's the good news. There is no bad news, especially if you do not need to deploy assemblies globally.
ASP.NET derives all this deployment bliss by virtue of being part of the .NET Framework. The deployment features mentioned earlier are common to all applications developed under the .NET Framework.
There are actually two different ways to deploy applications. The first, 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). In order to implement globally available code modules, you will use global deployment. Both deployment methods are described in more detail in the following sections.
An assembly is the .NET unit of versioning and deploying code modules. Strictly speaking, an assembly consists of Portable Executable (PE) files. PE files can be either dll files or exe files. These PE files are in exactly the same format as normal Windows PE files.
In ASP.NET, an assembly will typically consist of a single dll, although it may consist of multiple files. Assemblies appear to the user to be a single file.
Assemblies are self-describing because they contain metadata that fully describes the assembly and the classes, methods, and types it contains. 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 (name, version, etc.), a list of the types and resources in the assembly, a map to connect public types with the implementing code, and a list of assemblies referenced by this assembly.
An application consists of all the files and resources in an application virtual root directory and in all the subdirectories underneath the virtual root. One of the standard subdirectories found in nearly all applications is the \bin directory, sometimes called the application assembly cache. All the assemblies for the application are typically placed in this directory.
If an assembly file is placed in the application assembly cache, then all the classes contained in that assembly are automatically registered with the application. There is no developer or user action required for this registration to occur. Any class, method, or type defined in the \bin directory is available to the rest of the application.
Assemblies are not loaded into memory unless and until they are needed. When an assembly is needed, the CLR does not actually load the assembly itself into memory. If it did, then that assembly would be locked until the application was stopped. This would require the application to be stopped and restarted every time a new version of the assembly was to be installed. Instead, a shadow copy of the dll is created in memory. This shadow copy is then locked, leaving the original assembly file unlocked.
The CLR constantly monitors the assembly cache to see if any new assemblies have been added or if any of the existing assemblies have changed. If a new assembly is detected, the classes it contains are automatically registered with the application. If a change to an existing application is detected, than 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, then the shadow copy of that version is allowed to expire and the transition is complete.
20.4.2 XCOPY Deployment
All that is necessary to deploy most ASP.NET applications—in fact, to deploy most .NET applications — 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 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. There are a multitude of switches 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:
It is not required to actually use the XCOPY command to copy the files. You can copy the files in any manner you wish, including DOS commands from the command prompt, 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 to deploy 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 either the global.asax or web.config file (or their code-behinds) changes, the application is automatically restarted. If a page, web service, or custom or user control file changes, the next request to come in to the application just gets the new version. If an assembly file changes, the CLR handles the transition from old version to new for any pending requests. It doesn't get much easier than this.
Since 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 are 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 64KB boundaries or running out of conventional memory in DOS.
20.4.3 Global Deployment
In the previous section "XCOPY Deployment," it was stated that most applications are deployed by simply 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 it might be desirable to have a common assembly file accessible to multiple applications. A firm might have two different web sites on a server, both providing access to the same database. One web site is free of charge and open to the public but of limited functionality, while the other is fully functional, requiring a paid subscription. Since both sites access the same database, they will have common database query routines. They might 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 web sites running on a server. They might want to offer some functionality to all their client web sites. 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, then each application can have its own version of common assemblies. The .NET Framework also 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 global assembly cache, or GAC. The GAC is a machine-wide location for code that is to be shared among multiple applications on that machine. Typically, it is physically located at c:\winnt\assembly. However, you cannot just copy an assembly file to that directory and have it be made available to all the applications. The assembly needs to be registered with the GAC, using the .NET command-line utility GacUtil.exe.
In order to make an assembly file suitable for inclusion in the GAC, it must have assembly information compiled into it. This is done using Assembly attributes. These Assembly attributes can either be included in the same source code file as the class(es) being compiled into the assembly, or in a separate source code file that is compiled into the assembly along with the class source code file(s). The format of the attributes is dependent on the language used. In VB.NET they look like this:
In C#, the Assembly attribute looks like this:
where attributeName is the name of the Assembly attribute, and attributeValue is the string value assigned to the attribute. So, for example, if assigning the AssemblyVersionAttribute, it would look like the following in VB.NET:
<Assembly: AssemblyVersionAttribute ("22.214.171.124")>
It would look like this in C#:
[Assembly: AssemblyVersionAttribute ("126.96.36.199")]
If the project was developed using Visual Studio .NET, then this assembly information is contained in a file called AssemblyInfo.cs if the project was developed in C#, or AssemblyInfo.vb if the project was developed in VB.NET.
Table 20-17 lists the available Assembly attributes with a brief description.
If you are using Assembly attributes in a source file, you must also reference the System. Reflection namespace, using the Imports keyword in VB.NET or the using keyword in C#.
In order for an assembly to be included in the GAC, it must have a strong name. This is a cryptographically secure name that identifies an assembly by its name, version number, and a public key. A strong name can be generated using the .NET command-line utility sn.exe. To use this utility, enter at a command prompt:
sn -k outputDirectory\strongNameFile.snk
where outputDirectory is the path to the application virtual root directory, and strongNameFile.snk is the name of the file that will contain the public and private keys comprising the digital signature.
Having generated the strong name, you would add an Assembly attribute providing that strong name. In VB.NET, it appears as follows:
<Assembly: AssemblyKeyFileAttribute( _ "outputDirectory\strongNameFile.snk ")>
In C#, it takes the form:
[Assembly: AssemblyKeyFileAttribute ( @"outputDirectory\strongNameFile.snk ")]
Once all this is in place, you can use GacUtil.exe to add the assembly to the GAC. 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 at a command prompt:
Some of the more common switches are described in Table 20-18.
To use a global assembly in applications, it must be registered in the machine.config file. To add the above assembly to the machine.config file, add the following line to the <configuration><system.web><compilation><assemblies> section:
<add assembly="myDLL, Version=188.8.131.52, Culture=neutral, PublicKeyToken=nnnnnnnn"/ >
where nnnnnnnn is obtained from GacUtil by running:
from the command line, finding myDLL in the listing, and copying the public key token into place.