Assemblies are designed to simplify application deployment and to solve versioning problems that can occur with component-based applications.
Many deployment problems have been solved by the use of assemblies in the .NET Framework. Because they are self-describing components that have no dependencies on registry entries, assemblies enable zero-impact application installation. They also simplify uninstalling and replicating applications.
Alternatively, the elements of an assembly can be contained in several files. These files can be modules of compiled code (.netmodule), resources (such as .bmp or .jpg files), or other files required by the application. Create a multifile assembly when you want to combine modules written in different languages and to optimize downloading an application by putting seldom used types in a module that is downloaded only when needed.
Each computer where the common language runtime is installed has a machine-wide code cache called the global assembly cache. The global assembly cache stores assemblies specifically designated to be shared by several applications on the computer.
You should share assemblies by installing them into the global assembly cache only when you need to. As a general guideline, keep assembly dependencies private, and locate assemblies in the application directory unless sharing an assembly is explicitly required. In addition, it is not necessary to install assemblies into the global assembly cache to make them accessible to COM interop or unmanaged code.
Starting with the .NET Framework 4, the default location for the global assembly cache is %windir%\Microsoft.NET\assembly. In earlier versions of the .NET Framework, the default location is %windir%\assembly.
All versioning of assemblies that use the common language runtime is done at the assembly level. The specific version of an assembly and the versions of dependent assemblies are recorded in the assembly’s manifest. Versioning is done only on assemblies with strong names.
The runtime performs several steps to resolve an assembly binding request:
- Checks the original assembly reference to determine the version of the assembly to be bound.
- Checks for all applicable configuration files to apply version policy.
- Determines the correct assembly from the original assembly reference and any redirection specified in the configuration files, and determines the version that should be bound to the calling assembly.
- Checks the global assembly cache, codebases specified in configuration files, and then checks the application’s directory and subdirectories using the probing rules explained in How the Runtime Locates Assemblies.
This version number is physically represented as a four-part string with the following format:
<major version>.<minor version>.<build number>.<revision>
Operating systems and runtime environments typically provide some form of isolation between applications. For example, Windows uses processes to isolate applications. This isolation is necessary to ensure that code running in one application cannot adversely affect other, unrelated applications.
The applications are isolated because memory addresses are process-relative; a memory pointer passed from one process to another cannot be used in any meaningful way in the target process. In addition, you cannot make direct calls between two processes. Instead, you must use proxies, which provide a level of indirection.
The ability to run multiple applications within a single process dramatically increases server scalability.
The isolation provided by application domains has the following benefits:
- Faults in one application cannot affect other applications. Because type-safe code cannot cause memory faults, using application domains ensures that code running in one domain cannot affect other applications in the process.
- Individual applications can be stopped without stopping the entire process. Using application domains enables you to unload the code running in a single application.There is not a one-to-one correlation between application domains and threads. Several threads can execute in a single application domain at any given time, and a particular thread is not confined to a single application domain. That is, threads are free to cross application domain boundaries; a new thread is not created for each application domain.
At any given time, every thread executes in an application domain. Zero, one, or multiple threads might be executing in any given application domain. The run time keeps track of which threads are running in which application domains. You can locate the domain in which a thread is executing at any time by calling the Thread.GetDomain method.
The AppDomain class is the programmatic interface to application domains. This class includes methods to create and unload domains, to create instances of types in domains, and to register for various notifications such as application domain unloading.
For most applications, you do not need to create your own application domain; the runtime host creates any necessary application domains for you. However, you can create and configure additional application domains if your application needs to isolate code or to use and unload DLLs. You can create your own application domains and load into them those assemblies that you want to manage personally. You can also create application domains from which you execute code.
The Unload method gracefully shuts down the specified application domain. During the unloading process, no new threads can access the application domain, and all application domain–specific data structures are freed.
The common language runtime locks an assembly file when the assembly is loaded, so the file cannot be updated until the assembly is unloaded. The only way to unload an assembly from an application domain is by unloading the application domain, so under normal circumstances, an assembly cannot be updated on disk until all the application domains that are using it have been unloaded.
When an application domain is configured to shadow copy files, assemblies from the application path are copied to another location and loaded from that location. The copy is locked, but the original assembly file is unlocked and can be updated.
Shadow copying is not supported in Windows 8.x Store apps. Assemblies stored in the global assembly cache are not shadow copied.
Directory paths must not contain semicolons, because the semicolon is the delimiter character. There is no escape character for semicolons.The FirstChanceException event of the AppDomain class lets you receive a notification that an exception has been thrown, before the common language runtime has begun searching for exception handlers.
The event is raised at the application domain level. A thread of execution can pass through multiple application domains, so an exception that is unhandled in one application domain could be handled in another application domain. The notification occurs in each application domain that has added a handler for the event, until an application domain handles the exception.
FirstChanceException thrown twice, because exception is re-thrown in catch statement again.
When loading an assembly, the event handler must not use any of the AppDomain.Load or Assembly.Load method overloads that can cause the AssemblyResolve event to be raised recursively, because this can lead to a stack overflow.
An assembly’s name is stored in metadata and has a significant impact on the assembly’s scope and use by an application.
For example, a strong-named assembly called myTypes could have the following fully qualified name:
myTypes, Version=1.0.1234.0, Culture=en-US, PublicKeyToken=b77a5c561934e089c, ProcessorArchitecture=msilFor best results, manage assembly names as though they were case-sensitive.
You can deploy an assembly in the following locations:
Multifile assemblies can have only one entry point, even if the assembly has multiple code modules.
- The application’s directory or subdirectories.
This is the most common location for deploying an assembly. The subdirectories of an application’s root directory can be based on language or culture. If an assembly has information in the culture attribute, it must be in a subdirectory under the application directory with that culture’s name.
- The global assembly cache.
This is a machine-wide code cache that is installed wherever the common language runtime is installed. In most cases, if you intend to share an assembly with multiple applications, you should deploy it into the global assembly cache.
- On an HTTP server.
An assembly deployed on an HTTP server must have a strongname; you point to the assembly in the codebase section of the application’s configuration file.
There are several reasons you might want to create a multi-file assembly:
- To combine modules written in different languages. This is the most common reason for creating a multi-file assembly.
- To optimize downloading an application by putting seldom-used types in a module that is downloaded only when needed.
A strong-named assembly can only use types from other strong-named assemblies. Otherwise, the security of the strong-named assembly would be compromised.Reflector shows missing strong name
- The application’s directory or subdirectories.