Previous Section  < Day Day Up >  Next Section

15.4. Application Deployment Considerations

The purpose of this section is to take a broad look at the issues that should be considered when deciding on a strategy for installing your application. Much of the discussion centers on practical issues such as how deployment is affected by the use of public or private assemblies, how configuration files are used to tell the CLR where to search for an assembly, and how configuration files ease the problems of distributing new versions of an assembly.

Microsoft Windows Deployment: XCOPY Deployment Versus the Windows Installer

Almost always included in the list of the top .NET features is the promise of using XCOPY deployment to install your application on client machines. The idea is to create a simple script that uses the XCOPY utility to copy your assemblies and resource files to the specified directories on the client's machine. It is a terrific idea and works梚f you have an application with only a few assembly files that can be stored in a local directory. However, there are a lot of installation scenarios where XCOPY does not work: It cannot be used to register a COM component in your application, and it cannot be used to store an assembly in the GAC. In addition, for a professional application, XCOPY does not provide the well-designed interface that customers expect.

The most popular alternative to XCOPY is the Microsoft Windows Installer that ships with most of its operating systems and is also available as a separate download. The installer is an installation service that processes files having a special format (.msi) and performs install operations based on their content. An .msi file is referred to as a Windows Installer Package, and an install can contain more than one of these files.

The easiest way to create an MSI file for your application is using Visual Studio .NET. It has a wizard that steps you through the process. If your application requires special Code Access Security policies, use the Configuration tool to create an install package. To do so, right-click the Runtime Security Policy node and select Create a Deployment Package. The ensuing wizard steps you through the process. You can also create a custom installer by creating a class that inherits from the System.Configuration.Install.Installer class and overrides its Install and Uninstall methods.

Core Note

.NET 2.0 and Visual Studio 2005 introduce a ClickOnce technology that is designed to simplify the installation and update of Windows applications. The idea is to create the application and use VS.NET to "publish" it to a File or Web Server. The location is provided as a URL to users who link to a Web page that provides install instructions. Included with the application is a manifest that describes the assembly and files that comprise the application. This information is used to determine whether an update is available for the application. The update process can be set up to automatically check for updates each time the application is run, or only run when requested.

ClickOnce works for straightforward installations that do not need to access the registry or install assemblies in the GAC. It is also targeted for the Microsoft Windows environment, as it contains shortcuts and an uninstall feature used by Windows. Although VS.NET is the easiest way to publish an application for installation, it can be done manually.


Deploying Assemblies in the Global Assembly Cache

.NET supports two assembly deployment models: private and shared. A private assembly is identified by its file name梬ithout the extension梐nd is stored locally with other assemblies that it references or that reference it. Shared assemblies are stored in the Global Assembly Cache. Although this is a convenient way to make an assembly available to multiple applications, it does present an installation problem. Recall from the earlier discussion that an assembly cannot be copied into the GAC; instead, a special tool is required to install or "register" it. During code development, the GACUtil.exe utility is used for this purpose. However, this tool is not included with the end-user version of the .NET Framework redistributable package, so there is no assurance that it will exist on the user's machine. Instead, use the Windows Installer.

Deploying Private Assemblies

The easiest way to deploy an application with multiple private assemblies is to place all of the assemblies in the same directory. This home directory, referred to as the application's base directory, is the first place the CLR looks when trying to locate an assembly. However, using a single directory prevents assemblies from being grouped into folders that logically describe their purpose. For example, an application may have a set of assemblies dedicated to graphics and another dedicated to database access. Placing these in \Graphics and \Data subfolders represents a more logical and easier-to-maintain code deployment.

To see how to set up a meaningful directory structure for an application, let's first look at how the CLR locates private assemblies. This discovery process, called probing, begins with the application's base directory. If the assembly is not located there, it searches for an immediate subdirectory having the same name as the target assembly. For example, if the assembly is myClass, it looks first for the directory myClass.dll and then myClass.exe. The CLR loads the assembly from the first directory in which it locates it.

In our case, we want to use a directory structure that is not based on the name of the assemblies. To force the CLR to extend its probe into other folders, we must add a special <probing> element to the application's configuration file. For an application named myApp.exe, we create a configuration file named myApp.exe.config and store it in the application's base directory. Included in the file is a <probing> tag with a privatePath attribute that specifies the subdirectories (below the base directory) for the CLR to search.


