Disable transitive project reference in .NET Standard 2

Transitive project references (ProjectReference)

Transitive project references are new feature of SDK-style csproj (1,2) format used in .NET Core/.NET >= 5. You can also use this csproj for old .NET Framework projects (1,2,3) but with some exceptions.

In this SDK-style format project references (represented by <ProjectReference> entry in .csproj file) are transitive. This is different to old non-SDK .csproj used previously.

But you have three options to go back to old non-transitive behavior.

  1. Use <DisableTransitiveProjectReferences>true</DisableTransitiveProjectReferences> property in .csproj that references projects for which you don’t want their transitive dependencies to be visible by compiler.

    In your case you can add it to Web project. (first project that reference other projects, Web -> Service -> Business)

    You can also set this behavior globally for all .csprojs by doing it in Directory.Build.props file that you put in the root folder that contains your source.

     <Project>
       <PropertyGroup>    
         <DisableTransitiveProjectReferences>true</DisableTransitiveProjectReferences>
       </PropertyGroup>
     </Project>
    

    With this file you basically have old project reference behaviour. Useful when you do migration of old .NET Framework solution that uses old csproj format to new SDK-style .csprojs.

  2. On the project that you reference you can set which dependencies shouldn’t flow further when the project is referenced. You use PrivateAssets="All" attribute on <ProjectReference> for this. So for example you can edit Service.csproj like this:

    <ItemGroup>
        <ProjectReference Include="..\Business.csproj" PrivateAssets="All" />
    </ItemGroup>
    

    This is more flexible and fine-grained approach. You can control with particular transitive project references should be visible when the project is referenced.

  3. Use ItemDefinitionGroup to define default <PrivateAssets>all</PrivateAssets> metadata for all ProjectReferences. You can also define it in Directory.Build.props file if you want to apply it globally to all projects.

    <ItemDefinitionGroup>
        <ProjectReference>
            <PrivateAssets>all</PrivateAssets>
        </ProjectReference>
    </ItemDefinitionGroup>
    

    The effect would be the same as setting <ProjectReference Include="..."> <PrivateAssets>All</PrivateAssets> </ProjectReference> (or PrivateAssets="All" attribute on all ProjectReferences entries.

    The difference from using 1. approach (DisableTransitiveProjectReferences) is that if you explicitly define PrivateAssets metadata on ProjectReference item then this value is used. You can think of using ItemDefinitionGroup as a way to provide default value of PrivateAssets metadata it it is not provided explicitly.

What should you use? It depends what you prefer. If you are used to old csproj behavior or want to migrate old solution to .NET Core then using DisableTransitiveProjectReferences or ItemDefinitionGroup in your Directory.Build.props is the easiest solution.


Nuget references (PackageReference) are also transitive by default.

This is not strictly answering your question but is something that you should be aware too.

If you are using new PackageReference format for nuget packages (and you probably do because this is the default in new SDK-style csproj files) then you should also be aware that these references are transitive. If your project references another project (with ProjectReference) that references nuget package (with PackageReference) then your project will also reference this nuget package.

Diagram showing ProjectA referencing ProjectB which references Newtonsoft.Jon package

Here ProjectA will have implicit reference to Newtosoft.Json library.

Unfortunately there is no DisableTransitivePackagesReferences for package references. But you can use PrivateAssets metadata like you did for ProjectReference in 2nd option or use ItemDefinitionGroup like you did in 3rd option.

That’s why if you want to disable transitive dependencies both for project and package references for all projects then this is how your Directory.build.props file should look like:

<Project>
    <ItemDefinitionGroup>
        <ProjectReference>
            <PrivateAssets>all</PrivateAssets>
        </ProjectReference>
        <PackageReference>
            <PrivateAssets>all</PrivateAssets>
        </PackageReference>
    </ItemDefinitionGroup>
</Project>

(source: I learned about this technique from this blog )

or

<Project>
    <ItemDefinitionGroup>
        <DisableTransitiveProjectReferences>true</DisableTransitiveProjectReferences>
        <PackageReference>
            <PrivateAssets>all</PrivateAssets>
        </PackageReference>
    </ItemDefinitionGroup>
</Project>

Leave a Comment