MSBuild doesn't copy references (DLL files) if

2018-12-31 12:40发布

I have four projects in my Visual Studio solution (everyone targeting .NET 3.5) - for my problem only these two are important:

  1. MyBaseProject <- this class library references a third-party DLL file (elmah.dll)
  2. MyWebProject1 <- this web application project has a reference to MyBaseProject

I added the elmah.dll reference to MyBaseProject in Visual studio 2008 by clicking "Add reference..." → "Browse" tab → selecting the "elmah.dll".

The Properties of the Elmah Reference are as follows:

  • Aliases - global
  • Copy local - true
  • Culture -
  • Description - Error Logging Modules and Handlers (ELMAH) for ASP.NET
  • File Type - Assembly
  • Path - D:\webs\otherfolder\_myPath\__tools\elmah\Elmah.dll
  • Resolved - True
  • Runtime version - v2.0.50727
  • Specified version - false
  • Strong Name - false
  • Version - 1.0.11211.0

In MyWebProject1 I added the reference to Project MyBaseProject by: "Add reference..." → "Projects" tab → selecting the "MyBaseProject". The Properties of this reference are the same except the following members:

  • Description -
  • Path - D:\webs\CMS\MyBaseProject\bin\Debug\MyBaseProject.dll
  • Version - 1.0.0.0

If I run the build in Visual Studio the elmah.dll file is copied to my MyWebProject1's bin directory, along with MyBaseProject.dll!

However if I clean and run MSBuild for the solution (via D:\webs\CMS> C:\WINDOWS\Microsoft.NET\Framework\v3.5\MSBuild.exe /t:ReBuild /p:Configuration=Debug MyProject.sln) the elmah.dll is missing in MyWebProject1's bin directory - although the build itself contains no warning or errors!

I already made sure that the .csproj of MyBaseProject contains the private element with the value "true" (that should be an alias for "copy local" in Visual Studio):

<Reference Include="Elmah, Version=1.0.11211.0, Culture=neutral, processorArchitecture=MSIL">
  <SpecificVersion>False</SpecificVersion>
  <HintPath>..\mypath\__tools\elmah\Elmah.dll</HintPath>
    **<Private>true</Private>**
</Reference>

(The private tag didn't appear in the .csproj's xml by default, although Visual Studio said "copy local" true. I switched "copy local" to false - saved - and set it back to true again - save!)

What is wrong with MSBuild? How do I get the (elmah.dll) reference copied to MyWebProject1's bin?

I do NOT want to add a postbuild copy action to every project's postbuild command! (Imagine I would have many projects depend on MyBaseProject!)

17条回答
人间绝色
2楼-- · 2018-12-31 13:20

This requires adding a .targets file to your project and setting it to be included in the project's includes section.

See my answer here for the procedure.

查看更多
只靠听说
3楼-- · 2018-12-31 13:21

I'm not sure why it is different when building between Visual Studio and MsBuild, but here is what I have found when I've encountered this problem in MsBuild and Visual Studio.

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);
    }
查看更多
旧人旧事旧时光
4楼-- · 2018-12-31 13:21

I had the same problem.

Check if the framework version of your project is the same of the framework version of the dll that you put on reference.

In my case, my client was compiled using "Framework 4 Client" and the DLL was in "Framework 4".

查看更多
时光乱了年华
5楼-- · 2018-12-31 13:24

I just deal with it like this. Go to the properties of your reference and do this:

Set "Copy local = false"
Save
Set "Copy local = true"
Save

and that's it.

Visual Studio 2010 doesn't initially put: <private>True</private> in the reference tag and setting "copy local" to false causes it to create the tag. Afterwards it will set it to true and false accordingly.

查看更多
与君花间醉酒
6楼-- · 2018-12-31 13:28

Another scenario where this shows up is if you are using the older "Web Site" project type in Visual Studio. For that project type, it is unable to reference .dlls that are outside of it's own directory structure (current folder and down). So in the answer above, let's say your directory structure looks like this:

enter image description here

Where ProjectX and ProjectY are parent/child directories, and ProjectX references A.dll which in turn references B.dll, and B.dll is outside the directory structure, such as in a Nuget package on the root (Packages), then A.dll will be included, but B.dll will not.

查看更多
登录 后发表回答