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

阻止某个 NuGet 包意外升级

出于兼容性考虑,我们可能不再更新某个项目的 NuGet 包。典型的情况是软件版本进行了大规模的不兼容的升级,需要对旧格式的数据进行读取,以便迁移到新格式的数据。

然而,团队开发的软件可能因为某个小伙伴不知道这样的历史问题,从而手抖将某个不应该更新的 NuGet 包更新了,于是迁移就挂了。

本文提供了一种方法来避免某些特定 NuGet 包的升级。


如果你只关心结果,请直接前往最后一节:终极解决方案

准备工作

本文提供的方法仅适用于使用了 Sdk 风格的 csproj 项目文件。(当然并不是说旧的 csproj 不能使用这种方法,只是写法上会有差别,我没有去研究如何编写。)

如果你的项目还在使用旧的 csproj 格式,推荐阅读 将 WPF、UWP 以及其他各种类型的旧 csproj 迁移成 Sdk 风格的 csproj 迁移成新格式之后再开始。

作为例子,假设我们的项目文件是这样的:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net47</TargetFramework>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="LiteDB" Version="2.0.2" />
    <PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
  </ItemGroup>
</Project>

在这里插入图片描述

LiteDB 是一个不应该被升级的 NuGet 包,但是最新版本已经是 4.1.4 了,很容易被团队中的其他小伙伴误升级。

在这里插入图片描述
▲ 当小伙伴打开包管理器的时候,会发现包版本不一致,然后就不小心升级了

思路

NuGet 使用 PackageReference 来管理所有的包引用,于是我试图通过隐藏 LiteDB 的 PackageReference 节点来达到目的。

而一个典型的隐藏方法便是使用 Target。不在 Target 里面的属性和项是提前计算好的,而 Target 里面的属性和项是编译时才计算的。

可以通过阅读 如何编写基于 Microsoft.NET.Sdk 的跨平台的 MSBuild Target 了解更多 Target 的知识。

所以,我写了这样的 Target,然后去掉前面的 PackageReference

<!-- 其实这种改法并没有作用,可谁知道呢! -->
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net47</TargetFramework>
  </PropertyGroup>
  <ItemGroup>
  <!-- 在这里把之前的 LiteDB 去掉了。 -->
    <PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
  </ItemGroup>
  <!-- 这是新写的 Target,用来在编译期间引用 LiteDB。不过我不知道应该在什么时机执行。 -->
  <Target Name="ReferenceStaticLegacyPackage" BeforeTargets="???">
    <ItemGroup>
      <PackageReference Include="LiteDB" Version="2.0.2" />
    </ItemGroup>
  </Target>
</Project>

还留了一个 BeforeTargets 没有填,因为并不知道应该填什么。于是我打开了 Microsoft.NET.Sdk 的文件夹 C:\Program Files\dotnet\sdk\2.1.300\Sdks,试图寻找时机。

搜索 @(PackageReference) 发现有很多的 Target 都依赖于一个名为 CollectPackageReferencesTarget

<Target Name="CollectResolvedSDKReferencesDesignTime"
        Returns="@(_ResolvedSDKReference)"
        DependsOnTargets="ResolveSDKReferencesDesignTime;CollectPackageReferences">
    <!-- 省略 -->
</Target>

从名称上可以猜测这是用来收集 PackageReferenceTarget

于是我可以将我们的 BeforeTargets 指定为 CollectPackageReferences

不过我发现在这种情况下,NuGet 包管理器的界面中能够发现这个项目使用了旧版本。并且在安装了新版本的包后,将因为多次引用不同版本而导致编译不通过。

所以,方案否决。

最终解决

既然无法阻止发现这个 NuGet 包,那思路就换成无论如何更新,都无效好了。

于是,通过 Remove 和重新 Include 固定版本来解决。

下面是项目的最终解决源码:

