Reputation: 5650
I've been tasked in creating a new module for an application, and so, I'm adding new DLLs to the project. This is all fine and well.
However, in my DLLs I'd like to use a new version of an external DLL (over which I have no control). If I just reference the new DLL and work with that one only, my code will work, but the old code will stop functioning.
Could not load file or assembly 'itextsharp, Version=5.0.6.0, Culture=neutral,
PublicKeyToken=8354ae6d2174ddca' or one of its dependencies. The located assembly's
manifest definition does not match the assembly reference. (Exception from HRESULT:
0x80131040)
I've tried a simple trick of changing the DLLs name, but that apparently was a bit too naive of me, to think it would work. I've tried using the external aliases (by defining them in my references), but I still don't know how to get two files with the same name into one BIN folder...
What should I do?
Upvotes: 68
Views: 94518
Reputation: 18289
Let's assume you have a project structure as follows:
...where A
and B
are class libraries, and C
is an executable-type project (such as a unit test or console project).
Let's assume the folder structure is like this:
ABC.sln
A/A.csproj
A/...
B/B.csproj
B/...
C/C.csproj
C/...
lib/thirdparty4/thirdparty.dll
lib/thirdparty5/thirdparty.dll
If we attempted to naively reference our projects together, we'd have a problem: two versions of thirdparty.dll
will be copied into the same folder (the output (i.e., bin) directory of C
). We need a way for C
to copy both dlls into its output directory, and provide a mechanism for referencing either one.
To solve this, I modified C.csproj
to contain the following:
<ItemGroup>
<Content Include="..\lib\thirdparty4\thirdparty.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<Link>thirdparty4\thirdparty.dll</Link>
</Content>
<Content Include="..\lib\thirdparty5\thirdparty.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<Link>thirdparty5\thirdparty.dll</Link>
</Content>
</ItemGroup>
This will instruct it to create both thirdparty4\thirdparty.dll
and thirdparty5\thirdparty.dll
in its output directory.
Now, after building C
, its output directory looks like this:
C\bin\Debug\A.dll
C\bin\Debug\B.dll
C\bin\Debug\C.dll
C\bin\Debug\thirdparty4\thirdparty.dll
C\bin\Debug\thirdparty5\thirdparty.dll
To instruct C
to use both of these dlls, I added an App.config
file to it, with the following:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="thirdparty" culture="neutral" publicKeyToken="1234567890123445"/>
<bindingRedirect oldVersion="4.0.0.0-4.0.0.0" newVersion="4.0.0.0" />
<codeBase version="4.0.0.0" href="thirdparty4\thirdparty.dll" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="thirdparty" culture="neutral" publicKeyToken="1234567890123445"/>
<bindingRedirect oldVersion="5.0.0.0-5.0.0.0" newVersion="5.0.0.0" />
<codeBase version="5.0.0.0" href="thirdparty5\thirdparty.dll" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
This will instruct the assembly to, depending on which version is in need, use one DLL or the other, both of which will be available within subfolders of the output directory. (The bindingRedirect elements are optional, but you can use them if you need a range of revisions for this to apply to.)
Upvotes: 72
Reputation: 8311
If someone is interested in how to achieve this on a ASP.NET-MVC
project, then read on:
I had a requirement to switching a DB connector of a particular version during runtime based on a appsetting key (coming from web.config
) so this is how I went about it.
In your Global.asax
file, under the Application_Start
method, I simply added the AssemblyResolve
event handler to AppDomain.CurrentDomain
:
using System;
using System.Reflection;
using System.IO;
using System.Configuration;
protected void Application_Start()
{
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolveForMyConnector);
//rest code omitted for brevity
}
Then I created a method that would do the switching at runtime based on my configuration setting. In this I placed my new version of the dll file under NewVesion
folder and the original version of the dll resides in the bin
folder:
static Assembly CurrentDomain_AssemblyResolveForMyConnector(object sender, ResolveEventArgs args)
{
bool AssemblySwitch = Convert.ToBoolean(ConfigurationManager.AppSettings["AssemblySwitch"]);
if (AssemblySwitch)
{
//Get custom path where new version of dll resides
string appPath = AppDomain.CurrentDomain.BaseDirectory;
string latestdllpath = Path.Combine(appPath, "NewVersion");
return Assembly.LoadFrom(latestdllpath+"\\mynewversion.dll");
}
else
{
//Get bin path of the project
var binpath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, AppDomain.CurrentDomain.RelativeSearchPath ?? "");
return Assembly.LoadFrom(binpath+"\\myoriginalversion.dll");
}
}
Full Global.asax
file:
using System;
using System.Reflection;
using System.IO;
using System.Configuration;
namespace MyProject
{
public class MvcApplication : System.Web.HttpApplication
{
static Assembly CurrentDomain_AssemblyResolveForMyConnector(object sender, ResolveEventArgs args)
{
bool AssemblySwitch = Convert.ToBoolean(ConfigurationManager.AppSettings["AssemblySwitch"]);
if (AssemblySwitch)
{
//Get custom path where new version of dll resides
string appPath = AppDomain.CurrentDomain.BaseDirectory;
string latestdllpath = Path.Combine(appPath, "NewVersion");
return Assembly.LoadFrom(latestdllpath + "\\mynewversion.dll");
}
else
{
//Get bin path of the project
var binpath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, AppDomain.CurrentDomain.RelativeSearchPath ?? "");
return Assembly.LoadFrom(binpath + "\\myoriginalversion.dll");
}
}
protected void Application_Start()
{
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolveForMyConnector);
}
}
}
Upvotes: 0
Reputation: 131
If the AppDomains solution isn't applicable in your case, you are under time pressure, have conflicting requirements (like that ever happens), and don't mind ridiculously contrived hacks:
Hey, don't look at me like that. I did say ridiculously contrived hack ...
Upvotes: 13
Reputation: 393674
You can load another version into a specific AppDomain
Possibly too detailed, but here is an article that demonstrates the use of AppDomains in a useful setting and how they work:
http://msdn.microsoft.com/en-us/magazine/cc164072.aspx
In a very basic sense it comes down to this sample code:
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
...
static System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
if (/*some condition*/)
return Assembly.LoadFrom("DifferentDllFolder\\differentVersion.dll");
else
return Assembly.LoadFrom("");
}
Upvotes: 25
Reputation: 3013
You can also rely on assembly binding redirection for your strong named assembly as described in http://msdn.microsoft.com/en-us/library/2fc472t2.aspx.
You would only have one version of the file (the latest), and both references would resolve to it.
Upvotes: 0