MyClassLibrary
).ThirdPartyClassLibrary
).ThirdPartyClassLibrary
as my users. e.g., if I set a static value in ThirdPartyClassLibrary
the user needs to see that change.ThirdPartyClassLibrary
.ThirdPartyClassLibrary
is large, I do not want to distribute it with my software.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.).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).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?
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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With