7

I want to create a single dll that is merged with a 3rd party dll. This means end consumers will only have to deal with 1 dll instead of 2.

For augments sake lets say that the 3rd party dll is nLog. How do I deal with cases where the consumer of the merged dll already has NLog as a reference in their project?

Ideally what I would like to be able to do is change NLog namespace within my project to "XyzNLog", meaning that the user wouldn't need to do any aliasing... Any idea how I might do this?

Now I know I can add aliases to my project for NLog so that I have to refer to it as XyzNLog, but I want the same to carry over to consumers of the merged dll so that there is never a conflict.

UPDATE - Solution

http://blog.mattbrailsford.com/2010/12/10/avoiding-dependency-conflicts-using-ilmerge/

Bingo! So by using ILMerge, it becomes possible to merge the third-party libraries DLLs in with the Providers own DLL, meaning we will only have one DLL to deploy. But that’s not all, we can actually go one step further, and tell ILMerge to internalize all dependencies. What this does it converts all the third party classes to be declared as internal, meaning they can only be used from within the final DLL. Woo hoo! problem solved =)

Given this the problem where the consumer of my dll could also have NLog goes away... as my referenced NLog shifts to being all internal! This is exactly what I want.

Does anyone have any feedback or thoughts on this?

Cœur
  • 37,241
  • 25
  • 195
  • 267
vdh_ant
  • 12,720
  • 13
  • 66
  • 86
  • 9
    Yes, this has DLL Hell written all over it. The worst kind, the self-induced kind. There's no cure for that. – Hans Passant May 02 '11 at 02:06
  • @Hans Passant: So if my component uses nLog version x I have to *force* my customers to use version x of nLog *for their stuff* too and no other? There must be solution, right? – Andrew Savinykh May 02 '11 at 06:05
  • @zespri this is a similar situation that I am in but I don't want to use the GAC and I don't want the users of my dll to have to worry about this implementation detail... so thats why I am looking for a solution and feel something must be out there. – vdh_ant May 02 '11 at 10:47
  • that's the same approach I suggested in my EDITED response below, but please see the link for possible pitfalls. – dawebber May 02 '11 at 20:48

2 Answers2

1

I agree with Hans, I would strongly suggest releasing with registering the DLLs separately.

Otherwise, you could be in DLL hell which would drive your consumers away.

You could then devise some clever deploy methods to detect if the DLL is already registered, etc.

Mark Kadlec
  • 8,036
  • 17
  • 60
  • 96
0