<configuration>

   <runtime>

      <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">

         <probing privatePath="\graphics;\data" />

      </assemblyBinding>

   </runtime>

</configuration>


By default, the specified search paths are relative to the application's base directory. You can also specify an absolute path, but the path must specify a folder below the application's base directory.

The application configuration file in this example is rather simple, and you may choose to build it manually. However, because the file often contains other configuration elements, manual manipulation can quickly become an unwieldy task. As an alternative, you can use the same Configuration tool described in the CAS discussion. It has an Applications folder that can be opened and will lead you through the steps to create a configuration file for a selected assembly. One of its more useful and instructive features is an option to view a list of other assemblies that the selected assembly depends on.

Using CodeBase Configuration

A <codebase> element provides another way to specify the location of an assembly. It differs from the <probing> element in that it can specify any directory on the machine, as well as a location on a file or Web server across a network. The CLR uses the URI information provided by this element to download an assembly the first time it is requested by an application. Notice how <codebase> is used in the following sample configuration file.

The <codebase> element, along with a companion <assemblyIdentity> element, is placed inside the <dependentAssembly> block. The <assemblyIdentity> element provides information about the assembly the CLR is attempting to locate: its simple name, public key token, and culture. The <codebase> element provides the assembly's version number and location where it can be found. If the assembly is not strongly named, the version information is ignored.


<configuration>

  <runtime>

    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">

     <dependentAssembly>

        <assemblyIdentity name="movieclass"

           publicKeyToken="1F081C4BA0EEB6DB"

               culture="neutral" />

        <codeBase version="1.0.0.0"  

                   href="http://localhost/scp/movieclass.dll" />

     </dependentAssembly>

    </assemblyBinding>

  </runtime>

</configuration>


The HRef can also refer to the local file system:


<codeBase version="1.0.0.0" 

                  href="file:///e:\movieclass.dll"  />


Using a Configuration File to Manage Multiple Versions of an Assembly

One of the advantages of using a strongly named assembly is the version binding feature it offers. When Assembly A is compiled against Assembly B, A is bound to the specific version of B used during the compilation. If Assembly B is replaced with a new version, the CLR recognizes this at load time and throws an exception. It is a good security feature, but can make updates to B more time-consuming because all assemblies dependent on it must be recompiled.

A configuration file can be used to override version binding by instructing the runtime to load a different version of the assembly than was originally bound to the calling assembly. This redirection is achieved by adding a <bindingRedirect> element that specifies the original version and the new version to be used in place of it. Here is how the previous configuration file is altered to have a newer version of the movieclass assembly loaded:


<configuration>

  <runtime>

    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">

      <dependentAssembly> 

        <assemblyIdentity name="movieclass"

           publicKeyToken="1F081C4BA0EEB6DB"

               culture="neutral" />

        <bindingRedirect oldVersion="1.0.0.0" 

                         newVersion="2.0.0.0"/>



        <codeBase version="2.0.0.0"  

                   href="http://localhost/scp/movieclass.dll" />



      </dependentAssembly>

    </assemblyBinding>

  </runtime>

</configuration>


Use of the <bindingRedirect> element inside an application configuration file offers a flexible and practical approach to matching applications with new or older versions of a component. For example, both versions of the component can run side by side on the same machine. One application can continue to use the older version, whereas the configuration file of a second application is modified to direct it to the newer component. Moreover, if the second application encounters problems with the new component, the <bindingRedirect> element can be removed and the application will revert to using the original component.

Assembly Version and Product Information

As a final note on deployment, it is good practice to include assembly-level attributes to your assembly to provide useful information to clients that want to examine the properties of your .exe or .dll file. As an example, the attributes in the following code yield the properties shown in Figure 15-11:


using System;

using System.Reflection;

[assembly:AssemblyVersion("2.0.0.0")] 

[assembly:AssemblyCompany("Acme Software")]

[assembly:AssemblyCopyright("Copyright (c) 2005 Stephen Perry")]

[assembly:AssemblyTrademark("")]

// Set the version ProductName & ProductVersion fields

[assembly:AssemblyProduct("Core C# Examples")]

[assembly:AssemblyInformationalVersion("2.0.0.0")]

[assembly:AssemblyTitle("Core C# movieclass.dll")]

[assembly:AssemblyDescription("This is a sample C# class")]

//

public class Movies

{

// ... Remainder of code


Figure 15-11. Attributes provide information identifying an assembly


    Previous Section  < Day Day Up >  Next Section