【问题标题】:Disable transitive project reference in .NET Standard 2在 .NET Standard 2 中禁用传递项目引用
【发布时间】:2017-10-12 11:56:48
【问题描述】:

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

在 ASP.NET Core 项目(我们称之为 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 项目都这样?

编辑

正如 here 所指定的,这是由于传递项目引用,这是 .NET Standard 中的一个新“功能”,但我该如何解决呢?

【问题讨论】:

标签: c# asp.net-core .net-standard .net-standard-2.0


【解决方案1】:

传递项目引用 (ProjectReference)

传递项目引用是 SDK 样式 csproj (1,2) 格式的新功能,用于 .NET Core/.NET >= 5。您也可以将此 csproj 用于旧的 .NET Framework 项目 (@ 987654323@,2,3) 但还有一些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. 在您引用的项目上,您可以设置在引用该项目时哪些依赖项不应流动。为此,您使用 &lt;ProjectReference&gt; 上的 PrivateAssets="All" 属性。例如,您可以像这样编辑 Service.csproj:

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

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

  3. 使用ItemDefinitionGroup 为所有ProjectReferences 定义默认的&lt;PrivateAssets&gt;all&lt;/PrivateAssets&gt; 元数据。如果您想将其全局应用于所有项目,您也可以在 Directory.Build.props 文件中定义它。

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

    效果与在所有 ProjectReferences 条目上设置 &lt;ProjectReference Include="..."&gt; &lt;PrivateAssets&gt;All&lt;/PrivateAssets&gt; &lt;/ProjectReference&gt;(或 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 用于包引用。但是您可以像在第二个选项中对 ProjectReference 所做的那样使用 PrivateAssets 元数据,或者像在第三个选项中那样使用 ItemDefinitionGroup

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

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

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

【讨论】:

    【解决方案2】:

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

    感谢“Federico Dipuma”的评论和here的回答,我能够解决这个问题。

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

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

    【讨论】:

    • 很好很清楚 - 只是为了强调你所说的(但我第一次设法忽略了)。参考链是 Web->Services->Business,它在 SERVICES 中,您需要添加 PrivateAssets="All" 以从 Web 中隐藏业务(我将它放在 Web->Services ProjectReference 中,但它不起作用 - 我只需要更仔细地阅读您的答案!)
    【解决方案3】:

    以上所有答案目前都不适用于 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 命名空间,同时编译和复制所有必需的程序集以允许“封装”库工作。

    【讨论】:

    • 对我来说,我必须使用 PrivateAssets="compile",通过构建我可以访问命名空间
    • @JuanR,看来你是对的,我做了很多测试,我可能对本地 nuget 包缓存和正确的解决方案感到困惑。我更正了我的答案。谢谢!
    • 这应该是公认的答案。对于PrivateAssets="all",运行时指示缺少库,此答案解释了原因。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-06
    • 1970-01-01
    • 1970-01-01
    • 2022-11-17
    • 2018-05-04
    相关资源
    最近更新 更多