<!-- 其实这种改法并没有作用,可谁知道呢! -->
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net47</TargetFramework>
  </PropertyGroup>
  <ItemGroup>
    <!-- 无论这里版本填写多少,都不会有效。 -->
    <PackageReference Include="LiteDB" Version="4.1.4" />
    <PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
  </ItemGroup>
  <!-- 通过移除正常的引用并替换成固定版本的引用,达到无论如何更新都无法生效的目的。 -->
  <Target Name="ReferenceStaticLegacyPackage" BeforeTargets="CollectPackageReferences">
    <ItemGroup>
      <PackageReference Remove="LiteDB" />
      <PackageReference Include="LiteDB" Version="2.0.2" />
    </ItemGroup>
  </Target>
</Project>

在这种 Target 的帮助下,无论如何更新 LiteDB 的 NuGet 版本,都能更新成功,但无法生效。


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

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

知识共享许可协议

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

相关文章:

  • 解读 Microsoft.NET.Sdk 的源码,你能定制各种奇怪而富有创意的编译过程
  • 在 Visual Studio 的解决方案资源管理器中隐藏一些文件
  • 长期支持 LTS(Long-term Support)是怎样的一种支持方式
  • .NET Standard 的管理策略
  • 如何在 .NET/C# 代码中安全地结束掉一个控制台应用程序?通过发送 Ctrl+C 信号来结束
  • Windows 10 应用创建模糊背景窗口的三种方法
  • 使用 PInvoke.net Visual Studio Extension 辅助编写 Win32 函数签名
  • 程序员与英语:即时聊天中的英语缩写 lol / lmao / idk
  • 使用 IFTTT 做 RSS 的邮件订阅服务
  • .NET/C# 使窗口永不激活(No Activate 永不获得焦点)
  • 语法高亮不够漂亮?这里有你想要的 Rouge 主题
  • 理解 UWP 视图的概念,让 UWP 应用显示多个窗口(多视图)
  • UWP 扩展/自定义标题栏的方法,一些概念和一些注意事项
  • 图片点击放大,你的网页也能做到!
  • UWP 应用中 CoreApplication / Application, CoreWindow / Window 之间的区别
  • [NodeJS] 关于Buffer
  • “Material Design”设计规范在 ComponentOne For WinForm 的全新尝试!
  • Angular 2 DI - IoC DI - 1
  • CentOS 7 修改主机名
  • CODING 缺陷管理功能正式开始公测
  • const let
  • Docker下部署自己的LNMP工作环境
  • ERLANG 网工修炼笔记 ---- UDP
  • iOS仿今日头条、壁纸应用、筛选分类、三方微博、颜色填充等源码
  • Java 最常见的 200+ 面试题:面试必备
  • Java小白进阶笔记(3)-初级面向对象
  • java小心机(3)| 浅析finalize()
  • k个最大的数及变种小结
  • Linux快速复制或删除大量小文件
  • MySQL几个简单SQL的优化
  • PHP 7 修改了什么呢 -- 2
  • Promise初体验
  • React Native移动开发实战-3-实现页面间的数据传递
  • REST架构的思考
  • vue数据传递--我有特殊的实现技巧
  • vue总结
  • 动态魔术使用DBMS_SQL
  • 腾讯优测优分享 | Android碎片化问题小结——关于闪光灯的那些事儿
  • 微信小程序填坑清单
  • 我是如何设计 Upload 上传组件的
  • 一些基于React、Vue、Node.js、MongoDB技术栈的实践项目
  • ​​​​​​​​​​​​​​汽车网络信息安全分析方法论
  • ​sqlite3 --- SQLite 数据库 DB-API 2.0 接口模块​
  • #Ubuntu(修改root信息)
  • #我与Java虚拟机的故事#连载05:Java虚拟机的修炼之道
  • (4)通过调用hadoop的java api实现本地文件上传到hadoop文件系统上
  • (a /b)*c的值
  • (ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_READ_ONLY)讲解
  • (WSI分类)WSI分类文献小综述 2024
  • (六)Hibernate的二级缓存
  • (一)搭建springboot+vue前后端分离项目--前端vue搭建
  • (一)硬件制作--从零开始自制linux掌上电脑(F1C200S) <嵌入式项目>
  • .【机器学习】隐马尔可夫模型(Hidden Markov Model,HMM)
  • .NET Compact Framework 3.5 支持 WCF 的子集
  • .NetCore项目nginx发布