当前位置: 首页 > news >正文

csproj 文件中那个空的 NuGetPackageImportStamp 是干什么的?

当我们在传统格式的 csproj 项目文件中安装 NuGet 包后,有时会在项目文件中发现空的 NuGetPackageImportStamp 节点。这个空的节点让我们这波强迫症患者觉得有点难以接受,关键是手工删除之后也没发现有什么副作用。

那么为什么会出现这个节点?它究竟有什么作用?


本文内容

    • 空的 NuGetPackageImportStamp 节点
    • 什么情况下会出现 NuGetPackageImportStamp 节点
      • 不会新增 NuGetPackageImportStamp
      • 会新增 NuGetPackageImportStamp
    • NuGetPackageImportStamp 的出现目的

空的 NuGetPackageImportStamp 节点

NuGetPackageImportStamp 节点只会出现在传统的 csproj 文件中。如果你不清楚我这里指的传统的和新的 csproj 文件格式,那么可以阅读我的另一篇文章来了了解它们的区别:将 WPF、UWP 以及其他各种类型的旧 csproj 迁移成 Sdk 风格的 csproj。

简单说来,在 Project 根节点中可以指定 Sdk 特性的 csproj 文件格式是新的 csproj 格式。由于 Sdk 特性的存在,使得很多的项目文件的功能得以有一个默认的实现。

而传统的 csproj 由于没有指定 Sdk 特性,所以很多的特性如果需要执行,需要先 Import 到 csproj 中,或者不断地修改 csproj 文件的内容以添加新的功能。

空的 NuGetPackageImportStamp 节点只会出现在传统的 csproj 文件中。如果你使用新格式的 csproj 文件,那么无论你如何安装 NuGet 包,都是不会看到 NuGetPackageImportStamp 节点出现的。

NuGetPackageImportStamp 在传统 csproj 文件中是这样的:

    <?xml version="1.0" encoding="utf-8"?>
    <Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
      <PropertyGroup>
++      <NuGetPackageImportStamp>
++      </NuGetPackageImportStamp>
      </PropertyGroup>
    </Project>

文件已经经过过度简化,肯定是编译不过的了。不过,你可以意会。它会在某些 NuGet 包安装完后出现在 csproj 文件中。

什么情况下会出现 NuGetPackageImportStamp 节点

你也许会发现,并不是所有的 NuGet 包安装完后都会出现 NuGetPackageImportStamp 节点。实际上,只有那些会导致新 Import 文件部件的 NuGet 包才会出现这样的节点。

我们来了做个实验。

不会新增 NuGetPackageImportStamp

在项目中安装 Newtonsoft.Json。安装完后,你会看到仓库中有两个文件发生了变化:

两个文件发生了变化
▲ 两个文件发生了变化

一个是 packages.config 文件,这是传统的 NuGet 包管理方式所需要的一个文件,用于记录当前项目中管理的 NuGet 包信息。

    <?xml version="1.0" encoding="utf-8"?>
    <packages>
++    <package id="Newtonsoft.Json" version="11.0.2" targetFramework="net473" />
    </packages>

另一个是 csproj 文件:

    <?xml version="1.0" encoding="utf-8"?>
    <Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
      <ItemGroup>
++      <Reference Include="Newtonsoft.Json, Version=11.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
++        <HintPath>..\..\packages\Newtonsoft.Json.11.0.2\lib\net45\Newtonsoft.Json.dll</HintPath>
++      </Reference>
        <Reference Include="System" />
      <ItemGroup>
    </Project>

我们发现,安装 Newtonsoft.Json 是不会导致项目中新增 NuGetPackageImportStamp 节点的。

会新增 NuGetPackageImportStamp

现在,我们换另一个 NuGet 包来安装:StyleCop.MSBuild。

同样是两个文件的变化,一个是 packages.config 文件。

    <?xml version="1.0" encoding="utf-8"?>
    <packages>
++    <package id="StyleCop.MSBuild" version="5.0.0" targetFramework="net471" developmentDependency="true" />
    </packages>

