Using Configuration Files with Shared Assemblies

You can still use the configuration files with shared assemblies. In fact, because the GAC can hold multiple versions of an assembly, configuration files become extremely useful for indicating the version that should be used with a particular client.

By default, a client will use only a strongly named assembly if it has the exact same version number as the assembly it was compiled with. If the client cannot find a suitable assembly, a TypeLoadException or FileNotFoundException will occur.

Every .NET application defines the components it requires in its manifest, which is a special portion of assembly metadata at the beginning of the compiled file. You can view this information using a handy utility included with the .NET Framework called ILDasm.exe (short for IL Disassembler). You can typically find this utility in a directory such as C:\Program Files\Microsoft Visual Studio .NET\FrameworkSDK\Bin.

Figure 2-6 shows ILDasm.exe viewing an ordinary Windows application that contains a single form. Using the assembly metadata, ILDasm.exe can determine all the classes it contains and provide information about their methods, properties, and events.

Figure 2-6. Viewing an assembly with ILDasm.exe

graphics/f02dp06.jpg

If you double-click the MANIFEST entry in the tree, you'll see a text listing like the one shown in Figure 2-7. The .assembly extern statements at the beginning of the manifest indicate the dependent assemblies and the required version numbers. In this case, the only dependencies are for some of the core assemblies included with the .NET Framework and the custom DBComponent. Because these are strong-named assemblies, the .assembly extern statements include a public key token, which is a hash of the assembly's public key.

Figure 2-7. Viewing assembly dependencies in the manifest

graphics/f02dp07.jpg

Verifying References at Run Time

Public keys are quite large. The CLR uses a hashing algorithm to create a smaller hash value, which it stores in the manifest. When you run the application, .NET locates the appropriate assembly, extracts its public key, runs the hashing algorithm, and compares the result with the value in the manifest. If these values don't match, the code isn't allowed to execute.

Conceptually, the hash value is a little like a checksum it represents a larger value but doesn't contain all the original information. Unlike a checksum, the algorithm used to create a hash is complex enough that it can't be reverse-engineered. In other words, you can't create another document that will satisfy a given hash value without significant computing effort (on the scale of breaking an encrypted cipher).

.NET uses a similar technique to verify that a strong-named assembly hasn't been tampered with before loading it. In this case, the CLR hashes the contents of the assembly and compares them with a hash value embedded in the manifest. This hash value is even more difficult to tamper with because it is encrypted at compile time using your private key.

If the application can't find the required version of a dependent assembly in the GAC, it throws a TypeLoadException or FileNotFoundException (as shown in Figure 2-8). If there are multiple versions of the dependent assembly in the GAC, the application automatically selects the one it was compiled with.

Figure 2-8. Failing to find the correct version of a strong-named assembly

graphics/f02dp08.jpg

Note

In an early beta version, .NET supported a feature called Quick Fix Engineering (QFE). Under this system, the assembly loader would require a match only for the major and minor revision numbers and would use the assembly with the highest build and revision numbers. Unfortunately, this system wasn't strict enough. Therefore, the release versions of .NET 1.0 and 1.1 require component versions to match exactly, guaranteeing that DLL Hell can never return.


If you want the application to use a different version of a dependent assembly, you have two options. First, you can recompile the client with a new version. This way, the new version number will be embedded in the client's manifest. Alternatively, you can apply a new version policy.

Versioning Policies with Strong-Named Assemblies

If you need to override the default binding behavior after an application has been deployed (either to direct a client or to use an older or newer version than it would use by default), you must create a configuration file. In the configuration file, you identify the assembly by name and by its public key token (which is displayed in the GAC). You can then add one or more <bindingRedirect> tags. Each redirect maps a single requested version, or a range of requested versions, to a specific new version. This version must be present in the GAC; otherwise, the application will throw an exception on startup.

