dot.NET.Framework.Essentials.1002003,.3Ed [Electronic resources] نسخه متنی

اینجــــا یک کتابخانه دیجیتالی است

با بیش از 100000 منبع الکترونیکی رایگان به زبان فارسی ، عربی و انگلیسی

dot.NET.Framework.Essentials.1002003,.3Ed [Electronic resources] - نسخه متنی

Hoang Lam; Thuan L. Thai

| نمايش فراداده ، افزودن یک نقد و بررسی
افزودن به کتابخانه شخصی
ارسال به دوستان
جستجو در متن کتاب
بیشتر
تنظیمات قلم

فونت

اندازه قلم

+ - پیش فرض

حالت نمایش

روز نیمروز شب
جستجو در لغت نامه
بیشتر
لیست موضوعات
توضیحات
افزودن یادداشت جدید










4.1 Deployment Options


For a simple program like
hello.exe that we built in Chapter 2, deployment is easy: copy the assembly into a
directory, and it's ready to run. When you want to
uninstall it, remove the file from the directory. However, when you
want to share components with other applications,
you've got to do some work.

In COM, you must store activation and marshaling[2] information in the registry for
components to interoperate; as a result, any COM developer can
discuss at length the pain and suffering inherent in COM and the
system registry. In .NET, the system registry is no longer necessary
for component integration.

[2] Distributed applications require a communication layer to
assemble and disassemble application data and network streams. This
layer is formally known as a marshaler in Microsoft terminology.
Assembling and disassembling an application-level protocol network
buffer are formally known as marshaling and unmarshaling,
respectively.


In the .NET environment, components can be
private, meaning that they are unpublished and
used by known clients,
or
shared,
meaning that they are published and can be used by any clients. This
section discusses several options for deploying private and shared
components.


4.1.1 Private Components


If
you
have private components that are used only by specific clients, you
have two deployment options. You can store the private components and
the clients that use these components in the same directory, or you
can store the components in a component-specific
directory that the client can access.
Since these clients use the exact private components that they
referenced at build time, the CLR doesn't support
version checking or enforce version policies on private components.

To install your applications in either of these cases, perform a
simple
xcopy
of your application files from the source installation directory to
the destination directory. When you want to remove the application,
remove these directories. You don't have to write
code to store information into the registry, so
there's no worrying about whether
you've missed inserting a registry setting for
correct application execution. In addition, because nothing is stored
in the registry, you don't have to worry about
registry residues.

4.1.1.1 One-directory deployment


For simplicity, you can place supporting assemblies in the same
directory as the client application. For example, in Chapter 3, we placed the vehicle, car, and plane assemblies
in the same directory as the client application,
drive.exe. Since both the client application and
supporting assemblies are stored within the same directory, the CLR
has no problem resolving this reference at runtime (i.e., find and
load plane.dll and activate the Plane class). If
you move any of the DLLs to a different directory (e.g., put
vehicle.dll in c:\temp),
you will get an exception when you execute
drive.exe. This is because the CLR looks for the
vehicle assembly in the following order:

It looks for a file called vehicle.dll within
the same directory as drive.exe.

Assuming that the CLR hasn't found
vehicle.dll, it looks for
vehicle.dll in a subdirectory with the same name
as the assembly name (i.e., vehicle).

Assuming that the CLR hasn't found the vehicle
assembly, it looks for a file called vehicle.exe
in the same directory as drive.exe.

Assuming that the CLR hasn't found
vehicle.exe, it looks for
vehicle.exe in a subdirectory with the same name
as the assembly name (i.e., vehicle).

At this point, the CLR throws and exception, indicating that it has
failed to find the vehicle assembly.


The search for other supporting assemblies, such as
car and plane, follows the
same order. In steps 2 and 4, the CLR looks for the supporting
assemblies in specific subdirectories. Let's
investigate an example of this in the following section, which
we've called multiple-directory deployment.

4.1.1.2 Multiple-directory deployment


Instead of storing all assemblies in the
same directory as your client application, you can also use multiple,
private subdirectories to segregate your assemblies so that they are
easier to find and manage. For example, we will separate the vehicle,
car, and plane assemblies into their own private directories, as
shown in Figure 4-1. We will leave the
drive.exe application in the top directory,
MultiDirectories.


Figure 4-1. Multiple-directory tree of components


When you build the vehicle assembly, you
don't have to do anything special, as it
doesn't reference or use any third-party assemblies.
However, when you build the car or plane assembly, you must refer to
the correct vehicle component (i.e., the one in the
vehicle directory). For example, to build the
plane assembly successfully, you must explicitly refer to
vehicle.dll using a specific or relative path,
as shown in the following command (cd to the
plane directory):

csc /r:..\vehicle\vehicle.dll /t:library /out:plane.dll plane.cs

You can build the car assembly the same way you build the plane
assembly. To compile your client application, you must also refer to
your dependencies using the correct paths (cd to
the main directory, MultiDirectories, before you
type this command all on one line):

vjc /r:vehicle\vehicle.dll;car\car.dll;plane\plane.dll
/t:exe /out:drive.exe drive.jsl

Based on the previously discussed search algorithm, the CLR can find
the supporting assemblies within the appropriate subdirectories.


4.1.2 Shared Components


Unlike application-private assemblies, shared
assembliesones that can be used by any client
applicationmust be published or
registered in the system
Global Assembly Cache (GAC). When you
register your assemblies against the GAC, they act as system
components, such as a system DLL that every process in the system can
use. A prerequisite for GAC registration is that the component must
possess originator and version information. In addition to other
metadata, these two items allow multiple versions of the same
component to be registered and executed on the same machine. Again,
unlike COM, you don't have to store any information
in the system registry for clients to use these shared assemblies.

There are three general steps to registering your shared assemblies
against the GAC:

Use the shared named (sn.exe) utility to obtain
a public/private
key pair. This utility generates a random key pair for you and saves
the key information in an output filefor example,
originator.key.

Build your assembly with an assembly version number and the key
information from originator.key.

Use the .NET Global Assembly Cache Utility
(gacutil.exe) to register your assembly in the
GAC. This assembly is now a shared assembly and can be used by any
client.


The commands that we use in this section refer to relative paths, so
if you're following along, make sure that you create
the directory structure, as shown in Figure 4-2.
The vehicle, plane, and
car directories hold their appropriate
assemblies, and the key directory holds the
public/private key pair that we will generate in a moment. The
car-build directory holds a car assembly with a
modified build number, and the car-revision
directory holds a car assembly with a modified revision number.


Figure 4-2. Directory structure for examples in this section


4.1.2.1 Generating a random key pair


We will perform the first step once and reuse the key pair for all
shared assemblies that we build in this section.
We're doing this for brevity only because you can
use different key information for each assembly, or even each
version, that you build. Here's how to generate a
random key pair (be sure to issue this command in the
key directory):

sn -k originator.key

The -k option
generates a random key pair and saves the key information into the
originator.key file. We will use this file as
input when we build our shared assemblies. Let's now
examine steps 2 and 3 of registering your shared assemblies against
the GAC.

4.1.2.2 Making the vehicle component a shared assembly


In order to add version and key
information into the vehicle component (developed using Managed C++),
we need to make some minor modifications to
vehicle.cpp, as follows:

#using<mscorlib.dll>
using namespace System;
using namespace System::Reflection;
[assembly:AssemblyVersion("1.0.0.0")];
[assembly:AssemblyKeyFile("..\\key\\originator.key")];
public _ _gc _ _interface ISteering
{
void TurnLeft( );
void TurnRight( );
};
public _ _gc class Vehicle : public ISteering
{
public:
virtual void TurnLeft( )
{
Console::WriteLine("Vehicle turns left.");
}
virtual void TurnRight( )
{
Console::WriteLine("Vehicle turn right.");
}
virtual void ApplyBrakes( ) = 0;
};

The first boldface line indicates that we're using
the Reflection namespace, which defines
the attributes that the compiler will intercept to inject the correct
information into our assembly manifest. (For a discussion of
attributes, see Section 4.3.1
later in this chapter.) We use the
AssemblyVersion
attribute to indicate the version of this assembly, and we use
the AssemblyKeyFile
attribute to indicate the file containing the key information that
the compiler should use to derive the public-key-token value, to be
revealed in a moment.

Once you've done this, you can build this assembly
using the following commands, which you've seen
before:

cl /CLR /c vehicle.cpp
link -dll /out:vehicle.dll vehicle.obj

After you've built the assembly, you can use the
.NET GAC Utility to register this assembly into the GAC, as follows:

gacutil.exe /i vehicle.dll

Successful registration against the cache turns this component into a
shared assembly. A version of this component is copied into the GAC
so that even if you delete this file locally, you will still be able
to run your client program.[3]

[3] However,
don't delete the file now because we need it to
build the car and plane assemblies.


4.1.2.3 Making the car component a shared assembly


In order to add version and key information into the car component,
we need to make some minor modifications to
car.vb, as follows:

Imports System
Imports System.Reflection
<Assembly:AssemblyVersion("1.0.0.0")>
<assembly:AssemblyKeyFile("..\\key\\originator.key")>
Public Class Car
Inherits Vehicle
Overrides Public Sub TurnLeft( )
Console.WriteLine("Car turns left.")
End Sub
Overrides Public Sub TurnRight( )
Console.WriteLine("Car turns right.")
End Sub
Overrides Public Sub ApplyBrakes( )
Console.WriteLine("Car trying to stop.")
Console.WriteLine("ORIGINAL VERSION - 1.0.0.0.")
throw new Exception("Brake failure!")
End Sub
End Class

Having done this, you can now build it with the following command:

vbc /r:..\vehicle\vehicle.dll /t:library /out:car.dll car.vb

Once you've built this component, you can register
it against the GAC:

gacutil /i car.dll

At this point, you can delete car.dll in the
local directory because it has been registered in the GAC.

4.1.2.4 Making the plane component a shared assembly


In order
to add version and key information into the plane component, we need
to make some minor modifications to plane.cs, as
follows:

using System;
using System.Reflection;
[assembly:AssemblyVersion("1.0.0.0")]
[assembly:AssemblyKeyFile("..\\key\\originator.key")]
public class Plane : Vehicle
{
override public void TurnLeft( )
{
Console.WriteLine("Plane turns left.");
}
override public void TurnRight( )
{
Console.WriteLine("Plane turns right.");
}
override public void ApplyBrakes( )
{
Console.WriteLine("Air brakes being used.");
}
}

Having done this, you can build the assembly with the following
commands:

csc /r:..\vehicle\vehicle.dll /t:library /out:plane.dll plane.cs
gacutil /i plane.dll

Of course, the last line in this snippet simply registers the
component into the GAC.

4.1.2.5 Viewing the GAC


Now that
we've registered all our components into the GAC,
let's see what the GAC looks like. Microsoft has
shipped a shell extension, the Shell Cache Viewer, to make it easier for
you to view the GAC. On our machines, the Shell Cache Viewer appears
when we navigate to C:\WINDOWS\Assembly,
as shown in
Figure 4-3.[4]

[4] This path is entirely
dependent upon the %windir% setting on your
machine.



Figure 4-3. Our shared assemblies in the GAC


As you can see, the Shell Cache Viewer shows that all our components
have the same version number because we used 1.0.0.0 as the version
number when we built our components. Additionally, it shows that all
our components have the same public-key-token value because we used
the same key file, originator.key.

4.1.2.6 Building and testing the drive.exe


You should copy the previous drive.jsl
source-code file into the Shared Assemblies
directory, the root of the directory structure (shown in Figure 4-2) we are working with in this section. Having
done this, you can build this component as follows (remember to type
everything on one line):

vjc /r:vehicle\vehicle.dll;car\car.dll;plane\plane.dll
/t:exe /out:drive.exe drive.jsl

Once you've done this, you can execute the
drive.exe component, which will use the
vehicle.dll, car.dll, and
plane.dll assemblies registered in the GAC. You
should see the following as part of your output:

ORIGINAL VERSION - 1.0.0.0.

To uninstall these shared components (assuming that you have
administrative privileges), select the appropriate assemblies and
press the Delete key (but if you do this now, you must reregister
these assemblies because we'll need them in the
upcoming examples). When you do this, you've taken
all the residues of these components out of the GAC. All
that's left is to delete any files that
you've copied over from your installation
diskettetypically, all you really have to do is recursively
remove the application directory.

4.1.2.7 Adding new versions


Unlike private assemblies,
shared assemblies can take advantage of the rich versioning policies
that the CLR supports. Unlike earlier OS-level infrastructures, the
CLR enforces versioning policies during the loading of all shared
assemblies. By default, the CLR loads the assembly with which your
application was built, but by providing an application configuration
file, you can command the CLR to load the specific assembly version
that your application needs. Inside an application configuration
file, you can specify the rules or policies that the CLR should use
when loading shared assemblies on which your application depends.

Let's make some code changes to our car component to
demonstrate the default versioning support. Remember that Version
1.0.0.0 of our car component's ApplyBrakes( ) method
throws an exception, as follows:

Overrides Public Sub ApplyBrakes( )
Console.WriteLine("Car trying to stop.")
Console.WriteLine("ORIGINAL VERSION - 1.0.0.0.")
throw new Exception("Brake failure!")
End Sub

Let's create a different build
to remove this exception. To do this, make the following changes to
the ApplyBrakes( ) method (store this source file in the
car-build directory):

Overrides Public Sub ApplyBrakes( )
Console.WriteLine("Car trying to stop.")
Console.WriteLine("BUILD NUMBER change - 1.0.1.0.")
End Sub

In addition, you need to change the build
number in your code as follows:

<Assembly:AssemblyVersion("1.0.1.0")>

Now build this component, and register it using the following
commands:

vbc /r:..\vehicle\vehicle.dll
/t:library /out:car.dll car.vb
gacutil /i car.dll

Notice that we've specified that this version is
1.0.1.0, meaning that it's compatible with Version
1.0.0.0. After registering this assembly with the GAC, execute your
drive.exe application, and you will see the
following statement as part of the output:

ORIGINAL VERSION - 1.0.0.0.

This is the default behaviorthe CLR will load the version of
the assembly with which your application was built. And just to prove
this statement further, suppose that you provide Version 1.0.1.1 by
making the following code changes (store this version in the
car-revision directory):

Overrides Public Sub ApplyBrakes( )
Console.WriteLine("Car trying to stop.")
Console.WriteLine("REVISION NUMBER change - 1.0.1.1.")
End Sub
<Assembly:AssemblyVersion("1.0.1.1")>

This time, instead of changing the build number,
you're changing the revision
number, which should still be compatible with the previous two
versions. If you build this assembly, register it against the GAC and
execute drive.exe again; you will get the
following statement as part of your output:

ORIGINAL VERSION - 1.0.0.0.

Again, the CLR chooses the version with which your application was
built.

As shown in Figure 4-4, you can use
the Shell Cache Viewer to
verify that all three versions exist on the system simultaneously.
This implies that the support exists for side-by-side
executionwhich terminates DLL Hell in .NET.


Figure 4-4. Multiple versions of the same shared assembly


If you want your program to use a different, compatible version of
the car assembly, you have to provide an application configuration
file. The name of an application configuration file is composed of
the physical executable name and
".config" appended to it. For
example, since our client program is named
drive.exe, its configuration file must be named
drive.exe.config.

Here's a drive.exe.config file
that allows you to tell the CLR to load Version 1.0.1.0 of the car
assembly for you (instead of loading the default Version, 1.0.0.0).
The two boldface attributes say that although we built our client
with Version 1.0.0.0 (oldVersion) of the car
assembly, load 1.0.1.0 (newVersion) for us when we
run drive.exe.

<?xml version ="1.0"?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="car"
publicKeyToken="D730D98B6BDE2BBA"
culture=" />
<bindingRedirect oldVersion="1.0.0.0"
newVersion="1.0.1.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>

In this configuration file, the name
attribute of the assemblyIdentity tag
indicates the shared assembly's human-readable name
that is stored in the GAC. Although the name value
can be anything, you must replace the
publicKeyToken value appropriately in order to
execute drive.exe. The
publicKeyToken attribute
records the public-key-token value, which is an 8-byte hash of the
public key used to build this component. There are several ways to
get this 8-byte hash: you can copy it from the Shell Cache Viewer,
you can copy it from the IL dump of your component, or you can use
the Shared Name utility to get it, as follows:

sn -T car.dll

Once you create the previously shown configuration file (stored in
the same directory as the drive.exe executable)
and execute drive.exe, you will see the
following as part of your output:

BUILD NUMBER change - 1.0.1.0.

If you change the configuration file to
newVersion=1.0.1.1 and execute
drive.exe again, you will see the following as
part of your output:

REVISION NUMBER change - 1.0.1.1.

Having gone over all these examples, you should realize that you have
full control over which dependent assembly versions the CLR should
load for your applications. It doesn't matter which
version was built with your application: you can choose different
versions at runtime merely by changing a few attributes in the
application configuration file.


/ 121