另一个是 csproj 文件:

    <?xml version="1.0" encoding="utf-8"?>
    <Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
      <Import Project="..\..\packages\StyleCop.MSBuild.5.0.0\build\StyleCop.MSBuild.targets" Condition="Exists('..\..\packages\StyleCop.MSBuild.5.0.0\build\StyleCop.MSBuild.targets')" />
      <PropertyGroup>
++      <NuGetPackageImportStamp>
++      </NuGetPackageImportStamp>
      </PropertyGroup>
++    <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
++      <PropertyGroup>
++        <ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them.  For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
++      </PropertyGroup>
++      <Error Condition="!Exists('..\..\packages\StyleCop.MSBuild.5.0.0\build\StyleCop.MSBuild.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\StyleCop.MSBuild.5.0.0\build\StyleCop.MSBuild.targets'))" />
++    </Target>
    </Project>

我们发现,安装此 StyleCop.MSBuild NuGet 包的情况下,csproj 文件中新增了两个大的内容块:

  1. NuGetPackageImportStamp
  2. 用于 Import 一个 targets 文件的 Target

NuGetPackageImportStamp 的出现目的

我们发现 NuGetPackageImportStamp 其实是伴随着 Import 而出现的。而微软官方的注释也是诡异地说出了它的原因:

The overrides should ensure that Sets NuGetPackageImportStamp to a new random guid.
This is a hack to let the project system know it is out of date.
The value does not matter, it just needs to change.

这是为了让 Visual Studio 运行的时候,能够检测到 csproj 文件改变,以便重新加载这个项目,因为需要 Import 新的内容。在以前的 Visual Studio 版本中,会随机写下一段字符串;在新的版本中,它是个空字符串。

由于新的 csproj 文件能够识别到外部 Import 文件的改变,所以其实并不需要这样的机制来让 Visual Studio 感知到文件的改变。

在 Visual Studio 2017(工具版本 15.0)中,这个值会设为空,而在较低版本(14.0 及以下)这个值会设为一个随机的 guid。

以下是 NuGet 客户端设置此值的代码:

/// <summary>
/// This method should be on the UI thread. The overrides should ensure that
/// Sets NuGetPackageImportStamp to a new random guid. This is a hack to let the project system know it is out
/// of date.
/// The value does not matter, it just needs to change.
/// </summary>
protected static void UpdateImportStamp(IVsProjectAdapter vsProjectAdapter)
{
    ThreadHelper.ThrowIfNotOnUIThread();

    var propStore = vsProjectAdapter.VsHierarchy as IVsBuildPropertyStorage;
    if (propStore != null)
    {
        // <NuGetPackageImportStamp>af617720</NuGetPackageImportStamp>
        var stamp = Guid.NewGuid().ToString().Split('-')[0];
        try
        {
            propStore.SetPropertyValue(NuGetImportStamp, string.Empty, (uint)_PersistStorageType.PST_PROJECT_FILE, stamp);
        }
        catch (Exception ex1)
        {
            ExceptionHelper.WriteErrorToActivityLog(ex1);
        }

        // Remove the NuGetImportStamp so that VC++ project file won't be updated with this stamp on disk,
        // which causes unnecessary source control pending changes.
        try
        {
            propStore.RemoveProperty(NuGetImportStamp, string.Empty, (uint)_PersistStorageType.PST_PROJECT_FILE);
        }
        catch (Exception ex2)
        {
            ExceptionHelper.WriteErrorToActivityLog(ex2);
        }
    }
}

我的博客会首发于 https://blog.walterlv.com/,而 CSDN 会从其中精选发布,但是一旦发布了就很少更新。

如果在博客看到有任何不懂的内容,欢迎交流。我搭建了 dotnet 职业技术学院 欢迎大家加入。

知识共享许可协议

