Creating and Managing .NET Assemblies

An assembly is the unit of deployment, scoping, versioning, and security in the .NET Framework. Microsoft uses assemblies to deliver these benefits to .NET:

  • Each assembly has a version number All the types and resources in an assembly share the same version number so applications can easily refer to the correct version of files and avoid problems such as the infamous "DLL Hell," in which installing a new version of a shared library breaks older applications.

  • The self-describing nature of assemblies enables you to deploy applications using the zero-impact XCOPY installation There's nothing to register and no system files to change.

  • Assemblies define a security boundary This enables the CLR to restrict a set of operations to be executed depending on the identity and origin of the assembly.

All .NET code executes as part of an assembly. When a user browses to a page on your ASP.NET Web site, the compiled class for the page is part of an assembly.

graphics/note_icon.gif

One of the files in the assembly contains a special piece of information called the assembly manifest . The manifest contains the metadata for the assembly, which includes information such as the name and version of the assembly, the files that make up the assembly (including their names and hash values), the compile-time dependency of this assembly on other assemblies, the culture or language an assembly supports, and the set of permissions required for the assembly to run properly.


Single-File and Multifile Assemblies

A single-file assembly consists of a single EXE or DLL file. This file consists of code, any embedded resources, and the assembly manifest of the assembly. All the assemblies created so far in the examples in this book have been single-file assemblies.

A multifile assembly is an assembly that can include multiple files. At least one of the files in a multifile assembly should be a DLL or EXE file. You can either attach the assembly manifest with any of the assembly files or create a separate file containing just the manifest. Unfortunately, Visual Studio .NET does not support the creation of multifile assemblies, so you must use command-line tools to create them. The following steps describe this process:

  1. Create a new project based on the Empty Project template; name it Example8_4 . Select the project and then select Project, Properties. On the project's property page, select the Configuration Properties and then click the Configuration Manager button. In the Configuration Manager dialog box, uncheck the Build option for the Example8_4 project. With this setting, Visual Studio .NET will not automatically attempt to build the project files.

  2. Add a new class named MathLib in the project, and add the following method to the class:

     public static int Add (int first, int second) {     return first + second; } 
  3. Add another class to the project and name this class StringLib . Add the following method to it:

     public static string Concat(string firstHalf, string secondHalf) {     return firstHalf + secondHalf; } 
  4. Open Visual Studio .NET's command prompt and change the current working directory to the directory of project Example8_4 . Issue the following command to compile StringLib.cs as a module that is not yet part of any assembly:

     csc /t:module StringLib.cs 

    A file named StringLib.netModule is generated.

  5. Type the following command to open StringLib.netModule in the MSIL Disassembler:

     ildasm StringLib.netModule 

    Open its manifest. You'll note that this manifest is not an assembly manifest because it has no .assembly directive.

    graphics/note_icon.gif

    The MSIL Disassembler ( ildasm.exe ) can be used to view the metadata and disassembled code for .NET libraries, modules, and executables in a hierarchical tree view format.


  6. Now compile the MathLib.cs file into a module. When you inspect the MathLib.netModule file through the MSIL Disassembler, you should again note that this file is just a module and not an assembly.

  7. Type the following command to read module files and create an assembly:

     csc /t:library /out:Utils.dll /addmodule:MathLib.netModule, StringLib.netModule 
  8. Open the Utils.dll file in the MSIL Disassembler. Note that Utils.dll contains just the manifest and no code. Examine the manifest and note that it has an .assembly Utils directive that identifies it as an assembly manifest for an assembly named Utils . The assembly directive is followed by two .file directives that identify other files in the assembly.

The Utils.dll file created in the previous steps just specifies the assembly manifest. The assembly actually consists of three files: Utils.dll , MathLib.netModule , and StringLib.netModule . So you can see that the concept of an assembly is logical and not physical. Although Utils.dll , MathLib.netModule , and StringLib.netModule are three distinct files physically, logically they belong to the same assembly: Utils .

