There is an application (executor.exe) that invokes methods from class library (lib.dll) using reflection.
executor.exe has assembly Newtonsoft.Json version 8.0 as embedded resource.
lib.dll has reference to Newtonsoft.Json version 9.0.
lib.dll has reference to system.net.http.formatting version 4.0.0.21112, which in turn refers to Newtonsoft.Json 4.5.
I don't have the opportunity to modify executor.exe.config (except for testing).
What do I want to get:
new JsonMediaTypeFormatter().SerializerSettings;
Invoked from lib.dll. But it fails with:
Method not found: 'Newtonsoft.Json.JsonSerializerSettings
System.Net.Http.Formatting.JsonMediaTypeFormatter.get_SerializerSettings()'
What I was trying to do:
Handling AppDomain.CurrentDomain.AssemblyResolve (subscribed correctly, using ModuleInitializer). But it doesn't rise. After crash have 2 Newtonsoft.Json (with different versions) loaded to AppDomain.
Binding in app config:
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed"
culture="neutral" />
<bindingRedirect oldVersion="4.0.0.0-5.0.0.0" newVersion="9.0.0.0" />
Yes, it works. But I can't use this solution. After passing have 2 Newtonsoft.Json (with different versions) loaded to AppDomain.
I don't understand why this works (oldVersion="8.0.0.0-9.0.0.0") but:
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
<bindingRedirect oldVersion="8.0.0.0-9.0.0.0" newVersion="9.0.0.0" />
exception "Method not found" doesn't throw. After passing have 1 Newtonsoft.Json (9.0) loaded to AppDomain. But not suitable for me.
Why AppDomain.CurrentDomain.AssemblyResolve doesn't work? I guess the problem is in 2 loaded assemblies but I can not change this behavior.
Why AppDomain.CurrentDomain.AssemblyResolve doesn't work?
Event AppDomain.CurrentDomain.AssemblyResolve is fired if assembly resolution fails. It's not your case since you see Newtonsoft.Json assembly already loaded in the application domain.
You catch MissingMethodException because System.Net.Http.Formatting.JsonMediaTypeFormatter.get_SerializerSettings() returns JsonSerializerSettings declared in Newtonsoft.Json version 4.5.
Unfortunatelly, for soulless CLR JsonSerializerSettings from Newtonsoft.Json 4.5 is not the same at all as JsonSerializerSettings from Newtonsoft.Json 9.0.
To fix this problem, mechanism for redirection of assembly versions was introduced (bindingRedirect that you refer to).
exception "Method not found" doesn't throw. After passing have 1
Newtonsoft.Json (9.0) loaded to AppDomain. But not suitable for me.
Actually that's the solution you should stick to. Your best option is to have only one Newtonsoft.Json assembly loaded into application domain and have version redirection configured.
Why would you reinvent the wheels and try to find solution other than platform offers? If for some strange reason you're prohibited to modify application config, you could add assembly redirection on machine level
See this article for details. But assembly version redirection is the way how eternal DLL hell problem is fixed in .Net. Using any other workarounds (even if you manage to find them) will make you no good.
If you cannot apply binding redirects you can also load correct assembly on application startup use Assembly.LoadFrom method. In main method find Newtonsoft.Json dlls and load one with the version you need. That should avoid loading of assembly with incorrect version. Let me know if you want me to provide a code snippet for you.
Related
Using .NET 4.6.2 and an older Web-Site (not Web-Application) project. If I clear the BIN directory and then build and run it works, but sometimes after multiple builds and runs, it fails with this error.
Server Error in '/' Application.
Cannot load a reference assembly for execution.
....
[BadImageFormatException: Cannot load a reference assembly for execution.]
[BadImageFormatException: Could not load file or assembly 'netfx.force.conflicts' or
one of its dependencies. Reference assemblies should not be loaded for execution.
They can only be loaded in the Reflection-only loader context.
(Exception from HRESULT: 0x80131058)]
....
Is there any trick to getting Web-Site projects to work correctly when the libraries they use are beginning to pull in netstandard 2.0?
Also, it seems that this assembly binding redirect is necessary to get it to run but nuget adds a redirect to an older version 4.1.1.0. Any suggestions on how to fix that other than manually editing this line after most nuget updates?
<dependentAssembly>
<assemblyIdentity name="System.Runtime" publicKeyToken="b03f5f7f11d50a3a"
culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.1.2.0" newVersion="4.1.2.0" />
</dependentAssembly>
Would all these issues go away if the project was migrated to a Web-Application project?
Most likely you are encountering this error because of a mismatch between x86 and x64 compiled assemblies or similarly a mismatch between .NET Framework and .NET Core.
Here are some troubleshooting options for you that you may not have tried.
Option #1
In the AppDomain there is an event handler for when the program is trying to resolve an assembly that is reference. If you subscribe to it, you should be able to get more information about what is missing. This is how the implementation would look:
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
Event Handler:
private System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
System.Diagnostics.Trace.TraceInformation($"Trying to resolve assebly: {args.Name} requested by {args.RequestingAssembly.FullName}");
// This event handler allows you to help the find the assembly for the CLR.
// you can dynamically load the assembly and provide it here.
return null;
}
Option #2
There is also a tools in the .NET SDK for troubleshooting binding issues.
Here is more information about the Assembly Binding Log Viewer
You need to enable it before it will emit any interesting information, but this should get you to the root of your problem, if the AssemblyResolve event doesn't help.
Yes, you can to convert web-site to web application:
If you can't to see this configuration - probably you have problem with names of classes, for example:
/Directory/Index.aspx.cs
/Directory2/Index.aspx.cs
In my windows class library (consumed by a MVC website) I have installed the NugetPackage Microsoft.SqlServer.Types (Spatial).
Now, using ado.net I am trying to read the value by doing:
protected SqlGeography MapSqlGeography(DbDataReader reader, string key)
{
return reader[key] is DBNull ? null : (SqlGeography)reader[key];
}
If I add a brake point in this line and in the visual studio watch window I type: "reader[key]", I can see the correct Point(XXXX,XXX) of type: "object {Microsoft.SqlServer.Types.SqlGeography}"
But, as soon as I try to make the cast I have the following error:
(SqlGeography)reader[key] The type 'Microsoft.SqlServer.Types.SqlGeography' exists in both
'Microsoft.SqlServer.Types.dll' and
'Microsoft.SqlServer.Types.dll'
Main strange fact is that the dlls are exactly the same...
As far as I know I only have one "source" for this namespace/class name, it should not be duplicated....
My "usings" are:
using Microsoft.SqlServer.Types;
using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Data.SqlClient;
using System.Threading.Tasks;
Any ideas on how to solve this? Thanks.
Update #1
I uninstalled the NugetPackage `Microsoft.SqlServer.Types (Spatial)' and instead tried the one called: 'Microsoft.SqlServer.Types (Unofficial)' and even after cleaning all the previous folders/files and also cleaning up the "bin/obj", I continue to have the exact same exception....
I simply do now see the cause of this now.... any ideas would be really appreciated.
Update #2
Just tried to use extern alias destination;
return reader[key] is DBNull
? null
: (destination.Microsoft.SqlServer.Types.SqlGeography)reader[key];
And have the exception:
Cannot cast 'reader[key]' (which has an actual type of 'Microsoft.SqlServer.Types.SqlGeography')
to
'Microsoft.SqlServer.Types.SqlGeography'
Microsoft.SqlServer.Types.SqlGeography
I encountered this error today because a referenced library included a different version of Microsoft.SqlServer.Types from Nuget than the locally installed one.
You can install a matching version using Nuget to resolve the issue, or you may be able to use binding redirects if that is not an option.
For example:
Install-Package Microsoft.SqlServer.Types -Version 10.50.1600.1
Check your specific versions by looking at package.json for your dependencies, or perhaps you can check the DLL properties directly.
The runtime uses the following steps to resolve an assembly reference:
Determines the correct assembly version by examining applicable configuration files, including the application configuration file, publisher policy file, and machine configuration file. If the configuration file is located on a remote machine, the runtime must locate and download the application configuration file first.
Checks whether the assembly name has been bound to before and, if so, uses the previously loaded assembly.
Checks the global assembly cache. If the assembly is found there, the runtime uses this assembly.
Probes for the assembly using the following steps:
If configuration and publisher policy do not affect the original reference and if the bind request was created using the Assembly.LoadFrom method, the runtime checks for location hints.
If a codebase is found in the configuration files, the runtime checks only this location. If this probe fails, the runtime determines that the binding request failed and no other probing occurs.
Probes for the assembly using the heuristics described in the probing section. If the assembly is not found after probing, the runtime requests the Windows Installer to provide the assembly. This acts as an install-on-demand feature.
Note There is no version checking for assemblies without strong names, nor does the runtime check in the global assembly cache for assemblies without strong names.
Now, check to see if you have multiple assemblies referenced (as Damien_The_Unbeliever said in the comment) or you do not have specific version set for that assembly.
Also you can try the Assembly Binding Log Viewer (Fuslogvw) to see exactly what gets loaded and what are the search paths.
Try using this in your web.config file:
<dependentAssembly>
<assemblyIdentity name="Microsoft.SqlServer.Types" publicKeyToken="89845dcd8080cc91" culture="neutral" />
<bindingRedirect oldVersion="10.0.0.0-11.0.0.0" newVersion="14.0.0.0" />
</dependentAssembly>
I'm running an Azure Functions, called SmsWebhook. It calls a method in an external assembly, AzureFunctionsSample.Services.dll that has a reference to Newtonsoft.Json 8.0.3
The details of my Run.csx looks like:
#r "AzureFunctionsSample.Services.dll"
using System.Net;
using AzureFunctionsSample.Services
public static async Task<HttpResponseMessage> Run(HttpRequestMessage req, TraceWriter log)
{
...
}
Within the Run() method above, I create an instance and call a method in the instance. However, whenever I call that method, I receive the following error:
2016-05-19T13:41:45 Welcome, you are now connected to log-streaming service.
2016-05-19T13:41:46.878 Function started (Id=64fccf0c-d0ef-45ef-ac1c-7736adc94566)
2016-05-19T13:41:46.878 C# HTTP trigger function processed a request. RequestUri=https://ase-dev-fn-demo.azurewebsites.net/api/smswebhook
2016-05-19T13:41:46.878 Function completed (Failure, Id=64fccf0c-d0ef-45ef-ac1c-7736adc94566)
2016-05-19T13:41:46.894 Exception while executing function: Functions.SmsWebhook. Microsoft.Azure.WebJobs.Script: One or more errors occurred. AzureFunctionsSample.Services: Could not load file or assembly 'Newtonsoft.Json, Version=8.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040).
I manually added the same version of Newtonsoft.Json.dll under the bin directory, but still got the same error. Why is it complaining at the Newtonsoft.Json.dll file?
If I move all the logics within the external assembly into the Run.csx, it won't complain, by the way.
Json.Net can be simply reference adding this line at the top of your Run.csx file :
#r "Newtonsoft.Json"
See this article if you want to know which assemblies are automatically added by the Azure Functions hosting environment:
Azure Functions C# developer reference
Otherwise, if you want to use a specific version of Json.Net, you should probably add a reference to Json.Net using nuget package:
How can I use NuGet packages in my Azure Functions?
So you need to add a Project.json file that look like this:
{
"frameworks": {
"net46":{
"dependencies": {
"Newtonsoft.Json": "8.0.3"
}
}
}
}
If your external dependency references Newtonsoft.Json without using a nuget package, you can have a look at this post that explains how to upload your binaries:
How do I use external assemblies with Microsoft Azure Function Apps?
#JustInChronicles, I'm adding this here as an answer for reference, but the expected behavior should be that indirect dependencies of private assemblies are resolved from your bin folder, as expected.
I put together the following test to reproduce your scenario:
Created a simple class library with a simple type that uses Json.NET to serialize an object and return the JSON string. This assembly references Json.NET 8.0.3. The result includes the Json.NET assembly version it is using
Created a function that references that type only with a #r "DependencyWithJsonRef.dll" and returns the result produced by the method mentioned above
Deployed DependencyWithJsonRef.dll and Newtonsoft.Json.dll (8.0.3) to my function's bin folder
Invoking the function produces the expected result.
Here is the function, for reference:
#r "DependencyWithJsonRef.dll"
using System.Net;
public static string Run(HttpRequestMessage req, TraceWriter log)
{
var myType = new DependencyWithJsonRef.TestType();
return myType.GetFromJson();
}
As you can see, no explicit reference to indirect dependencies (Json.NET) required.
This is the output I get:
{
"Prop1":"Test",
"Prop2":1,
"AssemblyName": "Newtonsoft.Json, Version=8.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed"
}
Quick note: One thing you may want to check, particularly if you've updated that dependency while developing your function is that assembly resultion results were not cached. A sure way to make sure you're starting with a clean slate is to (after you deploy your function and assemblies) go to Kudu and kill the non-scm w3wp process to see if that helps. I'd be curious to know if that does the trick as there are a few things we can to to improve this if it does.
After some trial-and-error approach. I found what the issue was here.
#FabioCavalcante gave me a hint using a file-based reference,
#r "Newtonsoft.Json.dll"
It didn't work actually. I've copied those four files to Azure Functions' bin directory:
AzureFunctionsSample.Services.dll
AzureFunctionsSample.Services.pdb
Newtonsoft.Json.dll
Newtonsoft.Json.xml
It still gave me the same error, even though I did the file-based reference. Then, I found another file, AzureFunctionsSample.Services.dll.config that actually defines assembly binding redirects like:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-8.0.0.0" newVersion="8.0.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
After I copied this config file to the Azure Functions' bin directory, it worked!
Lessons Learnt
Use the file-based reference for Newtonsoft.Json, #r "Newtonsoft.Json.dll", if your external assembly also has a reference to it.
Make sure that the assembly binding redirect configuration exists in Azure Functions' bin directory.
Correct me, if I'm still wrong.
Cheers,
This question already has answers here:
Is it possible to replace a reference to a strongly-named assembly with a "weak" reference?
(3 answers)
Closed 4 years ago.
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?
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.
No, you can't build MyClassLibrary with a reference to ThirdPartyClassLibrary in a way that says "just use whatever version of ThirdPartyClassLibrary.dll is available at runtime".
When you build your library, the version number of any referenced assemblies are included in the assembly manifest. Running the ILDASM tool against your assembly would show something like this:
...
.assembly extern ThirdPartyClassLibrary
{
...
.ver 1:0:0:0
}
...
Both the name and version of ThirdPartyClassLibrary are specified. At runtime, the CLR will attempt to load ThirdPartyClassLibrary.dll when it first runs instructions in MyClassLibrary.dll that reference it. It will look specifically for version 1.0.0.0 of ThirdPartyClassLibrary.dll (and will also require a matching public key if it's a strong-named assembly).
Here's a quick overview of how the CLR locates and binds to assemblies at runtime (full details at http://msdn.microsoft.com/en-us/library/yx7xezcf(v=vs.110).aspx):
Step 1 - Determine the correct assembly version by examining configuration files - we'll return to this below, but for now, if you don't tell it otherwise, the CLR will attempt to load the exact version specified in the referencing assembly, so it will be looking for version 1.0.0.0.
Step 2 - Check whether the assembly name has been bound to before and, if so, uses the previously loaded assembly. Note that "assembly name" in this context includes the name and version, public key token etc, not just the name of the dll file.
Step 3 - Check the Global Assembly Cache GAC (strong-named assemblies only)
Step 4 - Locate the assembly through codebases or probing - essentially the CLR looks in different places to try to find (the specific version of) AssemblyB.dll somewhere. An error will occur if it can't find the specific version. It won't automatically fall back to an earlier or later version.
Unfortunately, this means that things won't "just work" and support what you describe above. If an application that references MyClassLibrary itself references a later version (2.0.0.0) of ThirdPartyClassLibrary, some bad things could happen when resolving MyClassLibrary's reference to ThirdPartyClassLibrary:
The CLR can't find version 1.0.0.0 of AssemblyB used by Assembly A and an error occurs
Version 1.0.0.0 happens to be installed in the GAC and is loaded successfully. While the code in the application is using ThirdPartyClassLibrary version 2.0.0.0, your library is using ThirdPartyClassLibrary version 1.0.0.0.
One thing that you can do is configure the application using your library so that the CLR will unify references to different versions of ThirdPartyClassLibrary.dll to a single version. This brings us back to step 1 of the assembly binding process outlined above - we essentially change the version of ThirdPartyClassLibrary that the CLR is looking for.
Binding redirects (http://msdn.microsoft.com/en-us/library/twy1dw1e.aspx) are designed to channel references to different versions of an assembly to a single version. These are usually defined within an application's configuration file (Web.config, MyApp.exe.config), but can also be defined globally at machine level (machine.config).
Here's an example of a binding redirect that redirects all earlier versions of ThirdPartyClassLibrary.dll to version 2.0.0.0:
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="AssemblyB" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-2.0.0.0" newVersion="2.0.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
Note that this can be automatically handled by Visual Studio 2013, which detects cases where different versions of an assembly are referenced and adds the binding redirects for you. There's also an Add-BindingRedirect command available in the NuGet Package Manager Console.
Binding redirects are a possible solution and could be a pragmatic choice in some scenarios. However, they could also confuse users of your library. If practical, you should consider distributing different versions of your library, built against the different versions of the third party library.
I have a WPF/C# application that references .NET 4.0 assemblies. However, within the application is a text editor that needs to display C# intellisense tied to .NET 3.5 assemblies. Thus, I want to be able to load the appropriate .NET 3.5 assemblies at runtime. However, when I try to use:
Assembly.Load()
Assembly.LoadFile()
Assembly.LoadFrom()
I always get the latest version from the GAC. Reading their MSDN pages, it seems like that's unfortunately by design.
I tried following the proposed solutions in this stack overflow post. However, the linked webpages' solutions don't work and a remoting process seems like overkill. Does anyone know a better and/or easier way to load older .NET assemblies at runtime?
By default, you cannot load assemblies made for previous versions of the .NET framework on the .NET framework version 4.
However, there is a configuration attribute that allows that: http://msdn.microsoft.com/en-us/library/bbx34a2h.aspx
Put the following lines in your app.config file:
<configuration>
<startup useLegacyV2RuntimeActivationPolicy="true|false" >
</startup>
</configuration>
There are reasons why this is not active by default, but I believe that it should be appropriate for your use.
I've done this before, albeit from assemblies that weren't in the GAC: Mine were being loaded from byte arrays, but I could easily have different versions of the same assembly.
The solution was to handle the AssemblyResolve event of the AppDomain, so that you can ensure that the assembly you require is returned. In your case this could be fairly simple, as you only care when doing this particular invocation. The rest of the time you'd take the default and not handle the even at all.
A possible example might be:
public DoSomething()
{
//Add the handler
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve
//Load your assembly...
}
private System.Reflection.Assembly CurrentDomain_AssemblyResolve(Object sender, ResolveEventArgs e)
{
foreach (System.Reflection.Assembly a In AppDomain.CurrentDomain.GetAssemblies())
{
if (a.FullName == <<The one you need>>) return a
}
}
This is pretty crude, but it'll give a idea of the process - you handle the assemblyresolve, and return what you need. The contents of your handler will likely be different as I'm not sure your assembly will be present in the CurrentDomain.GetAssemblies() list.
There are probably more subtle examples of assmeblyresolve out there that will handle GAC versions for you.
Notes: This was used in .Net 3.5, not 4, but did work for different versions. You may need #Jean's solution to load 3.5 assemblies in 4.
You can use the AssemblyBinding property in your app.config or web.config, whichever is appropriate.
For instance, I'm interfacing with Matlab, so I need this...
<loadFromRemoteSources enabled="true" />
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="MWArray" publicKeyToken="E1D84A0DA19DB86F" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-2.10.0.0" newVersion="2.10.0.0" />
</dependentAssembly>
</assemblyBinding>
Because the Matlab engine I work with is compiled (some crazy how) on a machine using a different copy of the runtime.
You should be able to modify this code to suit your needs.
One partial solution I discovered is to use Assembly.ReflectionOnlyLoadFrom(). This method correctly loads older versions of assemblies, although the assembly is loaded into a reflection-only context, meaning you can't execute code in the assembly. While this isn't a true assembly load, it does enable most of the intellisense scenario I'm working on.
The one lingering problem is mscorlib. When trying to use the assembly returned from ReflectionOnlyLoadFrom, for an older version of mscorlib, an exception is thrown: "System.TypeLoadException: Could not load type 'System.Object' from assembly 'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' because the parent does not exist."
I solved this problem using ReflectionOnlyLoadFrom() to load assemblies of whatever version that I need in read-only mode for use with reflection only. When it comes to the fact that you can't do this for mscorlib (you can ask for 2.0, but you'll get 4.0 back from the call), I handled this by filtering the types returned based upon the version that I needed. After all, in this mode, you can only inspect the types in the DLL using reflection, and not really do anything else with them, so you can easily filter the list of types returned as needed.
Here are the types that I've filtered out of mscorlib 4.0 so far so that everything works when an older version of mscorlib is expected:
/// <summary>
/// Types to be hidden from .NET mscorlib 4.0 for older versions.
/// </summary>
protected static readonly HashSet<Type> HideMscorlib4Types = new HashSet<Type>
{
// The Action delegates with 0, 2, 3, 4 type parameters were moved from System.Core 3.5 to mscorlib for 4.0
typeof(Action), typeof(Action<,>), typeof(Action<,,>), typeof(Action<,,,>),
// The Func delegates with 1 to 5 type parameters were moved from System.Core 3.5 to mscorlib for 4.0
typeof(Func<>), typeof(Func<,>), typeof(Func<,,>), typeof(Func<,,,>), typeof(Func<,,,,>),
// Other types moved from System.Core 3.5 to mscorlib 4.0
typeof(TimeZoneInfo),
// Hide various types that were new in mscorlib 4.0
typeof(Tuple)
};