My goal is to execute some "piece of code", which uses certain assembly, over multiple versions of that assembly. The way I'm doing this is by executing that "piece of code" on separate AppDomains, one for each assembly version.
I was able to do this only when the "piece of code" uses the assembly through a reflection, but what I would like is to have that "piece of code" written in strongly typed manner.
In other words, let's say I have the following assembly:
namespace ClassLibrary1
{
public class Class1
{
internal const string Version = "1.0.0.0";
public string Method1() { return Version; }
}
}
Also it has the following definition in AssemblyInfo.cs:
[assembly: AssemblyVersion(ClassLibrary1.Class1.Version)]
Now let's say I have a "Versions" folder in which I have multiple versions of that assembly, for example:
/Versions/
├─ /1000/
│ └─ ClassLibrary1.dll
├─ /1001/
│ └─ ClassLibrary1.dll
└─ /1002/
└─ ClassLibrary1.dll
Now to execute the "piece of code" I'm using the following console application:
class Program
{
static void PieceOfCode(Assembly assembly)
{
Type class1Type = assembly.GetType("ClassLibrary1.Class1");
dynamic class1 = Activator.CreateInstance(class1Type);
string vesion = class1.Method1();
Console.WriteLine(vesion);
}
public sealed class SeparateDomainExecutor : MarshalByRefObject
{
public void Execute(Action<Assembly> action, string assemblyPath)
{
action(Assembly.LoadFrom(assemblyPath));
}
}
static void Main(string[] args)
{
foreach (string file in Directory.EnumerateFiles(@"C:\Versions", "*.dll", SearchOption.AllDirectories))
{
AppDomain domain = AppDomain.CreateDomain("ClassLibrary1 Domain");
var type = typeof(SeparateDomainExecutor);
var runner = (SeparateDomainExecutor)domain.CreateInstanceAndUnwrap(type.Assembly.FullName, type.FullName);
runner.Execute(PieceOfCode, file);
AppDomain.Unload(domain);
}
Console.Read();
}
}
The console application works fine, but I would like to replace that reflection usage in "PieceOfCode" with something like the following:
static void PieceOfCode()
{
ClassLibrary1.Class1 class1 = new ClassLibrary1.Class1();
Console.WriteLine(class1.Method1());
}
Is this possible?
The problem I have with this is that the PieceOfCode would be written using some specific version of ClassLibrary1 (probably the latest) and I don't see how I could "override" that version in seperate AppDomain. I tried few things, but I always end up with FileLoadException.
Unfortunately when you write ClassLibrary1.Class1
in a statically typed piece of code, you need an assembly reference and the compiler uses that reference to name a given version of the class. Although the fully qualified names (typeof(Class1).AssemblyQualifiedName
) do not contain the path or filename of the assembly, just the assembly name and version, the class loader has further limitations as you might notice:
The way you use Assembly.LoadFrom(...)
and dynamic binding is the best I could come up with. And this is how different versions of Office assemblies are usually treated from applications that integrate them.
The only possible solution I see is to separate piece of code into a separate assembly (say, MyStaticIntegration.dll) compile it against each version of your dependency (ClassLibrary1.dll) separately and then integrate each version of MyStaticIntegration.dll into your application the same way as you did it with ClassLibrary1.dll before.
The same wall-of-dynamic will remain in your application, but you could narrow down the interface you are using dynamically with this trick.