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:

  1. If an assembly with the same name is already loaded, return it.
  2. If it exists in the current directory, load the assembly and return it.
  3. 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 custom AssemblyResolve event. This can potentially trigger infinite recursion and a StackOverflowException.
  • 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 the AssemblyName.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.