Overview
The Custom Assembly Binder (also called Custom Assembly Resolver) is the component that locates assemblies to be loaded in the application domain. The Virtual Runtime Engine calls our assembly resolver whenever an assembly is required and was not found by the standard CLR resolver. We get a name, a version and a public key token, and we have to load and return the chosen assembly.
Rationales
We had to implement our own assembly binder in order to provide the following functionalities:
- The Search Path facility, which allows assemblies to be located in various directories.
- Version tolerance: the possibility to bind to an assembly that has not exactly the same version number as required.
Version Tolerance
The behavior of the default .NET assembly binder is to require that the requested and the candidate assemblies should have exactly the same version number. It makes sense for self-contained applications but has a major drawback for open frameworks: all plug-ins should be recompiled every time the framework (or one of the main plug-ins) have been upgraded. The .NET Framework provides also a facility to redirect versions but, again, it is difficult to implement in a plug-in architecture.
The solution that was chosen for PostSharp is to allow backward compatibility. An assembly with higher version may be used when a lower version is required. The condition is that the assembly should have an instance of the BackwardCompatibleAssemblyAttribute custom attribute, which specifies the minimal version number where backward compatibility is guaranteed.
Assembly Binding Algorithm
Invariants
The assembly resolution binding secures the following invariants:
- Only highest version among all available versions of an assembly is loaded in the application domain.
- Assemblies loaded in the application domain are compatible with
each other (regarding
BackwardCompatibleAssemblyAttributeattributes).
Steps
When an assembly is requested, the Custom Assembly Binder goes through the following steps:
- The binder looks into the list of assemblies that are already loaded in the application. If an assembly with the same name and public key is found. If yes, it is selected as the candidate assembly.
- If the previous step failed, the binder probes each directory
for files named after the requested assembly (uniquely with
extensions
.dll). During this iteration, it remembers the location of the assembly with the highest version. This version is selected as the candidate. - The binder tests whether the candidate assembly is backward compatible with the requested assembly. If not, it throws an exception.