The configuration file in Listing 2-5 maps versions 1.0.0.0 through 2.9.9.9 of a shared assembly named DBComponent to version 3.0.0.0.

Listing 2-5 Redirecting an assembly reference to a new version
 <?xml version="1.0"?> <configuration>   <runtime>     <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">       <dependentAssembly>         <assemblyIdentity name="DBComponent"                           publicKeyToken="b03f5f7f11d50a3a"/>         <bindingRedirect oldVersion="1.0.0.0-2.9.9.9"                          newVersion="3.0.0.0" />       </dependentAssembly>     </assemblyBinding>   </runtime> </configuration> 

Remember, you can use this technique only with assemblies that have strong names. Otherwise, .NET ignores the version information and assumes that any local assemblies are compatible.

Code Bases with Shared Assemblies

Shared assemblies also give you additional power with the <codeBase> element. Namely, you can specify any file or URL location from which the assembly should be downloaded. This little-considered feature enables you to partially solve deployment problems by allowing the client computer to automatically download the version of an assembly that it needs, according to the configuration file of the client application it is running.

Listing 2-6 automatically downloads version 2.0.0.0 of a component from a Web site (provided the client attempts to use this component).

Listing 2-6 Automated deployment through downloading
 <configuration>    <runtime>       <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">          <dependentAssembly>             <assemblyIdentity name="DBComponent"                               publicKeyToken="b03f5f7f11d50a3a"/>             <codeBase version="2.0.0.0"                       href="http://www.prosetech.com/DBComponent.dll"/>          </dependentAssembly>       </assemblyBinding>    </runtime> </configuration> 

Note that when you use the <codeBase> tag with a shared assembly, you need to include the URI at the beginning of the address. Therefore, if you want to use a file path, you do it in the form file:///g:\Components\DBComponent.dll or http://MyServer/Components/DBComponent.dll. If the assembly is private, the <codeBase> tag must use a path relative to the application directory.

Downloading code in this manner can result in your assembly being placed into a lower security context and not being able to perform the work it needs to do. Before you can successfully use this technique to download assemblies from another computer, you need to modify code-access security settings, as described in Chapter 15. Chapter 15 also provides more information about automated deployment and downloading and presents some flexible techniques that don't require strong names.

Note

You can create and tweak configuration files using the .NET Framework Configuration tool that is included with the SDK. Just choose Settings, Control Panel, Administrative Tools, Microsoft .NET Framework Configuration from the Start menu. Among other things, this tool enables you to configure versioning policies and code bases for an application.


A Final Word About Assembly Binding

The assembly binding process can be complex, particularly for shared components. Figure 2-9 shows a high-level overview of the process.

You can use other .NET Framework features to configure how assembly binding takes place in even greater detail. For example, you can develop publisher policy or machine policy files that specify more generic assembly binding rules. However, these options (which are described on MSDN) continuously raise new versioning headaches. In a distributed application, the best deployment approaches don't require per-client configuration, which can complicate the setup immensely and introduce new problems when components are updated in the future.

Ideally, you will sidestep these issues by using private assemblies whenever possible. In a perfect world, the only consumer of a shared assembly will be a server-side component. This ensures that you can apply new version policies, if necessary, in a controlled environment. In a world with distributed clients, enforcing these policies might not be as easy.

Figure 2-9. The assembly binding process

graphics/f02dp09.jpg

If you encounter an assembly binding problem that you can't resolve, you can use the Assembly Binding Log Viewer utility included with Visual Studio .NET (look for the file fuslogvw.exe, which you can run from the command line). This utility can be configured to track failed assembly binding attempts for applications in a specific directory, or across the entire computer.



Microsoft. NET Distributed Applications(c) Integrating XML Web Services and. NET Remoting
MicrosoftВ® .NET Distributed Applications: Integrating XML Web Services and .NET Remoting (Pro-Developer)
ISBN: 0735619336
EAN: 2147483647
Year: 2005
Pages: 174

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