Versioning Issues


In COM, versioning of DLLs had some significant limitations. For example, a different DLL with the same nominal version number could be indistinguishable from the one desired.

.NET’s versioning scheme was specifically designed to alleviate the problems of COM. The major capabilities of .NET that solve versioning issues are as follows:

  • Application isolation

  • Side-by-side execution

  • Self-describing components

Application Isolation

For an application to be isolated it should be self-contained and independent. This means that the application should rely on its own dependencies for ActiveX controls, components, or files, and not have those files shared with other applications. The option of having application isolation is essential for a good solution to versioning problems.

If an application is isolated, components are owned, managed by, and used by the parent application alone. If a component is used by another application, even if it is the same version, the other application must have its own copy. This ensures that each application can install and uninstall dependencies and not interfere with other applications.

Important  

Does this sound familiar? This is what most early Windows and DOS applications did until COM required registration of DLLs in the registry and placement of shared DLLs in the system directory. The wheel surely does turn!

The .NET Framework enables application isolation by allowing developers to create application-private assemblies. These are in the application’s own directory, and if another application needs the same assembly, it can be duplicated in that application’s directory.

This means that each application is independent from the other. This isolation works best for many scenarios. It is sometimes referred to as a zero-impact deployment because by either installing or uninstalling such an application there is no chance of causing problems for any other application.

Side-by-Side Execution

Side-by-side execution occurs when multiple versions of the same assembly can run at the same time. Side-by-side execution is performed by the CLR. Components that are to execute side-by-side must be installed within the application directory or a subdirectory of it.

With application assemblies, versioning is not much of an issue. The interfaces are dynamically resolved by the CLR. You can replace an application assembly with a different version, and the CLR will load it and make it work with the other assemblies in the application, as long as the new version does not have any interface incompatibilities. The new version may even have elements of the interface that are new and that don’t exist in the old version (new properties or methods). As long as the existing class interface elements used by the other application assemblies are unchanged, the new version will work fine. In the discussion of exactly how the CLR locates a referenced assembly, which follows, you’ll learn more about how this works.

Self-Describing

In the earlier section on the manifest, the self-describing nature of .NET assemblies was mentioned. The term “self-describing” means that all the information the CLR needs to know to load and execute an assembly is inside the assembly itself.

Self-describing components are essential to .NET’s side-by-side execution. Once the extra version is known by the CLR to be needed, everything else about the assembly needed to run side-by-side is in the assembly itself. Each application can get its own version of an assembly, and all the work to coordinate the versions in memory is performed transparently by the CLR.

Versioning becomes more important with shared assemblies. Without good coordination of versions, .NET applications with shared assemblies are subject to some of the same problems as COM applications. In particular, if a new version of a shared assembly is placed in the GAC, there must be a means to control which applications get which version of a shared assembly. This is accomplished with a versioning policy.

Version Policies

As discussed earlier, a version number includes four parts: major, minor, build, and revision. The version number is used as part of the identity of the assembly. When a new version of a shared assembly is created and placed in the GAC, any of these parts can change. Which ones change affects how the CLR views compatibility for the new assembly.

When the version number of a component only changes according to its build and revision parts, it is compatible. This is often referred to as Quick Fix Engineering (QFE). It’s only necessary to place the new assembly in the GAC, and it will automatically be considered compatible with applications that were created to use the older version that had different numbers for the build and revision.

If either the major or minor build number changes, however, compatibility is not assumed by the CLR. In that case, there are manual ways to indicate compatibility if necessary, and these are covered later in this section.

When an application comes across a type that is implemented in an external reference, the CLR has to determine what version of the referenced assembly to load. What steps does the CLR go through to ensure that the correct version of an assembly is loaded? To answer this question, you need to look at version policies and how they affect what version of an assembly is loaded.

The Default Versioning Policy

Let’s start by looking at the default versioning policy. This policy is what is followed in the absence of any configuration files on the machine that modify the versioning policy. The runtime default behavior is to consult the manifest for the name of the referenced assembly and the version of the assembly to use.

If the referenced assembly does not contain a strong name, then it is assumed that the referenced assembly is application-private and is located in the application’s directory. The CLR takes the name of the referenced assembly and appends .dll to create the filename that contains the referenced assembly’s manifest. The CLR then searches in the application’s directory for the filename; if it’s found, it uses the version indicated, even if the version number is different from the one specified in the manifest. Therefore, the version numbers of application-private assemblies are not checked, because the application developer, in theory, has control over which assemblies are deployed to the application’s directory. If the file cannot be found, the CLR raises a System.IO.FileNotFoundException.

