Elements of a .NET Application

A .NET application is composed of four primary entities:

1. Types—The common unit of transmitting data between modules running in the CLR
2. Classes—The basic units that encapsulate data and behavior
3. Modules—The individual files that contain the intermediate language (IL) for an assembly
4. Assemblies—The primary unit of deployment of a .NET application

Classes are covered in detail in the next two chapters, As such we are going to limit the discussion here to clarify that they are defined in the source files for your application or class library. Upon compilation of your source files, you produce a module. The code that makes up an assembly's modules may exist in a single executable (.exe) file or as a dynamic link library (.dll).

A module, is in fact, a Microsoft intermediate language (MSIL) file, which is then used by the CLR when your application is run. However, compiling a .NET application doesn't produce only an MSIL file; it also produces a collection of files that make up a deployable application or assembly. Within an assembly are several different types of files, including not only the actual executable files, but also configuration files, signature keys, and related resources.

Types

The type system provides a template that is used to describe the encapsulation of data and an associated set of behaviors. It is this common template for describing data that provides the basis for the metadata that .NET uses when classes interoperate at runtime.

All types are based on a common system that is used across all .NET languages, profiles and platforms. Similar to the MSIL code, which is interpreted by the CLR based upon the current runtime environment, the CLR uses a common metadata system to recognize the details of each type. The result is that all .NET languages are built around a common type system. Thus it isn't necessary to translate data through a special notation to enable transfer of data types between different .exe and .dll files.

A type has fields, properties, and methods:

  • Fields—Variables that are scoped to the type. For example, a Pet class could declare a field called Name that holds the pet's name. In a well-engineered class, fields are typically kept private and exposed only as properties or methods.
  • Properties—Properties look like fields to clients of the type but can have code behind them (which usually performs some sort of data validation). For example, a Dog data type could expose a property to set its gender. Code could then be placed within the property definition so that only “male” or “female” are accepted values. Internally the selected value might be transformed and stored in a more efficient manner using a field in the Dog class.
  • Methods—Methods define behaviors exhibited by the type. For example, the Dog data type could expose a method called Sleep, which would suspend the activity of the Dog.

The preceding elements are just part of each class. Note that some types are defined at the application level, and others are defined globally. With .NET it is not only possible but often encouraged that the classes and types defined in your modules be visible only at the application level. The advantage of this is that you can run several different versions of an application side by side.

Modules

A module contains Microsoft intermediate language (MSIL, often abbreviated to IL) code, associated metadata, and the assembly's manifest. IL is a platform-independent way of representing managed code within a module. Before IL can be executed, the CLR must compile it into the native machine code. The default method is for the CLR to use the JIT (just-in-time) compiler to compile the IL on a method-by-method basis. At runtime, as each method is called for the first time, it is passed through the JIT compiler for compilation to machine code. Similarly, for an ASP.NET application, each page is passed through the JIT compiler the first time it is requested.

Additional information about the types declared in the IL is provided by the associated metadata. The metadata contained within the module is used extensively by the CLR. For example, if a client and an object reside within two different processes, then the CLR uses the type's metadata to marshal data between the client and the object. MSIL is important because every .NET language compiles down to IL. The CLR doesn't care about or even need to know what the implementation language was; it knows only what the IL contains. Thus, any differences in .NET languages exist at the level where the IL is generated; but once compiled to IL; all .NET languages have the same runtime characteristics. Similarly, because the CLR doesn't care in which language a given module was originally written, it can leverage modules implemented in entirely different .NET languages.

A question that always arises when discussing the JIT compiler and the use of a runtime environment is “Wouldn't it be faster to compile the IL language down to native code before the user asks to run it?” Although the answer is not always yes, the answer is that combined with the flexibility of changes to the machine and other advantages related to evaluation of the environment when the code is accessed, this is the most robust solution.

However, because sometimes the only consideration is speed, Microsoft has provided a utility to handle this compilation: the Native Image Generator, or Ngen.exe. This tool enables you to essentially run the JIT compiler on a specific assembly, which is then installed into the user's application cache in its native format. The obvious advantage is that now when the user asks to execute something in that assembly, the JIT compiler is not invoked, saving a small amount of time. Unlike the JIT compiler, which compiles only those portions of an assembly that are actually referenced, Ngen.exe needs to compile the entire code base. This is important because the time required for NGEN compilation is not the same as what a user actually experiences with the JIT compiler.

Ngen.exe is executed from the command line. The utility was updated as part of .NET 2.0 and now automatically detects and includes most of the dependent assemblies as part of the image-generation process. To use Ngen.exe, you simply reference this utility followed by an action; for example, install followed by your assembly reference. Several options are available as part of the generation process, but that subject is beyond the scope of this chapter, given that Ngen.exe itself is a topic that generates heated debate regarding its use and value.

