When you reference a .Net strong name signed assembly, the CLR will reject any version of that assembly except the one referenced at build time. This can be a problem when you are referencing packages from Nuget and your code or another reference (Nuget or otherwise) requires a different, yet compatible, version of a strong named assembly.
One solution is to put binding redirects into the app.config
:
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="myAssembly"
publicKeyToken="32ab4ba45e0a69a1"
culture="neutral" />
<bindingRedirect oldVersion="1.0.0.0"
newVersion="2.0.0.0"/>
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
If you are like me, then you try to do as much programatically as possible and avoid cluttering up, or even having, an app.config
or web.config
.
Unfortunately, there is no programmitic way to do binding redirects.
There is, however, an AssemblyResolve
event on the AppDomain
that fires when an assembly fails to load. Using this event, we can simply ignore the version of the assembly.
Below is a custom AssemblyResolve
event handler that does the following:
- If an assembly with the same name is already loaded, return it.
- If it exists in the current directory, load the assembly and return it.
- If unable to find the assembly, return
null
(basically what is already happening).
public class MyApplicationRoot
{
public MyApplicationRoot()
{
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomainOnAssemblyResolve;
}
//This bypasses version checking without putting binding redirects in the app.config
private Assembly CurrentDomainOnAssemblyResolve(object sender, ResolveEventArgs args)
{
var assemblyName = new AssemblyName(args.Name);
var alreadyLoadedAssembly = Array.Find(
AppDomain.CurrentDomain.GetAssemblies(),
assembly =>
assembly.GetName().Name
.Equals(assemblyName.Name, StringComparison.OrdinalIgnoreCase));
if (alreadyLoadedAssembly != null)
{
return alreadyLoadedAssembly;
}
var filename = assemblyName.Name + ".dll";
return File.Exists(filename) ? Assembly.LoadFrom(filename) : null;
}
}
Somethings to consider:
- This bypasses a key feature of the CLR and strong named signed assembly loading.
- If you use any of the static
Assembly
load methods, ensure they are successful. If they fail, you will simply return to the same customAssemblyResolve
event. This can potentially trigger infinite recursion and aStackOverflowException
. - This code assumes the assembly file is named the same as the
AssemblyName.Name
and has the extension.dll
. The assembly could very well live in a fle with the extension.exe
or have a file name that differs from theAssemblyName.Name
entirely.
Typically none of my code lives in assemblies that are strong named signed and I avoid references to strong named signed assemblies where possible.
In general, most Nuget package are not strong named signed or the author provides two packages, one signed and one unsigned. But there are a few packages that are strong named signed, like ImpromptuInterface, which is why I had to write this code in the first place.