本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。欢迎转载、使用、重新发布,但务必保留文章署名吕毅(包含链接:https://walterlv.blog.csdn.net/),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请与我联系。

相关文章:

  • C#/.NET 中 Thread.Sleep(0), Task.Delay(0), Thread.Yield(), Task.Yield() 不同的执行效果和用法建议
  • WPF 中那些可跨线程访问的 DispatcherObject(WPF Free Threaded Dispatcher Object)
  • 在 Visual Studio Code 中为代码片段(Code Snippets)添加快捷键
  • 在 Visual Studio 中使用 EditorConfig 统一代码风格(含原生与插件)
  • 在 Visual Studio Code 中添加自定义的代码片段
  • 用 dotTrace 进行性能分析时,Timeline 打不开?无法启动进程?也许你需要先开启系统性能计数器的访问权限
  • 了解 .NET/C# 程序集的加载时机,以便优化程序启动性能
  • git 如何更可靠地解决冲突?
  • .NET 编写一个可以异步等待循环中任何一个部分的 Awaiter
  • 文件被占用?系统自带的“资源监视器(resmon)”也能帮你找到占用它的真凶
  • Windows 系统文件资源管理器的命令行参数(如何降权打开程序,如何选择文件)
  • 为 .NET 各种开发工具设置网络代理,提升在大陆的网络性能
  • 如何在旧版本的 .NET Core / Framework 中使用 C# 8 的异步流(IAsyncDisposable / IAsyncEnumerable / IAsyncEnumerator)
  • .NET/C# 解压 Zip 文件时出现异常:System.IO.InvalidDataException: 找不到中央目录结尾记录。
  • 为什么实现 .NET 的 ICollection 集合时需要实现 SyncRoot 属性?如何正确实现这个属性?
  • SegmentFault for Android 3.0 发布
  • [rust! #004] [译] Rust 的内置 Traits, 使用场景, 方式, 和原因
  • centos安装java运行环境jdk+tomcat
  • E-HPC支持多队列管理和自动伸缩
  • iOS 系统授权开发
  • mysql 5.6 原生Online DDL解析
  • rabbitmq延迟消息示例
  • React Native移动开发实战-3-实现页面间的数据传递
  • React-Native - 收藏集 - 掘金
  • SAP云平台里Global Account和Sub Account的关系
  • VUE es6技巧写法(持续更新中~~~)
  • win10下安装mysql5.7
  • 订阅Forge Viewer所有的事件
  • 发布国内首个无服务器容器服务,运维效率从未如此高效
  • 构建工具 - 收藏集 - 掘金
  • 快速体验 Sentinel 集群限流功能,只需简单几步
  • 力扣(LeetCode)21
  • 批量截取pdf文件
  • 前端自动化解决方案
  • 如何邀请好友注册您的网站(模拟百度网盘)
  • $().each和$.each的区别
  • (2022版)一套教程搞定k8s安装到实战 | RBAC
  • (3)选择元素——(17)练习(Exercises)
  • (AtCoder Beginner Contest 340) -- F - S = 1 -- 题解
  • (Mac上)使用Python进行matplotlib 画图时,中文显示不出来
  • (Matalb回归预测)PSO-BP粒子群算法优化BP神经网络的多维回归预测
  • (附源码)spring boot球鞋文化交流论坛 毕业设计 141436
  • (附源码)ssm跨平台教学系统 毕业设计 280843
  • (个人笔记质量不佳)SQL 左连接、右连接、内连接的区别
  • (十一)c52学习之旅-动态数码管
  • (未解决)macOS matplotlib 中文是方框
  • (一)使用Mybatis实现在student数据库中插入一个学生信息
  • .[backups@airmail.cc].faust勒索病毒的最新威胁:如何恢复您的数据?
  • .aanva
  • .Net Attribute详解(上)-Attribute本质以及一个简单示例
  • .NET delegate 委托 、 Event 事件
  • .net中我喜欢的两种验证码
  • /bin/bash^M: bad interpreter: No such file ordirectory
  • /etc/skel 目录作用
  • @四年级家长,这条香港优才计划+华侨生联考捷径,一定要看!