How and When to Share Components in .NET
As developers begin to design and implement applications using Visual Studio .NET and the .NET Framework, one of the persistent confusions that arises is how to design and use shared components. In this article, I discuss the code-sharing model of the common language runtime and how the Global Assembly Cache can be configured to allow the sharing of components.
Public or Private?
At the core, a code-sharing model is simply the infrastructure that allows more than one piece of code (an EXE or DLL) to bind to a common library of code (typically a DLL) on the same computer. This is a fundamental aspect of reuse because it means that more than one application can share the services in a single code library. Developers who have been working on the Microsoft development platform have certainly taken advantage of the code-sharing models provided by the Win32 API using exported DLL functions and those exposed through COM using components configured in the system registry. The code-sharing model in the common language runtime is analogous to those models, but is both more secure and robust.
The most important concept to remember when thinking about shared components in the common language runtime is that all binding is private by default. In other words, when an assembly (the fundamental unit of deployment, security, and versioning for managed code) is referenced in a VS .NET project using the Add References dialog box, it is assumed that at runtime the assembly will be located in the directory in which the application is launched or a path relative to it.
The actual process of searching for the assembly, which is referred to as probing, can be affected by application configuration files and the locale of the assembly. For the complete set of rules, see Chapter 1 of my book, Building Distributed Applications with Visual Basic .NET (Sams, 2001, ISBN: 0672321300).
Each application is assumed to have its own copy of the assembly, and that is why the Copy Local property in the VS .NET IDE defaults to True. Indeed, to ensure that the assembly can be found at runtime VS .NET retrieves the assembly from the referenced location, and copies it to the application directory of the current project.
Obviously, this scheme serves to isolate applications, so if a new version of an assembly is placed on the machine, each application continues to work as expected because they each have their own private copy of the assembly. This scheme is also what enables XCOPY deployment, the idea that an application's directory can simply be copied from one machine to another and the application simply runs without any additional configuration.
This model sits in stark contrast to the code-sharing model in COM, in which every component was assumed to be public. This was the case because in Visual Basic 6.0, for example, compiling an ActiveX DLL also had the effect of registering it in the system registry, where it could be referenced by any application on the local machine. This meant that if a new version of the DLL were placed on the machine, applications that referenced it may break if the component differed in its binary layout. In addition, there was no concept of versioning, so multiple versions of the same component could not be simultaneously registered and referenced by different applications (referred to as side-by-side deployment). Because of these benefits, it is recommended that you develop your applications by using this private model unless you absolutely must have multiple applications pointing to the same assembly.
For example, it is often the case that assemblies hosted in Component Services (COM+) need to be shared so that multiple clients can access them.
In the managed world of the common language runtime, a developer must take explicit steps to make an assembly public, or shared. And when they do so, they get the benefits of side-by-side deployment as well as code sharing across the machine.