References from class library are not copied to running project bin folder

后端 未结 3 1476
礼貌的吻别
礼貌的吻别 2020-12-25 12:56

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 depe

相关标签:
3条回答
  • 2020-12-25 13:09

    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
    
    0 讨论(0)
  • 2020-12-25 13:18

    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.
    0 讨论(0)
  • 2020-12-25 13:24

    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:

    1. Add a reference to assembly B in project Y.
    2. Add dummy code to a file in project X that uses assembly B.

    Personally I prefer option 2 for a couple reasons.

    1. 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).
    2. 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);
        }
    
    0 讨论(0)
提交回复
热议问题