Take the following steps to see how you can use a multifile assembly from an application:

  1. Add a new Visual C# ASP.NET Web Application project named Example8_5 .

  2. Add a reference for the file Utils.dll . (It should be in the folder of project Example8_4 .)

  3. Switch to Code view and add the following using directive at the top of the code:

     using Example8_4; 
  4. In Design view, arrange the controls on the form (see Figure 8.2). Name the top two TextBox controls txtFirst and txtSecond ; name the Button control on the top btnAdd ; and name the Label control below them lblAddResult . Name the bottom two TextBox controls txtFirstHalf and txtSecondHalf ; name the Button control btnConcat ; and name the Label control at the bottom lblConcatResult .

    Figure 8.2. A Web application can use classes from a multifile assembly.

    graphics/08fig02.jpg

  5. Add the following event handling code to the Click event of btnAdd :

     private void btnAdd_Click(object sender, System.EventArgs e) {     int intAddResult = MathLib.Add(Convert.ToInt32(txtFirst.Text),        Convert.ToInt32(txtSecond.Text));     lblAddResult.Text = String.Format(       "The Result of Addition is: {0}", intAddResult); } 
  6. Add the following event handling code to the Click event of btnConcat :

     private void btnConcat_Click(object sender, System.EventArgs e) {     lblConcatResult.Text = "The Result of concatenation is: " +        StringLib.Concat(txtFirstHalf.Text, txtSecondHalf.Text); } 
  7. Set the project as the startup project and run the project. Enter two integer values in the text boxes at the top and click the Add button. This invokes the MathLib.Add() method from the MathLib.netModule file. Enter two string values in the text boxes at the bottom and click the Concat button. This invokes the StringLib.Concat() method from the StringLib.netModule file. You should see output similar to that shown in Figure 8.2.

Static and Dynamic Assemblies

When you compile programs using Visual Studio .NET or through the command-line compiler, they emit the files that make up an assembly. These files are physically stored on disk. Such an assembly is called a static assembly .

However, you can also create and execute assemblies on-the-fly (while a program is still under execution). Such assemblies are called dynamic assemblies . In fact, dynamic assemblies are used extensively by ASP.NET. While executing ASPX pages, the ASP.NET process creates the corresponding assemblies at runtime. If needed, dynamic assemblies can be saved to disk to be loaded again later. The classes used to create dynamic assemblies are available in the System.Reflection.Emit namespace.

Private and Shared Assemblies

Assemblies can be deployed using two approaches:

  • You can deploy an assembly for use with a single application. When an assembly is deployed this way, it is called a private assembly .

  • You can deploy an assembly for use with several applications. When an assembly is deployed in the shared mode, it is called a shared assembly .

Here are some fast facts about private assemblies:

  • Private assemblies are intended to be used only by the application with which they are deployed.

  • Private assemblies are deployed in the directory (or a subdirectory) where the main application is installed.

  • The CLR does not impose a strict versioning policy for the private assembly and instead leaves the versioning to the developer.

Here are some fast facts about shared assemblies:

  • All the shared assemblies on a computer are installed in a special place called the global assembly cache (GAC). Because of the shared nature of the GAC, the CLR imposes special security and versioning requirements on any assembly installed in the GAC.

  • All assemblies installed in the GAC must have a strong name. A strong name consists of an assembly's name, a version number, a culture, a public key, and an optional digital signature. Having a strong name ensures an assembly's identity.

  • The CLR checks for the assembly's integrity before installing it in the GAC and ensures that an assembly has not been tampered with by checking the strong name of the assembly.

  • The GAC is capable of maintaining multiple copies of an assembly with the same name but different versions.

  • The runtime can determine which version of an assembly to load based on the information in an application's configuration file or the machinewide configuration file ( machine.config ).

Satellite and Resource-only Assemblies

