Creating and Using Multifile Assemblies
 
Published: 13 Oct 2003
Unedited - Community Contributed
Abstract
You've heard about them, but have you used them? This article will tell you how and when to use multifile assemblies.
by J. Ambrose Little
Feedback
Average Rating: This article has not yet been rated.
Views (Total / Last 10 Days): 33614/ 57

Introduction


Originally Published: 5 July 2003

One of the lesser-talked-about features of .NET is what is commonly referred to as a "multifile assembly."  They are often mentioned briefly in passing in articles and books, but few rarely actually demonstrate how to create them and even fewer discuss using them.  In this article, we will do both.  First, we'll examine some reasons that you might want to use multifile assemblies.  Then, we'll cover a few of the options available in creating them, and finally, we'll take a look at how you use them and some of the considerations to keep in mind when deciding whether or not to use them.

In order to demonstrate the concepts involved, I have created a Visual Studio.NET 2003 solution with three projects--VBLibrary, CSLibrary, and Tester.  These are, respectively, a small VB.NET class library project containing one .vb file that has one class with one shared method, a small C# class library project containing an AssemblyInfo.cs file, which has assembly metadata attributes, and another .cs file that has a sample cryptography class, and lastly, a small C# console application that is used to demonstrate usage of the multifile assembly produced from the first two projects. 

 

Why Bother?

At this point, you may be asking yourself (and rightly so), why should I bother with creating a multifile assembly?  My first answer for you is that you probably shouldn't.  In my estimation, the amount of extra work involved does not justify the benefit in most common cases, particularly for ASP.NET development.  However, there some notable exceptions.

For instance, one exception would be if you wanted to create a class library and needed to write it in different languages for whatever reason, such as if you wanted your clients to only have to reference one assembly.  The docs (MSDN) say that this is the most common scenario.

Another good reason would be to optimize downloading an application--you can group types by their usage in the application and thus download them incrementally as you need them.  This is especially poignant for IE-hosted controls (such as using the <object /> tag) because IE will download the assembly manifest first and then create worker threads to download other modules and assemblies.  It is important because IE will be unresponsive while downloading the assembly manifest.

