This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How does Copy-local work?
I have following situation:
there's a project named OLAF.Tools, and that project references Microsoft.Data.SqlXml in C:\Program Files\SQLXML 4.0\bin\Microsoft.Data.SqlXml.dll. Reference Copy Local property is set to True. When I build that project in bin directory I can see both OLAF.Tools.dll and Microsoft.Data.SqlXml.dll
there's a console application named OLAF.Generator, and that application references OLAF.Tools (I've added reference using Project tab). When I build that application in bin directory I can see only OLAF.Generator.exe and OLAF.Tools.dll - there's no Microsoft.Data.SqlXml.dll, what supprises me. Another wierd thing is that even though that dll is missing application is executing properly.
So my questions are:
why Microsoft.Data.SqlXml.dll is not copied to bin folder of OLAF.Generator console application?
how application resolves directory where Microsoft.Data.SqlXml.dll can be found?
Thanks,Pawel
EDIT 1: (after response from Marc Gravell)
#Marc Gravell: Your answer gave me food for thought, as I could swore that I could always see indirectly dependant assemblies in main application's bin directory. And IMHO I don't agree with you - with all due respect :)
Of course, references are not cascaded physically (we're are talking about strong relationship to classes, interfaces etc) - and it's exactly what I wanted to achieve when building OLAF.Tools library. That library provides a level of abstraction, it contains factories, and one factory accepts as a parameter string and returns interface. One particular implementation of that interface uses Microsoft.Data.SqlXml components. As a result,
OLAF.Generator uses interface that is located in OLAF.Tools, but doesn't know about components in Microsoft.Data.SqlXml.
Apart from that (I think we both know what I tried to explain in preceding paragraph), when building application, dependant assemblies should be copied (if Copy Local is set to TRUE). I just wrote sample application, Project B lib has reference to Project A lib, and Project C (console app) has reference to Project B. In Project C's bin directory I can see all: Project A.dll, Project B.dll & Project C.exe. So in discussed scenario, the reason why Microsoft.Data.SqlXml doesn't end up in OLAF.Generator bin folder has something to do with that assembly itself.
Does compiler/visual studio knows that Microsoft.Data.SqlXml is located in directory which is automatically probed (or it's in GAC) and this is the reason why that assembly is not copied?
EDIT 2: I've just checked GAC, and indeed, Microsoft.Data.SqlXml.dll is installed in GAC.
How does Copy-local work? log4net.dll is not being copied to MyProject output directory - this is answer to my question. When library is installed in GAC, it won't be copied even though COPY LOCAL setting is used.
References are not automatically cascaded, so adding a reference to OLAF.Tools does not also add a reference to SQLXML. If you want to deploy SQLXML with your exe, then the most convenient way to do that is to explicitly add a reference to SQLXML from your exe, and set it to copy local. Without this, it is not deployed. Basically, the onus is on the developer to decide which files are actually needed at runtime (which is often a subset of the references used, and depends on a number of deployment decisions which only you can know).
Re how it is resolved at runtime... the probing paths are a bit of a black art, mainly meaning "the app folder", but it depends on the config, and indeed the GAC may be consulted. You also get an opportunity to provide your own resolver, via AppDomain.Current.AssemblyResolve.
Related
I have a solution with quite a few projects in it and several dependencies to external DLLs. While trying to build a x64 version of the solution, I found that when building one project several libraries are copied into the output directory which are not mentioned in the project's references or anywhere else within the solution. I figured out that there is a collection of file names called _CopyFilesMarkedCopyLocal, which contains these DLLs. In the concrete case the copied library was used indeed, but it is a x86 library, which overwrites the existing x64 file in the output folder. How does a DLL, which is not mentioned in any project of the solution, become part of this collection? Can some referenced DLL induce further implicit references? Can I control this behavior?
Update: In the x86 build the set of file names contained in _CopyFilesMarkedCopyLocal is what I expected, namely one reference explicitly mentioned.
I have a class library that represents my logic layer. To that library I've added a nuget package for Google.Apis.Analytics.v3 - it installed the package and all it's dependencies.
I have a console application that uses that logic class library (regular reference). everything is written and compiled fine.
The problem is that during runtime it threw an exception that Google.Apis.dll wasn't found. This DLL is a dependency that was downloaded with the nuget.
Checking the BIN folders, I've found that in the class library bin folder this DLL was present, but in the console application BIN folder it wasn't (while other related DLLs were). So this means that the not all references where copied during compilation.
I've searched online, and found all kind of workarounds that didn't really work (like manually editing the project file and removing a true xml line on that dll definition).
What I ended up doing is adding the same nuget library to my console application - it works but feels a little dirty and not the way it should be. I think the console app is the client who's supposed to get it's services from that logic class library which should know it's stuff without the "client" worrying about it.
Also, that console app is not the only one who's gonna use that service, I'm also planning on a web app that will use that functionality - so I will need to add the same nuget to that web app as well - again, feels a little messy...
Is it just me? is that the right way to go about it? I was thinking about writing a WCF project to handle that functionality - but that seems a little of a overhead for just on functionality, and probably slow my workflow down just to keep things "cleaner" in my opinion.
Am I just over-thinking it?
Thank
Explanation
For a sample scenario let's say we have project X, assembly A, and assembly B. Assembly A references assembly B, so project X includes a reference to both A and B. Also, project X includes code that references assembly A (e.g. A.SomeFunction()). Now, you create a new project Y which references project X.
So the dependency chain looks like this: Y => X => A => B
Visual Studio / MSBuild tries to be smart and only bring references over into project Y that it detects as being required by project X; it does this to avoid reference pollution in project Y. The problem is, since project X doesn't actually contain any code that explicitly uses assembly B (e.g. B.SomeFunction()), VS/MSBuild doesn't detect that B is required by X, and thus doesn't copy it over into project Y's bin directory; it only copies the X and A assemblies.
Solution
You have two options to solve this problem, both of which will result in assembly B being copied to project Y's bin directory:
Add a reference to assembly B in project Y.
Add dummy code to a file in project X that uses assembly B.
Personally I prefer option 2 for a couple reasons.
If you add another project in the future that references project X, you won't have to remember to also include a reference to assembly B (like you would have to do with option 1).
You can have explicit comments saying why the dummy code needs to be there and not to remove it. So if somebody does delete the code by accident (say with a refactor tool that looks for unused code), you can easily see from source control that the code is required and to restore it. If you use option 1 and somebody uses a refactor tool to clean up unused references, you don't have any comments; you will just see that a reference was removed from the .csproj file.
Here is a sample of the "dummy code" that I typically add when I encounter this situation.
// DO NOT DELETE THIS CODE UNLESS WE NO LONGER REQUIRE ASSEMBLY A!!!
private void DummyFunctionToMakeSureReferencesGetCopiedProperly_DO_NOT_DELETE_THIS_CODE()
{
// Assembly A is used by this file, and that assembly depends on assembly B,
// but this project does not have any code that explicitly references assembly B. Therefore, when another project references
// this project, this project's assembly and the assembly A get copied to the project's bin directory, but not
// assembly B. So in order to get the required assembly B copied over, we add some dummy code here (that never
// gets called) that references assembly B; this will flag VS/MSBuild to copy the required assembly B over as well.
var dummyType = typeof(B.SomeClass);
Console.WriteLine(dummyType.FullName);
}
If you have the following dependency chain:
Lib1 <-- Lib2 <-- MyApp
TLDR version: by not making assumptions, the build system avoids introducing uncertainty/unexpected behavior.
When you build MyApp, Lib2 will get copied to MyApp's bin directory for you, but Lib1 will not. You will need to add a reference to Lib2 and Lib1 in MyApp in order to get Lib1's dlls in MyApp's bin directory (otherwise you'll get the runtime error). It would be impossible (or maybe just really difficult) to identify the exact set of files that end up in Lib2's bin directory that would be safe & appropriate to copy over to MyApp's. If the build system made assumptions that everything in Lib2's bin directory was safe for MyApp, or if it implicitly referenced Lib1 for you, it could change the behavior of MyApp unintentionally.
Imagine a solution where more than 1 project depends on Lib2 but one of those projects wants to load an adjacent .dll file using Assembly.LoadFrom/Activator.CreateInstance/MEF/etc. (a plugin) and the other one does not. An automatic copy operation could grab Lib2 along with the plugin dll and copy it over to the first and the second project's build directory (since it's in the Lib2's bin directory as a result of a build operation). This would change the behavior of the second app.
Alternatively, if it was a little smarter and implicitly referenced Lib1 for you when you referenced Lib2 (and didn't just copy bin directory contents), it could still cause unintended consequences. What if MyApp already depended on Lib1, but it was using a GAC'd/ngen'd copy that was compatible with the one that Lib2 requires. If adding a reference to Lib2 implicitly created a reference to Lib1 for you, that could change which Lib1 got loaded and change the runtime behavior of your application. It could maybe detect that there already is a Lib1 in MyApp's bin directory and skip it, but then it would be making assumptions that the Lib1 that's already there is the right one. Maybe it's a stale .dll waiting to get wiped away by a Clean operation and the overwrite was the right move.
NuGet addresses the problem you're describing with package dependencies. If Lib1 and Lib2 both had nuget packages and the Lib2 package depended on the Lib1 package, when you add Lib2 to MyApp, Lib1 would get added as well. Both pacakges' dlls would end up in MyApp's bin directory.
The best thing to do is invert your thinking a little bit. Instead of thinking:
Lib2 needs a reference to Lib1 in order to compile so I'll add a reference to Lib1
Think:
MyApp needs Lib2. Whatever Lib2 needs, I need. So MyApp & Lib2 both get a reference to Lib1.
If you have 10's of dlls it's easier to do a copy with a postbuild event:
xcopy "$(ProjectDir)bin\*.dll" $(SolutionDir)MyTargetProject\bin\" /y
Our project has a lot of external DLLs, most but not all of which are 3rd party DLLs.
Currently we do not have these DLLs included in our project. They are included in SVN and given a path to our build output directory. So, after building our project the neccessary files are there, because of SVN, but the project itself has no knowledge of them.
My feeling is that we should have a folder under the root of our project named something like Dependancies or ThirdParty with all of the DLLs included there and set their build event to copy to the output directory. They would exist in SVN as well, but in the same structure as the project, not in the build output directory.
The project itself only references one of these DLLs called CommunicationProc.DLL. The CommunicationProc.DLL then references all of the other DLLs. We have numerous DLLs to support different types of radio. So not all DLLs will be used, but any one of them may be used depending on the radio type.
As to whether or not the DLLs should be included in the project we have differing opinions internally, some of the team beleives they should only be in SVN and not part of the project itself.
Of note is that this are not .NET DLLs, most are old C DLLs.
What is the accepted practice? Can someone please provide me with a compelling arguement one way or the other as to whether to include them in the project or just SVN?
Its better to have them in a folder on source control and then copy them over to the debug folder on build event. This way you can manage their versions. If a newer version of some dll comes then you can replace the old one and put some comments with check in. Also if you are working in a team, then instead of copying files from debug folder to each team member, you can let each team member to use the same set of dlls from source control. If you are developing some control and want your customers to use that control then its easier for you to have a set of dependent dlls some where so that you can give those to your customer along with your .Net dlls.
I had the same issue with some un-managed dlls and ended up putting them in a folder so that all the team members have the same version of the dlls. Hope this helps.
I include a project that has no code but contains a folder where all the external assemblies and their dependencies are kepts. For each file set the Build Action to None and Copy to Output as Do Not Copyp. The project then references the binaries from this location. In your other projects, reference this special project. When you build, because the special project is referenced and it references all the needed dependencies, the binaries are copied as needed.
If you do not want a special project, still create the folder in your main project, added the assemblies, set their properties, then reference the assemblies as needed.
This gives you complete control over the versions and output, and more importantly, it is simple.
This has to be a pretty common issue, and while we have answers, I'm trying to determine if there is any better way. We are using IoC / DI with structuremap. This means that our final application (an Asp.Net MVC 2 app) requires the presence of some assemblies which aren't being referenced anywhere else (our implemention of IRepository, etc).
In VS, I've added a Post Build event that simply goes to our lib folder (containing the already build assemblies which are not part of our solution) and copies everything with *Repository*.dll to the $(TargetDir).
On the build server, were we are also building packages for deployment (and hoping to automate deployment to some environments), I've done something similar to get those assemblies.
Is there any better way though? Or do you just accept that this is one of the issues with IoC and have to remember to add a bit of script to include the service assemblies as part of the process?
Thanks
Andy
You should be able to create a project of any type, and then reference everything you need. Then just copy everything, except the dll from that project. At least you don't have to edit a lot of scripts just to add a new project..?
I suppose you could add the assemblies you want into your project just as linked files, not as assembly references, and then mark them as 'Content' as the build type and 'Copy if newer' so they will get copied to your bin folder.
Never tried this, but it ought to work.
I have created a dll that will be used by multiple applications, and have created an installer package that installs it to the program files, as well as adds it to the Global Assembly Cache.
The dll itself uses log4net, and requires a xml file for the logging definitions.
Therefore when the installer is run, the following files get copied to the install directory within program files:
The main dll that I developed
- The Log4Net.dll
- the Log4Net.xml file
I am now experiencing a problem. I have created a test console application for experimentation. I have added my dll as a reference, and set the 'local copy' flag to false.
When I compile the test console exe however, I noticed that it has copied the log4net.dll and log4net.xml files to the bin directory. And when running the test console, it appears that it will only work if the log4net.dll is in the same directory as the exe. This is dispite the fact that the test console application does not use log4net, only the dll that was added as a reference does.
Is there some way to have it so that the log4net.dll & xml files used will be the ones that were installed to the program files, rather than any application needed to copy over local copies? The applications that will be using my dll will not be using log4net, only the dll that they are referencing uses it.
Many thanks
Don't install into the Global Assembly Cache! Even if your library dll is used by multiple applications each should have it's own local copy. Otherwise you get into a whole world of pain for saving a few KB of disk space.
Always copy the required dlls locally. If you are really sure that the application won't need it you can simply delete the unnessesary dlls later or don't include them in the installer. But if your application will call ANY reference there it will crash at runtime. So best option is to leave them there (after all they WERE referenced for a reason).
No, it's not possible (at least not without much efford) to have .Net load dlls from arbitrary locations on the disk. And it should be this way (look up DLL-hell if you want to know why).
I suspect your problem is the configuration. You must use fully qualified names if you want it to work from the GAC. As per the documentation at http://logging.apache.org/log4net/release/faq.html:
"When loading an assembly from the GAC the fully qualified assembly name, including the version, culture and public key must be specified. This is in the standard syntax supported by System.Type.GetType. See the next FAQ on how to get the version and public key for an assembly."
I managed to resolve this by adding Log4net.dll to the GAC as well. It will now run without needing a local copy the dll.
It does however require a local copy of the XML file, to correctly log.