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 (15)

  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.

Skip to main content