在 .NET Standard 2 中禁用传递项目引用

Disable transitive project reference in .NET Standard 2

我正在使用 ASP.NET Core 2.0 编写一个 MVC 网站。

在 ASP.NET 核心项目(我们称之为 Web)中,我在同一解决方案中引用了一个 .NET Standard 2 项目(我们称之为 Service)。 Service 项目还在解决方案中引用了第三个 .NET Standard 2 库(我们称之为 Business)。 Business 项目声明了一个名为 Model.

的类型

问题是我可以在 Web 项目中使用 Model(即编译器看到类型 Model 而我可以做 var a = new Model();)就好像Web 项目引用了 Business,但它实际上只引用了 Service

如何向 Web 隐藏 Model?这是 ASP.NET Core 2 中的新功能还是所有 .NET Standard 项目都是这样?

编辑

根据指定 ,这是由于传递项目引用所致,它是 .NET Standard 中的新 "feature",但我该如何解决它?

好吧,我的问题与此处标记为重复的问题很接近,但要解决它需要不同的策略。

感谢 "Federico Dipuma" 的评论和给出的答案 here 我能够解决这个问题。

您应该编辑 Service.csproj 文件并将 PrivateAssets="All" 添加到 ProjectReference 您不想流到顶部的键。

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

传递项目引用(ProjectReference

传递项目引用是 SDK 样式 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

在此 SDK 样式格式中,项目引用(由 .csproj 文件中的 <ProjectReference> 条目表示)是可传递的。这与之前使用的旧非 SDK .csproj 不同。

但是您有三个选项可以返回到旧的非传递行为。

  1. 在 .csproj 中使用 <DisableTransitiveProjectReferences>true</DisableTransitiveProjectReferences> 属性 引用您不希望其传递依赖项被编译器可见的项目。

    在您的情况下,您可以将其添加到 Web 项目中。 (第一个引用其他项目的项目,Web -> Service -> Business

    您还可以在 Directory.Build.props 文件中为所有 .csprojs 全局设置此行为,该文件位于包含源代码的根文件夹中。

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

    有了这个文件,您基本上就有了旧的项目引用行为。当您将使用旧 csproj 格式的旧 .NET Framework 解决方案迁移到新 SDK 样式的 .csprojs 时很有用。

  2. 在您引用的项目上,您可以设置在引用项目时哪些依赖项不应 进一步流动。为此,您在 <ProjectReference> 上使用 PrivateAssets="All" 属性。例如,您可以像这样编辑 Service.csproj:

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

    这是一种更灵活、更细粒度的方法。您可以控制特定的传递项目引用在引用项目时应该可见。

  3. 使用 ItemDefinitionGroup 为所有 ProjectReference 定义默认 <PrivateAssets>all</PrivateAssets> 元数据。如果你想将它全局应用到所有项目,你也可以在 Directory.Build.props 文件中定义它。

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

    效果与在所有 ProjectReferences 条目上设置 <ProjectReference Include="..."> <PrivateAssets>All</PrivateAssets> </ProjectReference>(或 PrivateAssets="All" attribute 相同。

    与使用 1. 方法 (DisableTransitiveProjectReferences) 的不同之处在于,如果您在 ProjectReference 项目上明确定义 PrivateAssets 元数据,则会使用此值。您可以考虑使用 ItemDefinitionGroup 作为提供 PrivateAssets 元数据默认值的一种方式,它没有明确提供。

你应该用什么?这取决于你喜欢什么。如果您习惯了旧的 csproj 行为或想将旧的解决方案迁移到 .NET Core,那么在 Directory.Build.props 中使用 DisableTransitiveProjectReferencesItemDefinitionGroup 是最简单的解决方案。


Nuget 引用 (PackageReference) 默认也是可传递的。

这并没有严格回答您的问题,但您也应该注意这一点。

如果您正在为 nuget 包使用新的 PackageReference 格式(您可能会这样做,因为这是新 SDK 样式的 csproj 文件中的默认格式),那么您还应该知道这些引用是可传递的。如果您的项目引用另一个引用 nuget 包(PackageReference)的项目(ProjectReference),那么您的项目也将引用此 nuget 包。

此处 ProjectA 将隐式引用 Newtosoft.Json 库。

不幸的是,有 no DisableTransitivePackagesReferences for package references. But you can use PrivateAssets 元数据,就像您在第二个选项中为 ProjectReference 所做的那样,或者像您在第三个选项中那样使用 ItemDefinitionGroup

这就是为什么如果你想禁用所有项目的项目和包引用的传递依赖,那么你的 Directory.build.props 文件应该如下所示:

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

(来源:我从这个 blog 中学到了这个技术)

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

以上所有答案目前都不适用于 nuget 包依赖项。

对于以下库项目 (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>

将生成以下 nuspec 文件:

<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>

注意“Newtonsoft.Json”甚至没有指定为依赖项。 这显然是行不通的,因为 Visual Studio 甚至不会意识到这种依赖关系,并且消费项目将无法在运行时解析“Newtonsoft.Json”。

正确答案设置为PrivateAssets="compile"

这将产生以下 nuspec 文件:

<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>

这将阻止消费项目在代码中访问 Newtonsoft.Json 命名空间,同时编译和复制所有必需的程序集以允许“封装”库工作。