A Web application typically contains resources, such as images and strings, in addition to code. When you add these resources to a Visual Studio .NET project, their default build type is Content . When you compile the project, the assemblies contain just code, metadata, and links to files that exist externally. This means that the resource files are distributed with the application as separate files, and all these files must be available at runtime for the application to function correctly.

If you set the build type of a project to Embedded Resources instead of Content , the contents of the resource file are included in the assembly itself at compile time. Visual Studio .NET does this in the following three steps:

  1. It creates an XML resource file with an extension of .resx . This file stores the resources as key/value pairsfor example, the name of a resource file and its location.

  2. At the time of compilation, all resources referenced by the .resx file are embedded into a binary file with the extension .resources .

  3. The binary resource file is embedded into the code assembly.

Of course, all these steps can be done manually. The .resx file is an XML file, so you can create it using any text editor. It can be compiled into a .resources file using the Resource File Generator tool ( resgen .exe ). A .resources file can be embedded in an assembly using the csc.exe compiler's /resource option.

These steps create an assembly that contains both code and resources. Assemblies created in such a way are not dependent on external resource files but have all the necessary information.

Another way to attach resources to an application is by creating resource-only assemblies. These assemblies just contain resources without any code.

Visual Studio .NET does not allow you to create a resource-only assembly. However, you can use the command-line tools provided by the .NET Framework to create such assemblies.

The .NET Framework provides various classes in the System.Resources namespace that can be used to work with resource files. Some important classes of this namespace are listed in Table 8.1.

Table 8.1. Some Important Classes That Deal with Resources

Class

Explanation

ResourceManager

Provides access to resources at runtime. You can use this class to read information from resource-only assemblies.

ResourceReader

Enables you to read resources from a binary resource file ( .resources ).

ResourceWriter

Enables you to write resources to a binary resource file ( .resources ).

ResXResourceReader

Enables you to read resource information from an XML-based .resx file.

ResXResourceWriter

Enables you to write resource information to an XML-based .resx file.

