How to have a Project Reference without referencing the actual binary


Sometimes you want a project reference from project B to project A to indicate a build-time dependency, but you don’t actually want assembly B to reference assembly A (maybe because it’s a runtime-only dependency or loaded using reflection). Having a project reference is beneficial because you indicate to the build system that in order to have B fully built you also need to build (and possibly deploy) A as well. You could use Visual Studio Configuration Manager dialog to declare the dependency between projects in the .sln file, but don’t. Configuration Manager is a foreign concept in MSBuild, it’s a legacy feature that shouldn’t exist.

Instead, on your project reference in the .csproj file, set the ReferenceOutputAssembly metadata:

    <ProjectReference Include="..\ProjectA\ProjectA.csproj">
      <Project>{b402782f-de0a-41fa-b364-60612a786fb2}</Project>
      <Name>ProjectA</Name>
      <ReferenceOutputAssembly>false</ReferenceOutputAssembly>
    </ProjectReference>

This will indicate a dependency between projects to MSBuild, but won’t pass the output assembly of ProjectA as a reference to the compiler when compiling ProjectB.

If you set ReferenceOutputAssembly to false you will notice that MSBuild stops copying the assembly A into the output directory for assembly B. If you would still like to copy assembly A to the output of assembly B (without referencing it), use this:

    <ProjectReference Include="..\ProjectA\ProjectA.csproj">
      <Project>{b402782f-de0a-41fa-b364-60612a786fb2}</Project>
      <Name>ProjectA</Name>
      <ReferenceOutputAssembly>false</ReferenceOutputAssembly>
      <OutputItemType>Content</OutputItemType>
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </ProjectReference>

This will produce an item of type Content in project B that includes the output assembly of project A, and the CopyToOutputDirectory metadata on that item will be set to Copy If Newer. This achieves copying a dependency without referencing it.

Update: many people have asked how to also copy the .pdb file in addition to the .exe/.dll. Turns out there is a trick, but it doesn’t work when building in VS. But it’s OK since you don’t really need the .pdb in VS anyway, since the debugger will find the .pdb at its original path anyway.

Add this line to the project reference to also copy the .pdb:

    <ProjectReference Include="..\ProjectA\ProjectA.csproj">
      <Project>{b402782f-de0a-41fa-b364-60612a786fb2}</Project>
      <Name>ProjectA</Name>
      <ReferenceOutputAssembly>false</ReferenceOutputAssembly>
      <OutputItemType>Content</OutputItemType>
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
      <Targets>Build;DebugSymbolsProjectOutputGroup</Targets>
    </ProjectReference>

Comments (20)

  1. Hello Kirill!

    Would you be so kind to tell us how to setup solution to build libraries with circular references? Say assembly A refers to B and B refers A at same time. Example : A – logging subsystem and it refers DAL to store logs, B is a DAL and of course it is using logging. In .Net framework class library there are some cases where such circular references exist, so Microsoft using it internally, but Visual Studio refuse to add reference in such case. I've heard it can be done with something called "temporary metadata only" assemblies. Please clarify and suggest.

    Thanks

  2. Unfortunately I have never done this myself so I can only imagine how it can work. You need to compile your first assembly, put the dll in a References folder, then reference the dll from second project, compile that, put that dll also in the references folder, then open the first project again and add a reference to the second dll.

    But really, don't do that. Instead, split your logging assembly into two parts – definitions and implementation. Have both the DAL and the implementation reference the logging definition assembly. It should just have an interface (API) for DAL to call. By having three assemblies instead of two you avoid circularity.

  3. Dan says:

    Seems it will copy the .dll's (with CopyToOutputDirectory), but not the .pdb's, is there a way around that?

  4. No idea about the pdb (and xml) unfortunately. It doesn't look like it's possible with this hack though…

  5. Daniel Rose says:

    AFAIK, Microsoft uses a special build system in order to create such circular references. However, as part of the cleanup of .NET Core, there will be no more circular references.

    One way I can imagine this could be done is by having multiple assemblies (so it is non-circular), and then later IL-merging the assemblies.

  6. Dan – updated to include a way to also copy the .pdb file.

  7. Alfred Myers says:

    My 2 cents on circular references: stackoverflow.com/…/151249

    But I agree it's better simply not to do it.

  8. Mariano García says:

    I need to copy de .config file from de project A to bin's folder of project B (since B references A). By default de .config copied from A has taked from src's folder but i want to take it from bin's folder of A. Where do i can configurate de source of .config in A?

  9. lenikur says:

    Kirill,
    Thank you for the post. It works great.
    It seems you are expert in this area. Could you see my question here: http://stackoverflow.com/questions/36842976/why-msbuild-task-does-not-build-a-project-dependencies

  10. Rune says:

    “This achieves copying a dependency without referencing it.”

    I have a .csproj that references a .vcxproj. Thanks to I am able to declare that reference and I can see that a build of the .vcxproj is triggered.

    I added this too:

    And the @(ReferenceCopyLocalPaths) collection does not contain the name of the .dll either.
    But the .csproj’s output directory does not contain the output of the .vcxproj.

    Built using VS 2013.

  11. David says:

    Hi Krill,
    Great post!
    I have a user control library (lets call it A) that references two other user control libraries (B and C) and I want to be able to reference Control A from an application and have the libraries from B and C copied to the application build destination as well as A.
    Do you know if there’s a way to do this?

  12. Damon says:

    Hi Kirill,
    Is there any way to do something similar for nuget packages?

  13. chmarroc says:

    If ProjectA references another ProjectC whose API it also accesses, ProjectB won’t copy the output of ProjectC to its output directory automatically. This seems to be a logical consequence of how the Project Reference from ProjectB to ProjectA is specified.
    Therefore, you need to specify a build-time dependency from ProjectB to ProjectC, too.

  14. chmarroc says:

    I’m facing an additional major issue with this approach. If I change a source file of project A, and build within Visual Studio, it won’t rebuild project B, thus not deploying the library of project A to project B’s output directory.
    On the other hand, deployment wasn’t working with solution level dependencies either.

    1. chmarroc says:

      I managed to solve my issue by setting the DisableFastUpToDateCheck property to true in project B.

  15. CZEMacLeod says:

    First: This is a brilliant article and I really wish I had seen it sooner!
    Second: I am trying to understand the mechanism for output files from a project and what gets copied.
    My issue is that I want a project reference to a project that generates 2 assemblies than need to be referenced and a dll that needs to be copied to ‘bin’ but not referenced.
    I can do this ‘easily’ by outputting the first project into a nuget package and importing that in to the second and that is all fine, but I want to manage the same via project reference…
    How do I make the second assembly and the dll appear in the project ‘output’ such that it is copied/referenced?

  16. Журихин Сергей says:

    Кирилл, отличная статья!!

  17. Jason Larns says:

    Hi Kirill,
    Your solution works perfect except copying the pdb for Asp.net Core 2.0 projects, DebugSymbolsProjectOutputGroup target does not work. What is required to copy the pdb file?

  18. Scott Louvau says:

    I found that this works for C++ project (vcxproj) also, for “not-Managed-C++” cases where you can’t reference the project normally.

    *However*, Visual Studio doesn’t copy the dependency to indirect references (like the managed library’s unit test project).

    I found that this alternative seems to work correctly for me, both for direct and indirect references:

    {70AC46A2-605B-4E9A-8414-D510A479742C}
    RE2.Native
    false

    PreserveNewest

    The first part tells MSBuild and VS that there’s a project dependency, but it should not be referenced. This causes VS to build things in the right order. (If you use a Post-Build COPY command instead, it fails if RE2.Native.dll wasn’t built first).

    The second part tells MSBuild and VS to copy the dependency to the output folder. (If you add to the , the native library disappears on Rebuild for indirect dependencies, like when changing a test and building RE2.Managed.Test.dll. The same happens if you use instead of as the type above.)

  19. Here’s a sample of referencing a native binary:

     <ItemGroup>
        <ProjectReference Include="..\RE2.Native\RE2.Native.vcxproj">
          <Project>{70AC46A2-605B-4E9A-8414-D510A479742C}</Project>
          <Name>RE2.Native</Name>
          <ReferenceOutputAssembly>false</ReferenceOutputAssembly>
        </ProjectReference>
        <None Include="$(ProjectDir)..\RE2.Native\bin\$(Platform)\$(Configuration)\RE2.Native.dll">
          <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
        </None>
      </ItemGroup>
    
    

    The first part tells MSBuild and VS that there’s a project dependency, but it should not be referenced. This causes VS to build things in the right order. (If you use a Post-Build COPY command instead, it fails if RE2.Native.dll wasn’t built first).

    The second part tells MSBuild and VS to copy the dependency to the output folder. (If you add to the , the native library disappears on Rebuild for indirect dependencies, like when changing a test and building RE2.Managed.Test.dll. The same happens if you use instead of as the type above.)

Skip to main content