Where does the debate begin about when to use Ngen.exe? Keep in mind that in a server application, where the same assembly will be referenced by multiple users between machine restarts, the difference in performance on the first request is essentially lost. This means that compilation to native code is more valuable to client-side applications. Unfortunately, using Ngen.exe requires running it on each client machine, which can become cost prohibitive in certain installation scenarios, particularly if you use any form of self-updating application logic.

Another issue relates to using reflection, which enables you to reference other assemblies at runtime. Of course, if you don't know what assemblies you will reference until runtime, then the Native Image Generator has a problem, as it won't know what to reference, either. You may have occasion to use Ngen.exe for an application you've created, but you should fully investigate this utility and its advantages and disadvantages beforehand, keeping in mind that even native images execute within the CLR. Native image generation changes only the compilation model, not the runtime environment.

Assemblies

An assembly is the primary unit of deployment for .NET applications. It is either a dynamic link library (.dll) or an executable (.exe). An assembly is composed of a manifest, one or more modules, and (optionally) other files, such as .config, .ASPX, .ASMX, images, and so on. By default, the Visual Basic compiler creates an assembly that combines both the assembly code and the manifest.

The manifest of an assembly contains the following:

  • Information about the identity of the assembly, including its textual name and version number.
  • If the assembly is public, then the manifest contains the assembly's public key. The public key is used to help ensure that types exposed by the assembly reside within a unique namespace. It may also be used to uniquely identify the source of an assembly.
  • A declarative security request that describes the assembly's security requirements (the assembly is responsible for declaring the security it requires). Requests for permissions fall into three categories: required, optional, and denied. The identity information may be used as evidence by the CLR in determining whether or not to approve security requests.
  • A list of other assemblies on which the assembly depends. The CLR uses this information to locate an appropriate version of the required assemblies at runtime. The list of dependencies also includes the exact version number of each assembly at the time the assembly was created.
  • A list of all types and resources exposed by the assembly. If any of the resources exposed by the assembly are localized, the manifest will also contain the default culture (language, currency, date/time format, and so on) that the application will target. The CLR uses this information to locate specific resources and types within the assembly.

The manifest can be stored in a separate file or in one of the modules. By default, for most applications, it is part of the .dll or .exe file, which is compiled by Visual Studio. For ASP.NET applications, you'll find that although there is a collection of ASPX pages, the actual assembly information is located in a DLL referenced by those ASPX pages.

Better Support for Versioning

Managing component versions was challenging prior to .NET's ability to associate a manifest and use application-level references. Often, even though the version number of the component could be set, it was not used by the runtime.

For many applications, .NET has removed the need to identify the version of each assembly in a central registry on a machine. However, some assemblies are installed once and used by multiple applications. .NET provides a global assembly cache (GAC), which is used to store assemblies that are intended for use by multiple applications. The CLR provides global versioning support for all components loaded in the GAC.

The CLR provides two features for assemblies installed within the GAC:

1. Side-by-side versioning—Multiple versions of the same component can be simultaneously stored in the GAC.
2. Automatic Quick Fix Engineering (QFE)—Also known as hotfix support; if a new version of a component, which is still compatible with the old version, is available in the GAC, the CLR loads the updated component. The version number, which is maintained by the developer who created the referenced assembly, drives this behavior.

The assembly's manifest contains the version numbers of referenced assemblies. The CLR uses the assembly's manifest at runtime to locate a compatible version of each referenced assembly. The version number of an assembly takes the following form:

Major.Minor.Build.Revision

Major.Minor.Build.Revision

Changes to the major and minor version numbers of the assembly indicate that the assembly is no longer compatible with the previous versions. The CLR will not use versions of the assembly that have a different major or minor number unless it is explicitly told to do so. For example, if an assembly was originally compiled against a referenced assembly with a version number of 3.4.1.9, then the CLR will not load an assembly stored in the GAC unless it has major and minor numbers of 3 and 4, respectively.

Incrementing the revision and build numbers indicates that the new version is still compatible with the previous version. If a new assembly that has an incremented revision or build number is loaded into the GAC, then the CLR can still load this assembly for applications that were compiled referencing a previous version.

For .NET applications running outside of Metro, most components should not be registered. The external assemblies are referenced locally, which means they are carried in the application's local directory structure. Using local copies of external assemblies enables the CLR to support the side-by-side execution of different versions of the same component.

Except when looking to add an application to Metro, the only requirement for installing a .NET application is to copy it to the local machine. A .NET application can be distributed using a simple command like this:

xcopy \serverappDirectory "C:Program FilesappDirectory" /E /O /I

However, because Metro has introduced the concept of a Windows Application Store details of deployment are being focused on in Chapter 22.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset