5

Here's my sample scenario. I have a Console application and a class library dll (call it libraryA). The LibraryA dll has a reference to Oracle.DataAccess.dll version 4.112.2.0. The Oracle DLL is in the GAC. The reference to the Oracle DLL in LibraryA is "Copy Local = false." So far so good. If you build the libraryA dll, then Oracle.DataAccess.dll does not show up in its output directory. OK. Now I reference the libraryA dll in my console app. The reference to libraryA dll is "copy local = true". Now, when I build the console application, the Oracle.DataAcess.dll does show up in the output directory for the console application. However, it appears that the ONLY DLL that acts this way is the Oracle dll. Here is the complete code from LibraryA

public void Foo() {            
   Oracle.DataAccess.Client.OracleConnection c = new Oracle.DataAccess.Client.OracleConnection();
   WebMatrix.WebData.OAuthAccountData x = new WebMatrix.WebData.OAuthAccountData("asd", "asd");            
   DevExpress.Web.ASPxCallback.ASPxCallback cvv = new DevExpress.Web.ASPxCallback.ASPxCallback();
}

WebMatrix and DevExpress are also in the GAC just like the Oracle DLL. However, neither of those DLLs are output to the output directory, only the Oracle dll. Why? What's happening here?

For that matter, you can create another class library, call it libraryB, DON'T put libraryB in the GAC, reference LibraryB from LibraryA and set copy local = false. Even when you do that, libraryB is not copied to the output directory of the console app. Of course, in this case, the program blows up because it can't find library B, but at least Visual Studio is respecting the copy local flag = false. What is different about this stupid Oracle DLL?

Oh, one other thing that's funny. If, in my console application, I explicitly add a reference to the Oracle.DataAccess.dll, and say copy local = false, then it doesn't show up in the output directory. It seems kind of hilarious that to not have a DLL show up in the output directory, I have to actually reference it :)

Edit:

Another clue. Oracle, in order to torture developers, do not have one DLL built for AnyCPU. They have an x86 and and an x64 version. In my case, I am referencing an x86 version and building for AnyCPU. However, if I build for x86 (to match the oracle dll), then the Oracle DLL is not copied to the output directory. when building in AnyCPU, MSBUILD says: "warning MSB3270: There was a mismatch between the processor architecture of the project being built "MSIL" and the processor architecture of the reference "Oracle.DataAccess, Version=4.112.2.0, Culture=neutral, PublicKeyToken=89b483f429c47342, processorArchitecture=x86", "x86". This mismatch may cause runtime failures. Please consider changing the targeted processor architecture of your project through the Configuration Manager so as to align the processor architectures between your project and references, or take a dependency on references with a processor architecture that matches the targeted processor architecture of your project." So, it almost looks like Msbuild is ultimately deciding that, ok, you have a mismatch, so let me go ahead and copy this dll to your output directory, thereby guaranteeing that your application will blow up. :)

aquinas
  • 23,318
  • 5
  • 58
  • 81
  • I am dealing with exactly the same odd issue with Oracle.DataAccess.dll, and am getting the same warning in my output. I guess I will have to add a reference to Oracle.DataAccess.dll in all my projects... – Jay Sullivan Nov 12 '13 at 19:18
  • @aquinas, did you ever come to any further resolution of this issue? I'm experiencing what sounds like the exact same issue with another third-party DLL (matching symptoms down to the multiple processor types) and I'm near tearing my hair out. – Sam Hanley Jan 21 '15 at 20:45
  • In our case, we've confirmed that this issue does not reproduce on machines which do not have .NET 4.5 installed, but when someone installs 4.5 the issue begins. We're working to determine what about .NET 4.5 triggers this behavior. – Sam Hanley Jan 21 '15 at 20:46
  • 2
    Unfortunately, my solution consists in consuming alcohol and cursing the fact that Oracle won't put out an AnyCPU build. – aquinas Jan 22 '15 at 04:02
  • Apparently the check for whether the processor types match (or at least the warning about it) was newly added with the release of .NET 4.5, as per http://stackoverflow.com/a/10196549/3486353. It seems like we're stuck in an odd edge case, and either deleting the local copy with a post-build event or adding "copy local=false" references to all projects which use Library A are the only resolutions. – Sam Hanley Jan 22 '15 at 14:05

2 Answers2

5

By not referencing it, you are letting it use implicit rules. Well, the implicit default here is "copy it local", because most dlls are not in the GAC. The IDE has the crazy notion of defaulting to "make it work", which means "assume it isn't going to be in the GAC".

If you want to use explicit rules, then yes: you need to tell it what you want. And the way you do that is by adding a reference, and then by setting the options you want.

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • OK, but why do other DLLs that I reference not work the same way? Also, the IDE *doesn't* try to make it work with a DLL that isn't in the GAC. For example, if I reference Library B from Library A and don't have copy local = false, Library B doesn't get copied. Why does this oracle (and only this oracle) DLL? – aquinas Feb 17 '13 at 17:36
  • 1
    @aquinas it depends on a few things : are you referencing it directly? or indirectly? is it in the GAC on the local machine? or not? in particular, there used to be (and may still be) a subtle IDE glitch where copy-local is actually tri-state: "true", "false", or "not explicitly given a value" - this means you can actually get different results for "false": if it is "false" initially, it might be "false by default", which can behave differently to "explicitly false", as "false by default" is actually a check against the GAC. – Marc Gravell Feb 17 '13 at 17:40
  • I'm referencing it directly in LibraryA. I'm referencing LibraryA directly from ConsoleApplication1. The DLL is in the GAC on the local machine when I build. However, I tried removing it from the GAC and it didn't make any difference. Again, this behavior ONLY occurs with this one oracle dll. Give it try yourself. 1) Create a console app 2) create a library. 3) Reference your library and set copy local = true. 4) reference ANY dll in the GAC from your library and set copy local = false. 5) Instantiate a class from the GACed dll. The GACed dll won't be copied to the output directory. – aquinas Feb 17 '13 at 17:47
  • ...*except if you reference the Oracle DLL* which, for some reason, magically acts different. One other thing that's odd, even though the Oracle DLL is in the GAC, when I directly reference it, by default it sets copy local = true, even though any other DLL I reference from the GAC sets copy local to false by default. Is there some setting you can set in a class library to control this that I don't know about? – aquinas Feb 17 '13 at 17:47
  • @aquinas if there is some oddness there (and I confess I haven't tried to repro), then I have no explanation to it. But in all cases: if you want to take control of what happens, you need to *actually tell it* what you want it to do, rather than letting the system make decisions for whatever internal logic is being odd in this case. – Marc Gravell Feb 17 '13 at 17:53
  • See my updated question. It appears like it has something to do with x86/x64/any cpu. – aquinas Feb 17 '13 at 18:14
0

After a good amount of investigation on what seems to be the same issue in a solution I work on, we've found that this issue appears to be caused by a new check which was introduced with the release of .NET 4.5, as mentioned here: at build time, references are now being checked to make sure that their processor type matches that of the projects.

While previously we didn't see any such error in the build log, on affected machines we now see a message indicating that the projects are building for MSIL and the DLL is built for x86 - rather than making available one DLL built for all CPUs, the third-party creator provides two separate DLLs for x86 and x64, both of which are in our GAC. Prior to this check being introduced, the system would use the DLL from the GAC and select the appropriate version, but it seems that after installing .NET 4.5, the build process is determining that neither version in the GAC is acceptable so it's pulling in a local copy.

We have not been able to identify any way to disable this behavior, so our resolution options are going to have to be to either just add a post-build event to delete the local copies, or (as mentioned in the question) to add references to the problem DLL to all projects which reference our "Library A" and then set those references to "copy local = false", which seems to also prevent copying a non-working local version into the output.

Community
  • 1
  • 1
Sam Hanley
  • 4,707
  • 7
  • 35
  • 63