ASP.NET Deployment Issues, I of II...
Part I of II: Introduction and Shared Assembly Issues.
Knowledge assumed: ASP.NET configuration, some experience with building components with VS.NET and ADO.NET.
Once you have developed and tested your web application the next step is to deploy the web application to the production environment. Via two articles we’re going to look at issues surrounding deployment. The first article will serve as an introduction examining how .NET AND VS.NET makes the deployment process simpler than in the past and look at some of the issues surrounding the deployment of more complex web applications via an example. The second article will focus on the support that Visual Studio .NET provides to the deployment process for more complex applications – primarily the inbuilt facility to create Windows Installer based installation packages via the Setup Project option.
The .NET model simplifies the deployment model by allowing 'XCOPY deployment', i.e. simply copying to the server, by whatever access method, will deploy the application. Nice headline concept ... if only life was always that simple. Dependent on the application implementation this will not always be possible, as we'll investigate shortly. However, 'DLL hell' has disappeared with the new model, which will be a great relief to both developers and system administrators.
For applications that are packaged and shipped to the user, Microsoft recommends the use of Microsoft Windows installer technology. In this case the user would be a customer wishing then to deploy this application to a new web server. Scenarios include the situation where the application is a 3rd party product or where the installation package is produced by the development team for installation on production servers by the system admin team. The particular project type we are concerned with in the Visual Studio .NET environment is the Windows Installer-based Setup and Deployment project.
There are a variety of deployment tools available to the Microsoft developer. Choosing the appropriate deployment tool for a particularly project is important and dependent on the nature of your application. XCOPY/ FTP will suffice for simple applications – for more sophisticated requirements it may well be preferable to create a Windows Installer based setup package. The choice will also be dependent on the level of access you have to the production sever environment and the development/ deployment tools you have at your disposal.
For a .NET application that only uses managed code and private assemblies (those in the /bin directory of your application), the application can be installed by just copying all files to the desired destination. Note particularly the restriction of private assemblies. Despite the attention Microsoft directed towards the XCOPY deployment 'feature' at time of release of .NET, there are many common installation tasks that are not possible via XCOPY, some of which are:
However, if you do need any of these you have Microsoft Windows Installer at your disposal.
Note that there are several mechanisms of XCOPY deployment whether by a mapped drive to the production machine from another machine you have access to, FrontPage extensions or FTP.
This is an installation and configuration service giving control over installation of an application, a component or an update. In addition to the provided built in actions such as installing files, creating shortcuts to files, making Start menu entries and writing registry entries it offers custom actions which allows for several common installation tasks such as running of a SQL script to install and configure a database during the installation.
The Windows installer service manages all installed components on a system by keeping a database of information about every application that it installs including files, registry keys and components. The windows installer .msi package, which is the installation program itself, includes a number of database tables that describes the application to the Windows installer service that this service uses along with other package contents to install the application.
VS.NET provides one of several ways of producing a Windows Installer package and this will be the main focus of part II of this series of 2 articles.
You will have noticed above that a major proviso of XCOPY deployment was that any components used were provided by private (aka local) assemblies. I'm now going to explore the issues around this proviso.
Each web application under an instance of IIS can have its own directory for components (\bin) and it's own XML based configuration file (web.config).
Each ASP.NET web application maintains a local assembly cache, generated from an assembly in which is contained multiple DLLs. By default an application is configured to use the \bin directory of the applications root as the assembly source.
When a web application is started, the .NET Framework instantiates a new instance of the System.AppDomain class. This class enables multiple web applications to run in the same process via the following:
When the AppDomain instance is constructed it creates in-memory shadow copies of the DLLs that exist in the \bin directory. Thus the actual DLLs are not locked. Further, the .NET Framework monitors the original DLLs and if they change a new shadow copy is created from the changed file as soon as existing requests have been processed.
This model dramatically simplifies the process of DLL registration from classic ASP. You now simple copy the components to the \bin directory. Further you can now have multiple versions of the same DLL resident on the same machine, each being used by a different application.
What about the situation where multiple applications want to use the same version of the same assembly? We don't really want multiple copies of the same assembly existing in multiple applications. The solution is to use the global assembly cache (GAC).
As shared assemblies are all installed at this central location distinguishing them simply by filename provides an insufficient method of identification – you could have the situation where one assembly overwrites another 'breaking' applications installed on the web server machine. Microsoft avoids this possibility by requiring that you assign a 'strong name' to the assembly before placing it in the GAC. We'll return to this after completing our process overview.
Once you have associated a strong name with the assembly you can place it in the GAC. This can be achieved in several ways:
As just introduced we have to associate a strong name with an assembly prior to placing it in the GAC. A strong name strengthens an assembly's identity by qualifying it with the author/ publisher's identity. The .NET Framework uses the standard cryptography technique of digital signing to achieve this.
The process of digital signing involves two related pieces of binary data known as public and private keys. The public key represents the author or publisher's identity and is freely distributed. In creating the strong name this public key is stored in the assembly manifest along with other identification information such as the name, version name and culture of the assembly. Not too secure so far as everyone knows the public key so to verify that only the legitimate owner of the public key has created the assembly it is 'signed' using the author/ publisher's private key which is assumed to only be known to the publisher of the assembly.
Thus the process of signing an assembly is as follows:
A signature is created by computing a cryptographic hash from the contents of the assembly. The hash is encoded with the private key. This signature is then stored within the assembly.
and verifying the signature works as follows:
The CLR will verify an assembly's identity before it is used - it reads its public key from the assembly manifest and uses it to decrypt the cryptographic hash stored in the assembly. It will then recalculate the hash for the current contents of the assembly. If the two hashes match it ensures two things:
The necessary public/ private key pairs can be easily generated using the Strong Name tool (sn.exe) available in the .NET Framework SDK, as we'll now see as part of the process of building a versioned assembly.
I'm first going to look at the process as it is unsupported by VS.NET and hopefully this will give a little greater insight into what is going on. I'll follow this up with a brief look at the VS.NET process in part II of this series of 2 articles in the context of using the Windows Installer tools in VS.NET. Hence here we shall use the GacUtil.exe for the assembly registration in addition to the command line compiler.
So, (assume) you have written a class or classes. In order to compile the code into a versioned assembly we must define assembly attributes. This can be done in the same file as the class, but it is preferable to keep the information separate in another file and compile them together.
Let's introduce some simple code to provide a working example. We're going to build a versioned assembly called myData which will expose a very simple class with a single method that binds a dataList to the data from an XML file which is a list of companies. Code as follows:
myDataClass.vb:
|
Imports System Imports System.Web.UI.WebControls Imports System.Data Imports System.Xml Imports System.Data.SqlClient Namespace myData Public Class myDataClass Public Sub getData(ByVal _myGrid As DataGrid) Dim dsCompanies As DataSet = New DataSet dsCompanies.ReadXml("[path to app dir]\Companies.xml") _myGrid.DataSource = dsCompanies End Sub End Class End Namespace |
It doesn’t matter a great deal exactly what form the XML file takes as long as it is syntactically correct. Here's an example for you for completeness:
|
<?xml version="1.0" standalone="yes" ?> - <Companies> <Company name="Argos" Employees="12343" turnover="237453274" /> <Company name="WRU" Employees="132" turnover="27474" /> <Company name="NHS" Employees="123443" turnover="237453274" /> <Company name="DHL" Employees="54645" turnover="847545" /> <Company name="ICI" Employees="23324" turnover="978556" /> </Companies> |
To compile this class into a versioned assembly we must define the required attributes of the assembly. This can be done in the same file as the class definition but here we’ll implement via a separate file, as follows:
myDataAssemblyInfo.vb:
|
Imports System.Reflection Imports System.Runtime.CompilerServices <Assembly: AssemblyTitle("DotNetJohn Deployment I")> <Assembly: AssemblyDescription("Data access for the application")> <Assembly: AssemblyCompany("www.cymru-web.net")> <Assembly: AssemblyProduct("NA")> <Assembly: AssemblyCopyright("Copyright June 2003")> <Assembly: AssemblyVersion("1.0.0.0")> <Assembly: AssemblyDelaySign(false)> <Assembly: AssemblyKeyFile("[path to app dir]\myKey.snk")> |
This file will be compiled with our class to create an assembly (myData.dll). Most of the definitions are self-explanatory. Note that the version number is of structure [major].[minor].[build].[revision]. Also i ncluded is the AssemblyDelaySign(false) attribute to indicate that we do not want to delay applying a signature to our assembly (useful when the assembly is still under development). A signature is required to register it with the GAC, as already discussed. The signature is generated when the assembly is compiled based on a private key, which is defined via the last line of code above – you should amend the path as appropriate.
We thus need to create a private key file for what is termed a Strong Named Assembly which is identified by it's text name, version number, and culture information (if provided), plus a public key and a digital signature. The .NET Framework SDK provides a tool, sn.exe, for generating keys used to generate a digital signature when the assembly is compiled.
The key that is generated ensures that the assembly name is unique, hence the term 'strong named'. This is how the system tracks the assembly. Since no two private keys are generated the same, two different assemblies cannot have the same strong name.
To generate the private key to be used when compiling your assembly:
sn.exe –k [path to app dir]\myKey.snk
where –k indicates a new key file should be generated.
When you have executed this code the key file myKey.snk should have been added to your application directory.
We now have all the pieces in place to compile the strong named assembly: the assembly information file, the class file and the private key file. Assuming that all the necessary files are located in the current application directory the following command and parameters will be appropriate:
vbc.exe /target:library /out:myData.dll myDataClass.vb myDataAssemblyInfo.vb /r:System.dll /r:system.data.dll /r:System.XML.dll /r:System.Web.dll
If you view the properties of the DLL created the version information should match that you gave it.
You now need to register the assembly with the GAC. This is where the GacUtil.exe tool enters the fray. It adds the assembly to the global assembly cache using its strong name. It is strongly recommended to choose a logical location and directory structure for your assemblies. For example you have an Assemblies directory with a myDatav1 directory so that when version 2 comes along you can create a new directory for the new version of the assembly/ dll. Thus you'll avoid any potential overwriting of assemblies still in use by applications within your system.
Then from the command prompt:
gacutil.exe /i c:\assemblies\myClassv1\myData.dll
where the /i option tells the utility to install the assembly represented in the DLL into the global assembly cache.
You can verify all has happened as it should with the gacutil.exe /l option (list).
All being well you should now be able to use the registered assembly but before we can use the myClass namespace we have now setup we must add a reference to it in the machine-wide configuration file machine.config, in the <configuration><system.web><compilation><assemblies> section:
<add assembly "myData, Version=1.0.0.0, Culture=neutral, PublicKeyToken=[value from GacUtil] /l" />
machine.config is by default installed at:
c:\winnt\Microsoft.NET\Framework\[version number]\config\machine.config
NOTE: take care when editing the machine.config file (take a back up first). Alternatively setup the assembly from the applications that use it via the web.config file.
Finally, a restart of IIS is required for changes to machine.config to take effect. This can be achieved via issuing an IISRESET at the command prompt or via the IIS MMC snapin.
This minor procedure makes the namespace available to all applications on the machine. The public key token is an 8-byte hash of the private key used to build the assembly.
We can now import the namespace into our code as per any other namespace.
To add a new assembly version to the GAC you go through the process again, specifying a different AssemblyVersion. If you use the same private key, the only distinguishing factor between old and new assemblies will be the version number.
How do you use specific versions of the assembly? If you don't alter the machine.config file further existing code will use the existing assembly as the earlier version will remain as the default version. However, if we simply changed machine.config to use version 2 of the assembly this might break existing applications. Hence we don't touch machine.config - we configure the local application configuration file (web.config), for example:
<runtime>
<assemblyBinding xmlns”urn:wschemas-microsoft-com:asm.v1>
<dependentAssembly>
<assemblyIdentity name="myData" publicKeyToken="[value from GacUtil /l]"/>
<bindingRedirect oldversion=”1.0.0.0” newVersion=”2.0.0.0 />
</dependentAssembly>
</assemblyBinding>
</runtime>
This is termed an assembly redirect. Note you can also use assembly redirection in machine.config for machine wide changes … as long as you know it will not break existing applications.
In this article we've introduced the topic of deployment looking at some of the pertinent issues before providing an overview of the practicalities of how you manually deploy an assembly to the GAC. In part II of this series of two articles we'll look at the VS.NET tools available to produce windows installer packages, including the facilities to install assemblies to the GAC. As you might hope and expect VS.NET makes the process simpler for the developer.
ASP.NET: Tips, Tutorial and Code
Scott Mitchell et al.
Sams
Developing and Implementing Web Applications with VB.NET and VS.NET
Mike Gunderloy
Que