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

  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?

Skip to main content