Third party components referencing different versions of the same assembly
You should reference 1.2.10 in your solution and add a binding redirect in the app.config to point 1.2.9 to 1.2.10 - something like this:
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="log4net" publicKeyToken="1b44e1d426115821" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-1.2.10.0" newVersion="1.2.10.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
Reference multiple versions of the same DLL
In your consuming project (Project A), create a common interface (ISpiroPdfAlex
) that encompasses all the functionality that the 3 versions of your external assembly provides (and you use). You cannot reference anything in Project A from these wrappers in any way, otherwise you'd create a dependency, which is what you're trying to avoid.
Have all 3 wrapper projects import Project A and implement ISpiroPdfAlex
. This will give you the ability to call each of the 3 different versions through the same API.
After this, create a subfolder under Project A for each of the versions (so 3 subfolders total) - since Project A has no reference to any of the external assemblies, it cannot load them by itself - you'll have to manually load them when you need the right version. Since your external DLLs may have dependencies with the same name, they cannot all be in the same folder (as you wrote), this is why you need the subfolders.
At run-time when you need one of these versions, you can call Assembly.LoadFile
to load a specific version of your assembly from the specified folder and then you can either use Activator.CreateInstance
or dependency injection to create an instance of a class that implements your interface. Once you have the instance, you're free to call any of the functions and you'll get version-dependent behavior.
Edit:
OP mentioned in a comment that it's not his code that has the dependency on different versions of the PDF library but the other 3rd-party Spire libraries that his code depends on.
In this case, the 3rd-party code cannot be modified to support dynamic loading of assemblies and they already have a binary dependency. It's not possible to load different versions of the "same" assembly into the same process, especially that you mentioned that these versions are not even backward-compatible with each other.
The only solution I can think of in this situation is to break out all dependent functionality into separate console applications (one for each different version) and call those separate .exe-s through the command-line.
To pass information, you can either pass data directly on the command-line or through stdin
. Alternatively, you can just pass the name of a temporary file that has all data necessary to do some processing. To get return data back from the console process, you can either read its stdout
or use the same / different file.
This way your main process never loads any of these assemblies and has no dependency on them - each console application has a dependency on just one version so there's no collision.
Using multiple versions of the same DLL
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.)
How can I enforce a 3rd party dll to use a newer version of another dll in my solution?
You can define a binding redirect in your config file. This will attempt to up-version all the boo.dll references to the defined version.
How to deal with two libraries that need different versions of an assembly?
You need an assembly binding redirect. For example, in your web.config
file:
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" culture="neutral" publicKeyToken="30ad4fe6b2a6aeed" />
<bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
That basically says "If you want to use any version of Newtonsoft.Json
earlier than 9, just load v9 instead."
This only works because Json.NET hasn't made (many?) breaking changes between versions. With full SemVer, using major version numbers for breaking changes (and embracing that possibility), I suspect we'll see more difficult situations in the future...
Related Topics
System.Drawing Out of Memory Exception
How to Add Event Handler for Dynamically Created Controls at Runtime
Why Is Infinity Printed as "8" in the Windows 10 Console
Access Form Component from Another Class
Loading Image from Code Using Relative Path in Windows Forms
How to Get the Correct Ip from Http_X_Forwarded_For If It Contains Multiple Ip Addresses
How to Split a String into Multiple Values
Ilookup<Tkey, Tval> VS. Igrouping<Tkey, Tval>
JSON Serialize Properties on Class Inheriting List
Customattribute Reflects HTML Attribute MVC5
System.Net.Webexception: the Remote Name Could Not Be Resolved:
Find Methods That Have Custom Attribute Using Reflection
Mstest Cannot Find the Assembly
Linq to Entities Does Not Recognize the Method 'System.Web.Mvc.Fileresult'