简体   繁体   中英

Disable transitive project reference in .NET Standard 2

I'm writing an MVC website using ASP.NET Core 2.0.

In the ASP.NET Core project (let's call it Web ), I reference a .NET Standard 2 project in the same solution (let's call it Service ). The Service project also references a third .NET Standard 2 library in the solution (let's call this one Business ). The Business project declares a type called Model .

The problem is that I can use Model in the Web project (ie the compiler sees the type Model and I can do var a = new Model(); ) as if the Web project has referenced Business , but it actually only has a reference to Service .

How can I hide Model from Web ? Is this a new feature in ASP.NET Core 2 or all .NET Standard projects are like this?

Edit

As specified here , this is due to transitive project references which is a new "feature" in .NET Standard, but how do I fix it?

Well my question was close to one marked as duplicate here but to solve it requires different tactic.

Thanks to comment from "Federico Dipuma" and the answer given here I was able to solve this problem.

You should edit the Service.csproj file and add PrivateAssets="All" to ProjectReference keys you don't want to flow to top.

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

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 ProjectReference s. 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.

显示 ProjectA 引用 ProjectB 的图表,该 ProjectB 引用了 Newtonsoft.Jon 包

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>
              <PackageReference>
                     <PrivateAssets>all</PrivateAssets>
              </PackageReference>
              <ProjectReference>
                     <PrivateAssets>all</PrivateAssets>
              </ProjectReference>
       </ItemDefinitionGroup>
</Project>

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

All the above answers currently do not work for nuget package dependencies.

For the following library project (PrivateAssets="all"):

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>netstandard2.0</TargetFramework>
    <GeneratePackageOnBuild>true</GeneratePackageOnBuild>
    <Authors>Roy</Authors>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Newtonsoft.Json" Version="13.0.1" PrivateAssets="all" />
  </ItemGroup>

</Project>

The following nuspec file will be produced:

<package xmlns="http://schemas.microsoft.com/packaging/2012/06/nuspec.xsd">
  <metadata>
    <id>MyLib</id>
    <version>1.0.0</version>
    <authors>Roy</authors>
    <requireLicenseAcceptance>false</requireLicenseAcceptance>
    <description>Package Description</description>
    <dependencies>
      <group targetFramework=".NETStandard2.0" />
    </dependencies>
  </metadata>
</package>

Notice how "Newtonsoft.Json" is not even specified as a dependency. This will obviously not work as Visual Studio will not even be aware of that dependency and the consuming project will fail to resolved "Newtonsoft.Json" at runtime.

The correct answer is setting PrivateAssets="compile" .

Which will result in the following nuspec file:

<package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd">
  <metadata>
    <id>MyLib</id>
    <version>1.0.0</version>
    <authors>Roy</authors>
    <requireLicenseAcceptance>false</requireLicenseAcceptance>
    <description>Package Description</description>
    <dependencies>
      <group targetFramework=".NETStandard2.0">
        <dependency id="Newtonsoft.Json" version="13.0.1" include="Runtime,Build,Native,ContentFiles,Analyzers,BuildTransitive" />
      </group>
    </dependencies>
  </metadata>
</package>

This will prevent the consuming project from accessing the Newtonsoft.Json namespace in code, while compiling and copying all the required assemblies to allow the "encapsulating" library work.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM