.NET Assemblies, GAC, Versioning, Manifests and Deployment

A .NET application is composed of three primary entities: assemblies, modules and types. An Assembly is the primary unit of deployment. The individual files that make up a .NET application are call modules. Types are the basic unit of encapsulating data and behavior behind an interface composed of public fields, properties, and methods.
Assemblies
An assembly is used by the .NET CLR (Common Language Runtime) as the smallest unit for: deployment; version control; security; type grouping and code reuse. Assemblies consists of a manifest and one or more modules and/or files like HTML, XML, images, video clips,...
An assembly can be thought of as a logical DLL and must contain a single manifest and may optionally contain type meta data, MSIL (Microsoft Intermediate Language) and resources.
Assemblies come in 2 flavors: application private and shared. Application private assemblies are used by one application only. This is the default style of assembly. Such assemblies must reside in the application folder.
Shared assemblies are meant to be used by more than one application. They must have a globally unique name and must be defined in the GAC (Global Assembly Cache). To learn more about viewing the GAC click here.
Manifest (Assembly Meta Data)
The manifest makes an assembly self describing and can be viewed with the IL Disassembler - Intermediate Language Disassembler. The IL Disassembler (Ildasm.exe) is included in the .NET Framework SDK and runs from a command line.
The manifest identifies the assembly, describes its security requirements, lists other assemblies it depends on, and lists all of the types and resources exposed by the assembly. If any of the resources exposed by the assembly are localized, the mainfest contains the default culture (language, currency, date/time format, etc.) the application will target.
The manifest will be discussed further later on.
Modules
A module is either a DLL or an EXE (Windows Portable Executable - PE) file. It contains IL (Intermediate Language), associated Meta Data and optionally the assembly's manifest. IL is a platform independent way of representing the managed code within an application. Before the managed code is executed the CLR compiles the associated IL into native machine code.
Types
A type describes the encapsulation of data and an associated set of behaviors. Types are scoped at the assembly level. Reference types can be thought of as classes and value types as structures (VB6 user-defined types).
Types have properties, methods and fields. Fields are variables used to hold values. Properties are like fields but with code behind them. Methods are the public procedures that define the behavior of the class.
Versioning
The CLR supports versioning of shared assemblies via the GAC and allows for side by side versioning and Automatic QFEs (hotfixes). Side by side versioning means multiple versions of the same component can be stored in the GAC at the same time. Automatic QFE support means that if a compatible newer version of a component is available, the CLR will use it.
Manifest - Revisited
The manifest contains several sections: Identity, Referenced Assemblies, File List and Custom Attributes. The first two sections are discussed subsequently.
Identity Section of the Manifest
The Identity section uniquely identifies an assembly. To find it using the IL disassembler look for a .assembly directive in the manifest with out the extern directive. Example:
.assembly Microsoft.VisualBasic
The CLR appends .dll to the name (ex. Microsoft.VisualBasic) to find the actual file containing the assembly.
The version directive specifies the version of the assembly in major version : minor version : build : revision format:
.assembly Microsoft.VisualBasic
    .ver 7:0:0:0
Assemblies with the same name but different versions (major and minor version numbers) are treated as completely different assemblies by the CLR.
The Identity section optionally contains a strong name for private assemblies. Strong names are required for shared assemblies. A strong name uses public/private key encryption to uniquely identify the assembly and distinguish between assemblies with the same name.
A public key is generated by the assembly author using the SN tool (sn.exe) included in the .NET Framework SDK. The public key is stored in the manifest and a signature of the file containing the assembly's manifest is stored in the resultant PE file. The CLR uses these two signatures to resolve type references to ensure the correct assembly is loaded at runtime. The public key is stored using this directive:
.publickeytoken = (xxxxx)
Optionally contained in the Identity section is the culture which defines the country and language the assembly is targeted for. The .locale directive is used for this purpose. Culture-neutral assemblies can be used for any culture.
Referenced Assemblies Section of the Manifest
The Referenced Assemblies section holds information about all assemblies referenced by your assembly. The .assembly directive accomplishes this:
.assembly extern 
    .publickeytoken = (xx xx xx)
    .ver 1:0:241:0
As stated before, the CLR appends .DLL to the name to find the physical file. If the referenced assembly has a strong name, the .publickeytoken will contain a hash of the referenced assemblies public key. Version and culture directives are used for externally referenced assemblies as described above.
Deployment
Recall, assemblies can be application private or shared. Application private assemblies must reside in the application folder and do not need a strong name. They only need a name and version in the Identity section of the manifest. If a strong name exists the CLR checks the strong name of the assembly and referenced assemblies to see if they match.
Shared assemblies are used by many applications and must have a globally unique name based on their strong name. Their information is stored in the GAC which is typically the Assembly folder in the Windows directory.
To learn more about viewing the GAC, click here.
Deployment
Applications can be deployed in an isolated fashion termed application isolation where the application is self contained and independent. All modules of an application are managed by the application. If a component is used by another application, even if it's the same version, that application has its own copy. Thus each application can be installed and removed independently. .NET provides for this using application private assemblies.
Side by side execution is when multiple versions of the same assembly can execute at the same time on the same PC or within the same process. Again, the CLR allows for this. Recall, a version number has four parts. The major and minor parts determine if a component version is compatible with a prior version. If they differ, they are not compatible. If only the build and revision numbers differ, the components are compatible. This is called Quick Fix Engineering (QFE).
Unless told otherwise, .NET uses the default versioning policy to decide what version of a referenced assembly to use. If the referenced assembly doesn't have a strong name it is assumed to be application private and to reside in the application folder. The CLR will load it regardless of whether its version matches what's in the calling assembly's manifest. Thus, version numbers of application private assemblies are not checked. If the referenced assembly is not found in the application folder, an error occurs.
Quick Fix Engineering
If a referenced assembly has a strong name the load process is as follows:
  • Assembly configuration files (discussed later) are examined to see what version of a referenced assembly to load.

  • The CLR checks if the assembly was loaded in a previous call. If so, it is used.

  • If the assembly isn't loaded the GAC is queried for a match and if found that's what's loaded.

  • If a configuration file has a codebase entry for the assembly the file specified by this entry is used.

  • If none of the above exist, the CLR looks for the referenced assembly starting in the application folder.

  • If still not found, the CLR asks the Windows Installer service if it has the assembly. If so, the assembly is installed and this is the assembly used. This is call install on demand.

  • If the assembly still cannot be found, an error is raised.
Just because a referenced assembly has a strong name doesn't mean it has to be deployed to the GAC. A developer can install a known good version with the application. The GAC is checked to see if it has an assembly with a higher build.revision number. This allows deployment of an updated assembly without having to re-install or rebuild the application. This is known as Automatic Quick Fix Engineering Policy.
Configuration Files
Three configuration files can alter the above policy. The application configuration file which resides in the application folder and has the same name as the application file with .config appended to it (Ex: myExe.exe.config) is, as the name implies, application specific.
The machine configuration file named machine.config and located in the \Config folder overrides all application configuration file(s) on a machine. Lastly the security configuration file allows granting/denying access to resources by an assembly.
Configuration files are XML files used to override the default policy used by the CLR. They have a <startup> section which specifies the version of the CLR to use for the application. This is because different versions of the .NET runtime can run side by side on a machine. Click here for more info.
The <runtime> section has a few key elements that can specify the version of an assembly to use either all the time or to replace a specific version of the assembly.
<configuration>
      <runtime>
        <assemblyBinding xmlns="urn:schemas=microsoft-com:asm.v1">
          <dependentAassembly>
             <assemblyIdentity name="AssemblyName" 
                                      publickeytoken="b8838383d8s88"
                                      culture="en-us"/>  
             <bindingRedirect  oldVersion="*" 
                                     newVersion="2.0.34.0"/>  
          </dependentAassembly>
        </assemblyBinding>
      </runtime>
    </configuration>
When the CLR resolves the reference to AssemblyName it loads version 2.0.34.0 instead of the version stated in the manifest. Instead of replacing all versions with 2.0.34.0 the oldVersion element can specify the exact version to replace.
The exact location of the assembly to load can be specified with the <codeBase> element. Thus, on demand downloading can be employed to distribute an application and have an externally referenced assembly downloaded the first time it is used.
<configuration>
      <runtime>
        <assemblyBinding xmlns="urn:schemas=microsoft-com:asm.v1">
          <dependentAassembly>
             <assemblyIdentity name="AssemblyName" 
                                      publickeytoken="b8838383d8s88"
                                      culture="en-us"/>  
             <codeBase version="2.0.34.0" 
                             href="http://www.thescarms.com/mydll.dll"/>  
          </dependentAassembly>
        </assemblyBinding>
      </runtime>
    </configuration>
An application configuration file can also specify the search path for the CLR to use. By default the CLR looks in the application folder and not sub folders. This can be changed by specifying a relative path or list of relative folders separated by semi-colons.
<configuration>
      <runtime>
        <assemblyBinding xmlns="urn:schemas=microsoft-com:asm.v1">
          <probing privatePath="myfolder"/>
        </assemblyBinding>
      </runtime>
    </configuration>
The CLR will look in the application's folder, all specified sub folders and a sub folder with the same name as the assembly name. Further, it a culture is used, the CLR will look in a culture specific sub folder of each of the directories it looks in. For example, if an assembly named "AssemblyName" resides in "C:\MyApp" and the culture is "en" the CLR will look in:
C:\MyApp
   C:\MyApp\en
   C:\MyApp\en\AssemblyName
   C:\MyApp\myfolder
   C:\MyApp\myfolder\en\AssemblyName

0 comments:

Post a Comment