3

I have a project which needs to indirectly use three different versions of a third-party library. These versions are incompatible with each other, so I can't use a binding redirect - it has to be the exact .dll file. (The libraries are Spire.Doc, Spire.XLS & Spire.PDF; the Spire.PDF DLL is referenced by all three)

I have separated the three components into individual wrapper projects, and created classes which wrap direct references to anything in the libraries. However, this doesn't solve my issue: the 'consuming' project still has to copy all of the libraries to the bin folder in order to run. The build process doesn't know which version to copy, and so just copies the latest one. This gives me runtime exceptions due to the wrong DLL being present.

What I've considered/tried:

  • Adding a binding redirect to a specific version (runtime exception because the exact version of the library is not found)
  • Using a post-build step to merge the wrapper projects (again a runtime exception complaining about the absence of the library DLL)
  • Creating separate console applications for each part of the application, then invoking them in a separate - this is a complicated last resort that I'd really rather not do!

I have read that extern alias might be able to help - but as far as I can tell, you can only distinguish between assemblies with different names. The Spire.PDF library has the same name in each project (and the same signed public token).

How can I use these three separate versions of the library independently in the same solution?

Edit:

This issue is slightly different to the suggested duplicate because I don't have the ability to change any code in the dependent libraries. Spire.Doc relies on a different version of Spire.PDF to Spire.XLS

Alex
  • 7,639
  • 3
  • 45
  • 58
  • Interesting quesion! I think you may have to go for reflection and loading dlls at run time. Is this a possibility? – Muthu Sep 25 '15 at 15:15
  • Maybe I don't completely understand the problem, but as I see it, the problem is that Visual Studio copies the files one over the other. Why not just tell it not to do that? – Dialecticus Sep 25 '15 at 15:16
  • Also, maybe this question could help: http://stackoverflow.com/q/5260404/395718 – Dialecticus Sep 25 '15 at 15:20
  • There is a solution if the assemblies are strongly named in the following possible duplicate: [Using different versions of the same assembly in the same folder](http://stackoverflow.com/questions/2460542/using-different-versions-of-the-same-assembly-in-the-same-folder) – Kevin Cathcart Sep 28 '15 at 17:39
  • If you want to use the three library at the same solution, you need use the .dll in the Spire.Office that contains compatible Spire.Doc, Spire.XLS and Spire.PDF. – Passion4Code Oct 08 '15 at 06:39
  • @Passion4Code yep but my company doesn't want to pay for the license :( – Alex Oct 08 '15 at 09:15
  • To my knowledge, if you have the license of Spire.Doc, Spire.XLS & Spire.PDF, you can also use the .dll in the Spire.Office with no need of the license of Spire.Office. – Passion4Code Oct 08 '15 at 09:24
  • You say, you have tried binding redirection. Do you mean, you have defined for the third-party Dlls a DllName.dll.config file and it didn't work? (see [here](http://stackoverflow.com/a/3337358/880076)) – Mehrdad Mirreza Sep 19 '16 at 15:43
  • @MehrdadMirreza I now can't remember the details of my situation but I definitely didn't try that - perhaps it would have but I no longer have the issue! If you think it'd help someone else then perhaps you could submit it as an answer – Alex Sep 19 '16 at 16:30
  • http://roman.tumaykin.com/tools/2016/07/12/Plugins-Isolation-Without-AppDomains-Overhead.html – Niklas Peter May 07 '21 at 11:49

1 Answers1

3

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.

xxbbcc
  • 16,930
  • 5
  • 50
  • 83
  • I still have a problem that the libraries have dependencies between themselves - so Spire.Doc is trying to load Spire.PDF `3.4.108.54040` but Spire.XLS needs version `3.4.183.55040` – Alex Sep 28 '15 at 09:20
  • @AlexFoxGill I updated my answer following your comment. – xxbbcc Sep 28 '15 at 17:21
  • 2
    Thanks - I did end up going down the console app route as I couldn't think of any other way to do it! – Alex Sep 29 '15 at 08:58