Assemblies are the basic unit of deployment for managed applications, including ASP.NET applications. Assemblies are used in ASP.NET in several ways, each of which has different implications for deployment. The three categories of assembly are described below. The steps required to deploy application-specific and global assemblies appear in subsequent sections.
Includes assemblies that are dynamically generated by the ASP.NET runtime for each .aspx page in your application, as well as for code-behind files identified by the src attribute of the @ Page directive, and for global.asax and .ascx user control files. Because these assemblies are generated at runtime automatically, no action is required on the part of the developer at deployment time.
Includes assemblies resulting from the compilation of code-behind files, whether through building an application in Visual Studio .NET or using the command-line compilers. This category also includes compiled assemblies containing business logic code, and/or assemblies containing custom server controls. All application-specific assemblies should reside in the application's bin subdirectory (also known as the local assembly cache), which allows your application to locate and use the assembly's members. There they are automatically loaded into memory by the ASP.NET runtime. Because these assemblies reside within the folder tree of the application, when you deploy the application using XCOPY, FTP, or Windows Explorer, they are copied with the rest of the files in your application and available to the deployed application automatically. Additionally, the ASP.NET runtime automatically modifies the IIS permissions on the local assembly cache directory to deny all HTTP access to the directory, which prevents anyone from reading or accessing assemblies directly.
Includes any assemblies that are shared across all applications on a machine by installing them in the global assembly cache (GAC). Assemblies must be strongly named in order to be installed in the GAC and can be versioned to support side-by-side installation of multiple versions of the same assembly.
As noted in the preceding section, deploying application-specific assemblies is as simple as deploying other application files. As long as the assemblies reside in the bin subdirectory of the application, no further action is required on the part of the developer apart from copying the application's files to the target server.
The magic of the bin directory is enabled by the <assemblies> configuration element in machine.config, which contains the following <add> element by default:
<add assembly="*" />
This element tells the ASP.NET runtime to load any assemblies residing in the bin subdirectory for use by the application.
Another important point is that when assemblies are loaded by the ASP.NET runtime, the actual physical DLL containing the assembly is never loaded into memory. Instead, the ASP.NET runtime makes a shadow copy of the DLL and loads and locks it on the disk instead (while setting up a file monitor on the original DLL file). This means that you can update the .NET assemblies associated with your application at any time, without the need to shut down IIS or restart the server. The ASP.NET runtime automatically detects when an assembly is updated and serves all new requests using the new version of the assembly. This makes it significantly easier to maintain and update applications with a minimum of downtime.
If you use code-behind for your page-specific logic, you can deploy only the .aspx pages and the precompiled assembly (or assemblies) for your code-behind files, without deploying the code-behind files. This allows you to run your application while protecting the source code contained in the actual code-behind files.
Some have argued that, as with Java bytecode, the Intermediate Language (IL) contained in .NET managed assemblies is readily decompiled into a managed language such as C#. Thus, even if you do not deploy the code-behind files for an application, it may still be possible for someone to derive this code from the assemblies by using an IL decompiler, assuming that they could get access to the assemblies, which would require either physical access to the machine, or would require the attacker to exploit a vulnerability that allowed access to the filesystem.
The global assembly cache (GAC) provides a centralized location for the storage of assemblies that are to be shared across applications on a given machine. As noted previously, to be stored in the GAC, an assembly must be named strongly. This process is outlined in this section. Because of this requirement, the extra deployment effort entailed, and the fact that global assemblies are available to all applications on a machine by default, you should only use the GAC for assemblies that fit the following profile:
They need to be shared by many applications.
The effort of maintaining and/or updating individual local copies of the assembly for each application outweighs the effort of strongly naming and deploying the assembly to the GAC.
The first step in deploying an assembly to the GAC is to provide the assembly with a strong name. This is a three-step process:
Generate a cryptographic key pair using the sn.exe command line tool:
sn -k keyPair.snk
To see all the options for the sn.exe tool, run sn /? at a command prompt.
Add an assembly-level AssemblyKeyFile attribute. You can also optionally add an AssemblyVersion attribute to provide a version number for the assembly. The * in the following version number example auto-increments the last two of the four version number parts with each compilation:
' VB.NET <Assembly: AssemblyKeyFile("keyfile.snk")> <Assembly: AssemblyVersion("0.1.*")> // C# [assembly: AssemblyKeyFile(@"..\..\sgKey.snk")] [Assembly: AssemblyVersion("0.1.*")]
Compile the assembly using the appropriate command-line compiler. Note that the key pair should be copied to the location of the code files to be compiled, since this location is where the compiler will look for the file based on the AssemblyKeyFile attribute shown previously.
Once the assembly has been strongly named, you can install it into the GAC in any one of three ways:
Use the gacutil.exe command-line tool to install (or uninstall) an assembly into the GAC, as follows:
gacutil -i myAssembly.dll
Note that this syntax assumes that gacutil.exe is called from the directory containing the assembly's physical file. The gacutil.exe utility is recommended only for development systems.
Use Windows Explorer to drag and drop a copy of the assembly to the GAC. Access to the GAC is provided via a Windows Explorer shell extension, which displays the contents of the GAC as the folder %windir%\Assembly.
Use Microsoft Windows Installer 2.0 to create an installation package that will install the assembly or assemblies to the GAC. This installation ensures easier rollback of assemblies and other benefits. See the Windows Installer 2.0 documentation for information on creating Installer packages.
What About COM Components?
If you're using only managed assemblies in your application, the deployment picture is pretty rosy. It's considerably easier to deploy, update, and maintain managed assemblies than it was to maintain COM components in classic ASP. But what if your application requires you to use COM components through the .NET COM interoperability layer?
The good news is that if the version of the COM component you are using is already installed on the target machine, you should need to deploy only the runtime-callable wrapper (RCW) assembly for the component, since that assembly contains the information necessary to locate the component based on information in the registry.
However, if the COM component is a custom component or is not installed on the target system, you need to deploy the COM component to the target system and register it using regsvr32.exe as follows:
Note that you would replace <dllname> with the name of the component to register.