The following example uses some of these classes to write an application that shows how to programatically generate .resx and .resources files from given resources. The objective is to create resource files for storing the flags of various countries . You can get these graphics from the common7\graphics\icons\flag folder from your Visual Studio .NET installation. For this example, I renamed those files with their corresponding two-letter International Standards Organization (ISO) country codes. Perform the following steps to learn how to work with resources:

  1. Add a new Windows Application project to your solution and name it Example8_6 .

  2. In Solution Explorer, right-click the project and select Add, New Folder. Name the folder Flags . Right-click the Flags folder and select Add, Add Existing Item; then add all the icon files of the country flags to this folder.

  3. Place two Button controls on the form and name them btnGenerateResX and btnGenerateResources .

  4. Place the following using directive at the top of the code:

     using System.Resources; using System.IO; 
  5. Add an event handler for the Click event of btnGenerateResX and add the following code to it:

     private void btnGenerateResX_Click(object sender, System.EventArgs e) {     ResXResourceWriter rsxw = new ResXResourceWriter("Flags.resx");     //the EXE will be placed in bin\debug folder so     // refer to Flags folder from there     foreach (string file in Directory.GetFiles(          @"..\..\Flags", "*.ico"))     {         string countryCode = file.Substring(file.Length-6, 2);         Image img = Image.FromFile(file);         //store the Key-Value pair.         rsxw.AddResource(countryCode,img);     }     rsxw.Close();     MessageBox.Show("Flags.resx file generated"); } 
  6. Add an event handler for the Click event of btnGenerateResources and add the following code to it:

     private void btnGenerateResources_Click               (object sender, System.EventArgs e) {     ResourceWriter rw = new ResourceWriter("Flags.resources");     //the EXE will be placed in bin\debug folder so     // refer to Flags folder from there     foreach (string file in Directory.GetFiles(         @"..\..\Flags", "*.ico"))     {         string countryCode = file.Substring(file.Length-6, 2);         Image img = Image.FromFile(file);         //store the Key-Value pair.         rw.AddResource(countryCode,img);     }     rw.Close();     MessageBox.Show("Flags.resources file generated"); } 
  7. Set the project as the startup project and run the project. Click each of the buttons to create both a Flags.resx file and a Flags.resources file. The location of these files is the same as the location of the project's EXE file.

Note that the Flags.resources file is not generated by compiling the Flags.resx file; rather, it is directly created using the ResourceWriter class. If you chose to create a .resources file from a .resx file, you could use the following command:

 resgen Flags.resx 

Although the Flags.resources file has resources embedded in binary format, it is not an assembly. To create an assembly from this file, you can use the command-line Assembly Linker tool ( al.exe ) as shown here:

 al /embed:Flags.resources /out:Flags.Resources.dll 

Now that you know how to create a resource-only assembly, let's see how to use it from a Web application. I'll demonstrate this in the following example. In this example you should especially focus on the use of the ResourceManager class to load the resources from resource-only assemblies. Do the following:

  1. Create a new Visual C# ASP.NET Web Application project and name it Example8_7 .

  2. Add a TextBox control ( txtCountryCode ), an Image control ( imgFlag ), a Button control ( btnGetFlag ), and a Label control to the Web form. Arrange the controls on the form (see Figure 8.3). Set the ImageUrl property of imgFlag to WebForm2.aspx .

    Figure 8.3. The ResourceManager class enables you to load the resources from resource-only assemblies .

    graphics/08fig03.jpg

  3. Add the following code to the page's Load event handler:

     private void Page_Load(object sender, System.EventArgs e) {     if (! Page.IsPostBack)         Session["Country"] = "US";     else         Session["Country"] = txtCountryCode.Text.ToUpper(); } 
  4. Add another Web form ( WebForm2.aspx ) to the project. Switch to Code view and add the following using directives:

     using System.Reflection; using System.Resources; 
  5. Add an event handler for the Load event of the WebForm2.aspx Web form, like so:

     private void Page_Load(object sender, System.EventArgs e) {     // Build a new resource manager on the resource-only assembly     ResourceManager rm = new ResourceManager("Flags",         System.Reflection.Assembly.LoadFrom(         Server.MapPath("Flags.resources.dll")));     // Use the session variable to retrieve the appropriate bitmap     Bitmap bmp = (Bitmap) rm.GetObject(Session["Country"].ToString());     // And stream it back as the result of this page     Response.ContentType = "image/gif";     bmp.Save(Response.OutputStream,         System.Drawing.Imaging.ImageFormat.Gif);     // Cleanup     bmp.Dispose(); } 
  6. Copy the Flags.resources.dll file to the folder for this project where the WebForm2.aspx page is stored.

  7. Set the project as the startup project for the solution and run the project. It will default to displaying the U.S. flag. Enter another country code in the text box and click the Get Flag button. The appropriate flag will be loaded into the Image control from the resource assembly, as shown in Figure 8.3.

A common use of resource-only assemblies is to store language and culture-specific information. A Web application designed for international usage might package resource information for each locale in a separate assembly file. When a user downloads the application, she can ignore the assemblies for other cultures. Skipping the unnecessary files can significantly reduce the user's download time for the application.

Resource-only assemblies that store culture-specific information are also known as satellite assemblies . Visual Studio .NET offers user interface support for creating and using satellite assemblies for Windows forms but not for Web forms. To use a satellite assembly for a Web form, you need to build your own resource-only assembly and use custom code to retrieve the resources, as shown in the previous example.



MCAD Developing and Implementing Web Applications with Visual C#. NET and Visual Studio. NET (Exam [... ]am 2)
MCAD Developing and Implementing Web Applications with Visual C#. NET and Visual Studio. NET (Exam [... ]am 2)
ISBN: 789729016
EAN: N/A
Year: 2005
Pages: 191

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