I have to agree with @Hans Passant (and here's some info about the oft-discussed DLL hell), but since you've asked the question, I'll try to answer it.

You can bundle the third-party DLL as a resource. Please see this question for details.

As far as your other questions, I'd just expose the relevant classes from a third-party DLL under your own namespace, and maybe use extension methods to provide whatever additional functionality you want.

For instance, you can provide access to NLog's Log() method using a static method in your class, say XyzNLog.Logger.Log(), taking care of initialization, and whatever else internally, inside your code (static constructor or whatever else you fancy up). Since you load the NLog assembly using the method above, you'll be the only one having access to the embedded NLog assembly directly and the user won't be able to access it. Now, you don't get the benefit of having all classes autoexposed from NLog, you still have to expose them manually in this case.

EDIT: Another approach would be to try to use ILMerge with /internalize flag as described here. You may not be able to completely resolve the issue, but look at this article to see if you can avoid the pitfalls the author described. Spoiler alert: it's not all peaches'n'cream on this one either, but it may work, with enough extra effort.

Community
  • 1
  • 1
dawebber
  • 3,503
  • 1
  • 16
  • 16
  • In the example you have here, the part I don't get is that the static method Logger has to return a type... What type would I be returning If I don't have a reference to NLog in my project? – vdh_ant May 02 '11 at 02:51
  • @vdh_ant, the Logger class is your class wrapping NLog's Logger, which you get by reflection using the link I referenced. In other words, you expose your own classes instead of NLog's. You can always expose interfaces also (`ILogProvider`), but in either case you won't have compile-time access to NLog's classes, and the only run-time access would be via reflection or similar. – dawebber May 02 '11 at 03:31
  • Humm so if the class dll that I'm wrapping has a much more complex API then its much harder :( – vdh_ant May 02 '11 at 10:45
  • "You can bundle the third-party DLL as a resource". This works good if you are writing an exe, but not so good if you are writing a dll that other people will use. Generally it's not ok for a dll to mess around with AppDomain by subscribing to AssemblyResolve event. It can lead to a nasty surprise to the author of exe who uses the dll. – Andrew Savinykh May 02 '11 at 10:57
  • @vdh_ant: this library, that you are using, does it come in form of source code? Do you have an option of recompiling it yourself? – Andrew Savinykh May 02 '11 at 11:00
  • Ya I'm simply authoring a dll, so that makes using the described method about hard. Also I may have access to the source (which would make everything easy as I can just change the namespaces in the source) but what If I didn't? – vdh_ant May 02 '11 at 11:09
  • @zespri, what is a concern you see with handling AssemblyResolve event in a DLL? – dawebber May 02 '11 at 15:46
  • @vdh_ant, you can look at using Cecil library to extract the info at run-time, but the problem still remains--you can't expose the methods cleanly, other than using some sort of `Invoke(method)` signature semantics, returning `dynamic` objects, which sounds like a nightmare for the end user of your library. You've asked the question, so I'm trying to answer, but honestly, the approach itself doesn't seem clean enough. – dawebber May 02 '11 at 15:49
  • @vdh_ant, I just stumbled upon another approach, which you may want to try. I've added it to the answer. – dawebber May 02 '11 at 15:59
  • @dawebber: the concern is that a library should not introduce non-apparent side effects. The exe might use AssemblyResolve for it's owe purposes it might even use it for resolving the same of the same version. It might want to load it differently. Also you have to deal with order in which the even handlers are run. Of course you can put in documentation, is that "the library uses AssemblyResolve to load it's dependencies. If you want to use AssemblyResolve you need to do it before you init the library. You should not try to load these listed dependencies yourself" But his defeats the purpose. – Andrew Savinykh May 02 '11 at 20:22
  • @Zespri, I agree with "non-apparent side effects" statement, but Microsoft is using this in the Application Blocks (Enterprise Library or whatever it's called now), as well as several other places. I've looked at [this link](http://msdn.microsoft.com/en-us/library/dd153782.aspx), and while they did show some best practices of using `AssemblyResolve`, none of them seems to indicate the same concern you have. Yet, your concern is valid as a possible side effect, IMO. It goes more towards the entire concept of statically linking 3rd party code in as discussed on multiple threads, but that's OT. – dawebber May 02 '11 at 20:45
  • @dawebber: I just searched the source code of Enterprise Library 5.0 and the only place where AssemblyResolve is in the component that supports the Visual Studio designer. (Microsoft.Practices.EnterpriseLibrary.Configuration.DesignTime.dll) I don't think that any other exe but VS will ever reference it and since both exe and dll is from the same manufacturer this is fine. – Andrew Savinykh May 02 '11 at 22:36
  • @dawebber: The link that you gave IMO talking about slightly different things. It's in the 'Deploying the .NET Framework and Applications' chapter, and while this is open for interpretation, my opinion is that consideration for deploying applications vs libraries are distinctively different. AppDomain is "global". Executable is what is supposed to control it, not some library. Btw, we can put up an SO question, asking community opinion =) – Andrew Savinykh May 02 '11 at 22:43
  • @zespri--this is becoming an interesting discussion, and I think that I stand to learn something new if we get community involved. My concern is that our discussion as a set of comments is starting to detract from the OP's question. So, I'm game for a new SO thread! – dawebber May 03 '11 at 00:55
  • @zespri--couple more places Microsoft does it in: [Prism](https://prism.svn.codeplex.com/svn/V4/PrismLibrary/Desktop/Prism/Modularity/AssemblyResolver.Desktop.cs) and WPF/Silverlight runtimes (this is an [indirect link](http://connect.microsoft.com/VisualStudio/feedback/details/526836/wpf-appdomain-assemblyresolve-being-called-when-it-shouldnt), to a Connect bug) – dawebber May 03 '11 at 01:04