Automatic Quick Fix Engineering Policy

If the referenced assembly contains a strong name, then the process by which an assembly is loaded is different:

  1. The three different types of assembly configuration files (discussed later) are consulted, if they exist, to see whether they contain any settings that will modify which version of the assembly the CLR should load.

  2. The CLR then checks whether the assembly has been requested and loaded in a previous call. If it has, it uses the loaded assembly.

  3. If the assembly is not already loaded, then the GAC is queried for a match. If a match is found, it is used by the application.

  4. If any of the configuration files contains a codebase (discussed later) entry for the assembly, then the assembly is looked for in the location specified. If the assembly cannot be found in the location specified in the codebase, a TypeLoadException is raised to the application.

  5. If there are no configuration files or if there are no codebase entries for the assembly, then the CLR probes for the assembly starting in the application’s base directory.

  6. If the assembly still isn’t found, then the CLR asks the Windows Installer service if it has the assembly in question. If it does, then the assembly is installed and the application uses it. This is a feature called on-demand installation.

If the assembly hasn’t been found by the end of this entire process, then a TypeLoadException is raised.

Although a referenced assembly contains a strong name, this does not mean that it has to be deployed into the GAC. This enables application developers to install a version with the application that is known to work. The GAC is consulted to see whether it contains a version of an assembly with a higher build.revision number to enable administrators to deploy an updated assembly without having to reinstall or rebuild the application. This is known as the Automatic Quick Fix Engineering Policy.

Configuration Files

The default versioning policy described earlier may not be the most appropriate policy for your requirements. Fortunately, you can modify this policy through the use of XML configuration files to meet your specific needs. Two types of configuration files can hold versioning information:

  • The first is an application configuration file, and it is created in the application directory. As the name implies, this configuration file applies to a single application only. You need to create the application configuration file in the application directory with the same name as the application filename, appending .config. For example, suppose that you have a Windows Forms application called HelloWorld.exe installed in the C:\HelloWorld directory. The application configuration file would be C:\HelloWorld\HelloWorld.exe.config.

  • The second type of configuration file is called the machine configuration file. It is named machine.config and can be found in the C:\Windows\Microsoft.NET\Framework\ v2.0.xxxx\ CONFIG directory. The machine.config file overrides any other configuration files on a machine and can be thought of as containing global settings.

The main purpose of the configuration file is to provide binding-related information to the developer or administrator who wishes to override the default policy handling of the CLR.

Specifically, the configuration file, as it’s written in XML, has a root node named <configuration>, and it must have the end node of </configuration> present to be syntactically correct. The configuration file is divided into specific types of nodes that represent different areas of control. These areas are as follows:

  • Startup

  • Runtime

  • Remoting

  • Crypto

  • Class API

  • Security

Although all of these areas are important, this chapter covers only the first two. All of the settings discussed can be added to the application configuration file. Some of the settings (these are pointed out) can also be added to the machine configuration file. If a setting in the application configuration file conflicts with one in the machine configuration file, then the setting in the machine configuration is used. When we talk about assembly references in the following discussion of configuration settings, we are talking exclusively about shared assemblies (which implies that the assemblies have a strong name, as assemblies in the GAC are required to have one).

Startup Settings

The <startup> node of the application and machine configuration files has a <requiredRuntime> node that specifies the runtime version required by the application. This is because different versions of the CLR can run on a machine side by side. The following example shows how you would specify the version of the .NET runtime inside the configuration file:

  <configuration>   <startup>     <requiredRuntime version ="2.0.xxxx" safemode ="true"/>   </startup> </configuration> 

Runtime Settings

The runtime node, which is written as <runtime> (not to be confused with <requiredRuntime>), specifies the settings that manage how the CLR handles garbage collection and versions of assemblies. With these settings, you can specify which version of an assembly the application requires, or redirect it to another version entirely.

Loading a Particular Version of an Assembly

