Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I have my assembly reference any version of another assembly? [duplicate]

  • I am developing a class library (MyClassLibrary).
  • I depend on a third party class library (ThirdPartyClassLibrary).
  • I need to use the same version of ThirdPartyClassLibrary as my users. e.g., if I set a static value in ThirdPartyClassLibrary the user needs to see that change.
  • Users of my class may be depending on any one of 4 different versions of ThirdPartyClassLibrary.
  • ThirdPartyClassLibrary is large, I do not want to distribute it with my software.
  • I have reflected on all 4 versions of ThirdPartyClassLibrary and validated that the things I will be doing with them are compatible across all versions (interfaces are the same, methods signatures are the same, etc.).
  • I need calls into ThirdPartyClassLibrary to be performant! I can't reflect on everything every time I need to call something.
  • MyClassLibrary will be loaded at runtime, so I can't expect users to mess with assembly binding redirects or other develop-time settings (or any settings at all, my users are resistant to doing anything).
  • I would like to benefit from compile-time checking of my code, so ideally no reflection at all.

How can I write MyClassLibrary such that when it is loaded into the process everything works correctly with whichever version of ThirdPartyClassLibrary the user has loaded?

like image 999
Micah Zoltu Avatar asked Jun 04 '14 21:06

Micah Zoltu


People also ask

What are assembly references?

Reference assemblies are usually distributed with the Software Development Kit (SDK) of a particular platform or library. Using a reference assembly enables developers to build programs that target a specific library version without having the full implementation assembly for that version.

Which component of the assembly stores the version number?

The version number is stored in the assembly manifest along with other identity information, including the assembly name and public key, as well as information on relationships and identities of other assemblies connected with the application.


1 Answers

One workaround would be to use the AppDomain.AssemblyResolve event at runtime. This fires whenever the resolution of an assembly fails. You can use this to load a different version of an assembly to that which the CLR is trying to load.

I've added a very simple demo on GitHub here:

https://github.com/danmalcolm/AssemblyResolutionDemo

This is set up as follows:

  • The main application App.exe directly references assembly ThirdPartyLibrary.dll version 2.0.0.0.

  • It also references MyLibrary, which references an older version of ThirdPartyLibrary version 1.0.0.0.

  • The AppDomain.AssemblyResolve event is used to redirect to the version used by the application when version 1.0.0.0 fails to load

AssemblyResolve is handled as follows:

public static void Initialise()
{
    AppDomain.CurrentDomain.AssemblyResolve += ResolveThirdPartyLibrary;
}

private static Assembly ResolveThirdPartyLibrary(object sender, ResolveEventArgs args)
{
    // Check that CLR is loading the version of ThirdPartyLibrary referenced by MyLibrary
    if (args.Name.Equals("ThirdPartyLibrary, Version=1.0.0.0, Culture=neutral, PublicKeyToken=fbcbfac3e44fefed"))
    {
        try
        {
            // Load from application's base directory. Alternative logic might be needed if you need to 
            // load from GAC etc. However, note that calling certain overloads of Assembly.Load will result
            // in the AssemblyResolve event from firing recursively - see recommendations in
            // http://msdn.microsoft.com/en-us/library/ff527268.aspx for further info
            var assembly = Assembly.LoadFrom("ThirdPartyLibrary.dll");
            return assembly;
        }
        catch (Exception exception)
        {
            Console.WriteLine(exception);
        }
    }
    return null;
}

We need to bind to the event before ThirdPartyLibrary is loaded, hence the explicit Initialise method.

Note also that the event only fires when the resolution of an assembly fails. If the version of ThirdPartyLibrary referenced by MyClassLibrary (1.0.0.0) were available in the GAC, then it would be loaded successfully and AssemblyResolve wouldn't fire. There would then be 2 different versions in use.

I'm demonstrating here that this mechanism could be used, I'm not saying it's a good idea. There are several things you'd need to take into account based on the environment in which your app is running and how it is set-up / installed / maintained etc.

like image 115
Dan Malcolm Avatar answered Oct 04 '22 02:10

Dan Malcolm