Also, if you wanted multiple developers to write separate sections of the assembly in different projects but did not want to publicly expose some or all types to obtain interoperability between the two, you could declare those types with assembly-level visibility (e.g., internal in C# and Friend in VB.NET).  This particular situation could come up if you needed separate projects because of separate languages (as above) or if you did not have a central code repository and/or no source control but still had multiple developers needing to work independently, or perhaps also if you have two existing projects that you would like to easily merge into one assembly.

As you can see, there are some good reasons for wanting multifile assemblies, but the situations demanding them have thus far been quite rare.  Perhaps once distributed windows forms become more common, they might become more common as well.  So if you find yourself in one of these situations or simply are curious, read on.  I have chosen to demonstrate the multiple language situation because it demonstrates the concepts without overly complicating the example with other issues, such as those related to internet deployment.

 

So How Do I Do It?

If you've come this far, you're likely rapping your fingers on the table, wondering when I'll get to the example.  Good news!  You've arrived at the right place.  As mentioned, I created three projects in VS.NET, though having them as projects is really unecessary, since neither VB.NET nor Visual C# give you the option to compile as modules, which is what you need to do for a multifile assembly.  The main reason I used projects is for the nice editing environment and logical grouping.  It also leaves the option open to easily build them separately as assemblies in their own right.

We'll ignore the details of the code, as that is not particularly pertinent.  Suffice it to say that I used a sample cryptography class in my C# project (feel free to grab it and use it in your own projects), and I created an extremely simple class in my VB project that makes use of the VisualBasic.DateDiff function.  (As an aside: Please note that, despite common misconceptions, you can easily use the Microsoft.VisualBasic namespace in C# as well--it's a .NET assembly just like the rest of them--so don't let the name mislead you.  I only used it here on a whim because I could not think of anything else I would want to do in VB.NET that I wouldn't prefer to do in C#.  In general, I recommend C# over VB.NET for code libraries due to C#'s inherently supported Intellisense and documentation via XML comments.)  The third project is a tester project that references the assembly created from the other two projects and uses methods from each to demonstrate that the example works as expected.

The real core of the sample is in the build scripts I have created.  You will notice, if you download the sample, that there are several files in the solution directory that enable us to easily build the multifile assembly.  Remember, you cannot do this in VS.NET (2002 or 2003), which is why we must have build scripts.  I have created two batch files, BuildWithAL.bat and BuildWithCSC.bat, to demonstrate two different ways that you can create a multifile assembly.  BuildWithAL.bat builds both the VB.NET and C# projects as modules and then links them together using the Assembly Linker (al.exe), producing an assembly of three files--the two modules and the assembly manifest.  BuildWithCSC.bat builds the VB.NET project as a module and then uses the C# Compiler (csc.exe) to create the assembly by incorporating the VB.NET module with the /addmodule compiler switch.

In both cases, you will note that the C# project has the AssemblyInfo.cs file, which contains the desired assembly attributes for the assembly.  Most of these can also be specified on the command line using the compilers and the Assembly Linker, but I chose the file for conceptual and physical grouping of the assembly attributes, such as title, description, version, key file, etc.

 

Option 1: Assembly Linker

First, let's examine building the projects as modules and then linking them via the Assembly Linker.  In Figure 1, we see the contents of the BuildWithAL.bat batch file.  First, I add the path to the desired version of the Framework (1.1 in this example).  Then I invoke the VB.NET Compiler (vbc.exe), providing it with what is called a response file.  Response files are simply a plain text file with compiler switches in them.  You specify them with the VB.NET and C# compilers by prefixing the response file name with the @ sign. 

Figure 1

Figure 2 shows the contents of the VBModule.rsp file.  As you can see, I specify /nologo to suppress the compiler informational blurb, /t:module, which tells it to output a module,  /r to specify the assemblies I'm referencing in my code, /out to specify the target filename, and finally I specify the files to include in the compilation.  Since we only have one, I just put its name there (as opposed to using wildcards).  We could also have put all of these switches on the command line, but I use response files for readability and managability.  The key here is the /t[arget] switch.  This is what tells the compiler to output a module instead of an assembly.  "Module" is the .NET term for a file containing Microsoft Intermediate Language code.  It cannot be used or executed on its own because it lacks assembly metadata required by the Common Language Runtime (CLR).  It must be included in an assembly, either by using the Assembly Linker or by using /addmodule in a compiler that supports that functionality.

Figure 2

The next significant line in Figure 1 is Line 6 where we are invoking the C# Compiler to compile the C# project as a module.  Again, we see that I am using a response file, CSModule.rsp, whose contents (Figure 3) are much the same as VBModule.rsp, except that I use a wildcard (*.cs) to specify all class files in the CSLibrary directory.  Again, the key here is to target a module.  You may also notice the /noconfig switch on Line 6 of Figure 1.  This simply instructs the compiler not to use the csc.rsp response file (located in the same directory as csc.exe) that automatically references all of the Framework System assemblies.

Figure 3

The last significant lines in Figure 1 are Lines 8-9. These are really just one command that I used the line continuation character (^) on to make more readable.  The command invokes the Assembly Linker, which will create an assembly file as specified in the /out switch.  Notice the first arguments are the modules to link, which are the two modules just created on Lines 4 and 6 of Figure 1.  Again I specify /nologo to suppress the informational blurb, and /target tells it to output a code library (DLL), which is actually the default.  You can also specify exe (console app) and win (windows app), but for those you should add a /main switch to specify the entry point method for the executable.

And that is it.  It really just boils down to a few command lines, so while it's not as easy as clicking Build in VS.NET, it's not that difficult either.  The main difficulty would be keeping the project items and options synchronized between the VS.NET project and your build script. 

Using this method leaves you with two .netmodule files containing your two projects' code and one .dll file that is only the assembly manifest.  You must deploy all modules in the assembly into the same directory on the target computer, and you need to reference the file containing the assembly manifest (typically the DLL file), which in our case is MultifileAssembly.dll.  Also note that you would likely want to use this method if you were deploying the application over the net, as mentioned above as the second reason that you might want a multifile assembly.

 

Option 2: /addmodule Compiler Switch

Next let's look at another method of creating a multifile assembly.  Both the VB.NET Compiler and the C# Compiler support an /addmodule switch that allows you to automatically link modules into the assembly that you are building with the compiler executive.  I have done this in my example in the BuildWithCSC.bat (Figure 4).

Figure 4

The first part of the batch file is the exact same as in the first option, setting the path and building the VB.NET module.  The difference is that instead of building the C# project as a module (as in Figure 3), we build it as an assembly, linking the VB.NET module to it (see Figure 5).  First, we change the /t[arget] option to specify that we want to build a code library instead of a module (Figure 5, Line 3), as we did previously.  Next, we use the /addmodule switch to add the VBLibrary.netmodule (Figure 5, Line 5), and last, we specify our assembly name (MultifileAssembly.dll) instead of CSLibrary.netmodule, as we did previously.

Figure 5

The net result of this is that we end up with only two files in our assembly, VBLibrary.netmodule and MultifileAssembly.dll, instead of the three we had previoiusly.  And now, instead of having an assembly file with only the assembly manifest, we now have the assembly manifest built into the same file with the C# project code.  This method might be preferred in situation where speed of deployment is less of an issue and having fewer files is more important.  Ultimately, the CLR does not care which method you choose because both will act like one assemly as far as it is concerned, and the same goes for VB.NET and C# compilers (and likely most other compilers).

 

Quick Example

In order to quickly demonstrate how to use a multifile assembly, I created a C# console project called Tester.  This application simply creates two DateTime objects, sets one of them to System.DateTime.Now and the second to be that plus three days (using the AddDays method).  It then uses the one method from our VBLibrary module (called DayDiff), which uses Microsoft.VisualBasic.DateDiff to return the difference in days between the two dates (which as we know will be 3).  Finally, it writes to the console the difference (3) and what that would be encrypted as using the default encryption method (Encrypt) from the C# module's Cryptography class.  So the output of the application is (and will always be) what is in Figure 6.  The point, again, was simply to demonstrate that all you need to do is reference the main assembly file (containing the assembly manifest), and then you can use all of the types from both projects as if they were part of one single-file assembly.

Figure 6
 
And Now for the Gotchas

As already mentioned, VS.NET 2002 and 2003 cannot handle multifile assemblies.  In fact, with the notable exception of Visual C++ with Managed Extensions, you cannot even specify a project to compile as a module.  Because of this, you will have to maintain build scripts like the ones in the demo (only more complex) and ensure that the files, references, and other project settings are the same in the build scripts as they are in the VS.NET project file.

Also, as of v7.1 (2003), Visual C# does not support Intellisense for code that is in modules, that is, not in the main assembly file with the assembly manifest.  Thus, if you use option one above and reference that assembly in a C# project in VS.NET, absolutely none of the types in that assembly will show up in Intellisense.  If you use option two above, only those types in the main assembly project (in our case, CSLibrary) will be visible in Intellisense.  As I see it, this is a very grave reason not to use multifile assemblies for code libraries unless you can architect it in such a way that all public types will be in the main assembly.  Note that this limitation does not apply to VB.NET--all types are visible in Intellisense regardless of which method you use.

I presume that you can understand now why I recommend against using multifile assemblies unless you really need them.  Both of these limitations can make creating and using them more of a problem than a solution for most.  The biggest niche for them will be internet deployment, but that is not too common today.  Hopefully the Visual Studio.NET teams will enhance their products to better support them in the future, but in the meantime, beware.



User Comments

Title: very good article and explanantion   
Name: Jon
Date: 2005-08-12 12:18:56 AM
Comment:
I don't use Visual Studio for .net development, so this is excellent information for my work.

Thanks
Title: fd   
Name: sp
Date: 2005-07-06 7:08:13 AM
Comment:
good
Title: Good quick explanation of the al   
Name: Tom
Date: 2004-09-28 1:06:37 PM
Comment:
I was looking for a quick explanation of the assembly linker and I found this through a search on the web. It was concise and well written. Thanks.






Community Advice: ASP | SQL | XML | Regular Expressions | Windows


©Copyright 1998-2024 ASPAlliance.com  |  Page Processed at 2024-04-24 7:14:03 PM  AspAlliance Recent Articles RSS Feed
About ASPAlliance | Newsgroups | Advertise | Authors | Email Lists | Feedback | Link To Us | Privacy | Search