The application and machine configuration files can be used to ensure that a particular version of an assembly is loaded. You can indicate whether this version should be loaded all the time or only to replace a specific version of the assembly. This functionality is supported through the use of the <assemblyIdentity> and <bindingRedirect> elements in the configuration file, as shown in the following example:

  <configuration>   <runtime>     <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">       <dependentAssembly>         <assemblyIdentity name="AssemblyName"                           publickeytoken="77a5c561934e089"                           culture="en-us"/>           <bindingRedirect oldVersion="*"                            newVersion="2.0.50.0"/>       </dependentAssembly>     </assemblyBindings>   </runtime> </configuration> 

The <assemblyBinding> node is used to declare settings for the locations of assemblies and redirections via the <dependentAssembly> node and the <probing> node (which you will look at shortly).

In the last example, when the CLR resolves the reference to the assembly named AssemblyName, it loads version 2.0.50.0 instead of the version that appears in the manifest. If you want to load only version 2.0.50.0 of the assembly when a specific version is referenced, then you can replace the value of the oldVersion attribute with the version number that you would like to replace (for example, 1.5.0.0). The publickeytoken attribute is used to store the hash of the strong name of the assembly to replace. This ensures that the correct assembly is identified. The same is true of the culture attribute.

Defining the Location of an Assembly

The location of an assembly can also be defined in both the application and machine configuration files. You can use the <codeBase> element to inform the CLR of the location of an assembly. This enables you to distribute an application and have the externally referenced assemblies downloaded the first time they are used (on-demand downloading):

  <configuration>   <runtime>     <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">       <dependentAssembly>         <assemblyIdentity name="AssemblyName"                           publickeytoken="b77a5c561934e089"                           culture="en-us"/>         <codeBase version="2.0.50.0"                   href="http://www.wrox.com/AssemblyName.dll/>       </dependentAssembly>     </assemblyBindings>   </runtime> </configuration> 

You can see from this example that whenever a reference to version 2.0.50.0 of the assembly AssemblyName is resolved (and the assembly isn’t already on the user’s computer), the CLR will try to load the assembly from the location defined in the href attribute. The location defined in the href attribute is a standard URL and can be used to locate a file across the Internet or locally.

If the assembly cannot be found or the details in the manifest of the assembly defined in the href attribute do not match those defined in the configuration file, the loading of the assembly will fail and you will receive a TypeLoadException. If the version of the assembly in the preceding example is actually 2.0.60.0, then the assembly will load because the version number is only different by build and revision number.

Providing the Search Path

The final use of configuration files to look at is that of providing the search path for use when locating assemblies in the application’s directory. This setting only applies to the application configuration file. By default, the CLR will search for an assembly only in the application’s base directory - it will not look in any subdirectories. You can modify this behavior by using the <probing> element in an application configuration file, as shown in the following example:

  <configuration>   <runtime>     <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">       <probing privatePath="regional"/>     </assemblyBinding>   </runtime> </configuration> 

The privatePath attribute can contain a list of directories relative to the application’s directory (separated by a semicolon) that you would like the CLR to search in when trying to locate an assembly. The privatePath attribute cannot contain an absolute pathname.

As part of an assembly reference being resolved, the CLR checks in the application’s base directory for it. If it cannot find it, then it looks through, in order, all the subdirectories specified in the privatePath variable, as well as looking for a subdirectory with the same name as the assembly. If the assembly being resolved is called AssemblyName, then the CLR also checks for the assembly in a subdirectory called AssemblyName, if it exists.

This isn’t the end of the story, though. If the referenced assembly being resolved contains a culture setting, then the CLR also checks for culture-specific subdirectories in each of the directories it searches in. For example, if the CLR is trying to resolve a reference to an assembly named AssemblyName with a culture of en and a privatePath equal to that in the last example, and the application being run has a home directory of C:\ExampleApp, then the CLR will look in the following directories (in the order shown):

  • C:\ExampleApp

  • C:\ExampleApp\en

  • C:\ExampleApp\en\AssemblyName

  • C:\ExampleApp\regional\en

  • C:\ExampleApp\regional\en\AssemblyName

As you can see, the CLR can probe quite a number of directories to locate an assembly. When an external assembly is resolved by the CLR, it consults the configuration files first to determine whether it needs to modify the process by which it resolves an assembly. As discussed, you can modify the resolution process to suit your needs.




Professional VB 2005 with. NET 3. 0
Professional VB 2005 with .NET 3.0 (Programmer to Programmer)
ISBN: 0470124709
EAN: 2147483647
Year: 2004
Pages: 267

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net