我们.NET开发会引用很多外部Nuget包,多项目、多个解决方案、甚至多个仓库。

简单的Nuget包管理,通过VS就能比较简单处理好。但复杂的场景呢,比如:

1.一个仓库里,有多个解决方案的Nuget包管理 -- 我现在项目就是这样的,针对会议大屏的全家桶软件集代码仓库。这个仓库里,接近30个工具/应用软件:

2. 多个仓库,多个解决方案、多个项目的Nuget管理 --  公司产品项目业务比较复杂,抽了很多组件(目前有53个),组件间需要处理好版本冲突,以及因版本依赖需要递增升级自己的版本号

这些复杂场景会给开发同学带来操作管理代码的低效问题

所以如何高效处理好项目之间的Nuget版本冲突,如何快速升级Nuget版本,如何解决多项目对Nuget组件源码的快速调试,下面我们分别介绍下

Nuget版本统一、版本升级

项目Csproj引用Nuget包,管理多个项目的解决方案,通过VS的Nuget管理页面以及CsProj文件编辑能解决大部分情况下的Nuget版本操作。

稍复杂的仓库内多解决方案场景,经MVP德熙大佬 lindexi - 博客园 (cnblogs.com) 的提醒,中央包管理即Packages.props文件也能可以管理一个仓库内的Nuget包,中央包管理 | Microsoft Learn。Packages.props统一配置好仓库内公共的Nuget包版本,然后Csproj根据需要引用相应的Nuget包

1 <Project Sdk="Microsoft.NET.Sdk">
2 <PropertyGroup>
3 <TargetFramework>net6.0</TargetFramework>
4 </PropertyGroup>
5 <ItemGroup>
6 <PackageReference Include="Newtonsoft.Json" />
7 </ItemGroup>
8 </Project>

但是,其实我个人不推荐。如果新增了一个Nuget引用DirectShowLib,VS又会默认在CSproj文件内添加版本号,未自动归纳到Package.props内:

需要按中央包管理路径,去解决方案或者仓库下Packages.props添加Nuget版本信息。此方案有一定的修改成本,个人也觉得项目的Nuget引用版本号不够显眼、阅读性会减弱

继续用csproj管理Nuget包,多项目之间的管理成本我们用工具解决: WindowsOrg/NugetEfficientTool: VisualStudio项目开发-Nuget操作效率工具 (github.com)

下图是使用Nuget版本工具,对全家桶应用集所在的文件夹进行Nuget版本检测:

按上图界面操作:
1. 项目路径,输入仓库代码所在的目录(也可以外层目录)、解决方案sln文件路径

2. 点击“开始检测”,会显示Nuget待修复信息。如上图,H3C.Setting.Camera存在很多csproj文件内版本是一致的,但与Nuget源最新版本不一致,需要升级

3. 点击“修复版本冲突”,弹出版本选择窗口,选择相应版本执行修复。执行后,csproj都会同步H3C.Setting.Camera版本为1.1.15

选择版本时,可以全选也可以指定Nuget同步版本号。

Nuget版本工具,是基于小伙伴博哥工具版本dotnet-campus/dotnetCampus.NugetMergeFixTool (github.com)优化改良的。工具目前已经实现的功能:

1. 同步一个解决方案下Nuget版本

2. 同步一个文件夹下多解决方案的Nuget版本
3. 支持设置Nuget源,同步升级Nuget版本(上方截图就是这个场景)

Nuget版本同步的原理,是获取路径下的所有csproj文件,解析出引用Nuget的PackageReference信息-Nuget名称、版本号。列出这些版本不同的Nuget,让开发人员选择需要降级或者升级的版本号,然后再次修改保存csproj文件的Nuget引用版本号。

获取一个csproj文档内所有PackageReference节点:

 1     /// <summary>
2 /// 获取PackageReference名称对应的节点
3 /// </summary>
4 /// <param name="xDocument"></param>
5 /// <returns></returns>
6 private List<XElement> GetPackageReferenceElements(XDocument xDocument)
7 {
8 if (xDocument == null)
9 {
10 throw new ArgumentNullException(nameof(xDocument));
11 }
12 var xElementList = new List<XElement>();
13 var itemGroupElements = xDocument.Root.Elements().Where(x => x.Name.LocalName == "ItemGroup");
14 foreach (var itemGroupElement in itemGroupElements)
15 {
16 xElementList.AddRange(itemGroupElement.Elements().Where(x => x.Name.LocalName == "PackageReference"));
17 }
18 return xElementList;
19 }

另外,针对多个仓库多个解决方案的组件集版本号同步场景,下面介绍下53个组件仓库70多个Nuget包是如何一键解决Nuget之间版本依赖的问题。Nuget版本工具结合Jenkins做了个版本号自动构建:

1. 组件集工作组下的所有组件仓库clone到本地,并行编译多个解决方案

2. 后台执行Nuget版本工具,csproj文件中Nuget自动升级到最高版本。同时csproj文件如有引用变更,则当前组件自身版本号新增一位构建号

3. 循环第2步,直到所有csproj文件内相同Nuget包的版本均一致

4. 保存归档Nuget包至服务器,Git Push所有组件仓库对csproj文件的变更

下图是Jenkins某次一键同步Nuget包版本的流程日志:

有需要的可以直接下载Nuget工具:Nuget工具_1.0.3.1012.exe,以命令行参数执行、一键升级目录下的所有组件版本号:NugetEfficientTool.exe D:\Gitlab-Company\Components

Nuget源代码替换

开发应用,遇到需要调试内部的Nuget源代码,一般我们是卸载Nuget包、然后引用源组件项目csproj。

这样操作,1-2个项目操作还好,遇上几十个项目需要替换Nuget源项目代码,效率就很低了

简单重复的事情,都要通过工具来简化,这里也是Nuget工具操作:

1. 项目路径,输入仓库代码所在的目录(也可以是多仓库目录),或者仓库解决方案.sln文件路径

2. 输入Nuget包H3C.Family.App对应的源代码csproj文件路径,或者csproj所在目录。输入后,会自动填充Nuget名称H3C.Family.App

3. 点击“替换”,替换所有csproj项目内H3C.Family.App的Nuget引用PackageReference,为H3C.Family.App的项目引用ProjectReference,就可以开始源代码调试了

代码调试完,可以点击“还原”,撤回Nuget源代码的替换。然后Nuget源代码可以提交代码、归档Nuget包给上层应用使用了。

是不是很方便呢?这里Nuget替换,目前版本支持:

1. 一个解决方案,对多个Nuget源的替换

2. 目录即多个解决方案,对多个Nuget源的替换

3. 支持多个相互之间依赖的Nuget包替换(自动识别依赖)和还原

支持目录下的Nuget替换,极大提升了多项目多仓库协同开发的效率,见某次的操作:

我所在项目是智能交互大屏会议场景的会议软件集(全家桶),应用软件太多了。有时候修复一个摄像头组件的BUG,人工操作的话需要打开每个解决方案、替换源代码,太浪费时间。

而通过Nuget工具一键替换所有的Nuget源代码,然后用VS解决方案管理器切换视图,可极快的打开项目进行源代码确认、调试,效率提升相差一个量级

这里的Nuget替换,最初始版本是小伙伴俊杰同学 J.晒太阳的猫 - 博客园 (cnblogs.com) 开发的,初始版本是在VS内以扩展工具来运行。可惜我没有他的源代码,就按个人需求另外开发了一套exe的Nuget工具版本。

其中主要逻辑,
1. 解决方案,新增Nuget名称项目:

 1     var solutionFileLines = File.ReadAllLines(_solutionFile).ToList();
2 //添加Project
3 var previousProjectIndex = solutionFileLines.FindLastIndex(i => i.Contains(StartProjectRex));
4 var previousProjectLine = solutionFileLines[previousProjectIndex];
5 var solutionId = previousProjectLine.Replace(StartProjectRex, string.Empty).Substring(0, GuidLength);
6 var newProjectLine = $"{StartProjectRex}{solutionId}}}\") = \"{_nugetName}\", \"{_sourceProjectFile}\", \"{{{_newProjectId}}}\"";
7 solutionFileLines.Insert(previousProjectIndex + 2, "EndProject");
8 solutionFileLines.Insert(previousProjectIndex + 2, newProjectLine);
9 //添加编译配置
10 var projectConfigStartIndex = solutionFileLines.FindIndex(i => i.Contains("ProjectConfigurationPlatforms"));
11 var projectConfigEndIndex = solutionFileLines.FindIndex(projectConfigStartIndex + 1, i => i.Contains("EndGlobalSection"));
12 solutionFileLines.Insert(projectConfigEndIndex, $"{{{_newProjectId}}}.Debug|Any CPU.Build.0 = Release|Any CPU");
13 solutionFileLines.Insert(projectConfigEndIndex, $"{{{_newProjectId}}}.Debug|Any CPU.Build.0 = Release|Any CPU");
14 solutionFileLines.Insert(projectConfigEndIndex, $"{{{_newProjectId}}}.Release|Any CPU.Build.0 = Release|Any CPU");
15 solutionFileLines.Insert(projectConfigEndIndex, $"{{{_newProjectId}}}.Release|Any CPU.Build.0 = Release|Any CPU");
16 File.WriteAllLines(_solutionFile, solutionFileLines, Encoding.UTF8);

2. Csprj文件,替换Nuget引用为源项目csproj的项目引用:

 1     public ReplacedFileRecord ReplaceNuget()
2 {
3 var nugetInfoReferences = CsProj.GetNugetReferences(Document).ToList();
4 var referenceElement = nugetInfoReferences.FirstOrDefault(x => CsProj.GetNugetInfo(x).Name == _nugetName);
5 if (referenceElement == null)
6 {
7 return null;
8 }
9 //获取Nuget引用信息
10 var replacedFileRecord = GetNugetReferenceInfo(referenceElement, nugetInfoReferences.IndexOf(referenceElement));
11 //删除Nuget的引用
12 referenceElement.Remove();
13 //添加源项目的引用
14 AddSourceReference();
15 SaveFile();
16 return replacedFileRecord;
17 }

一个csproj文件,可能会有多个替换Nuget的记录,这些记录用于后续的还原操作:

 1     /// <summary>
2 /// 一个文件Nuget变更记录
3 /// </summary>
4 [DataContract]
5 public class ReplacedFileRecord
6 {
7 /// <summary>
8 /// Nuget名称
9 /// </summary>
10 [DataMember]
11 public string NugetName { get; set; }
12 /// <summary>
13 /// 文件名称
14 /// </summary>
15 [DataMember]
16 public string FileName { get; set; }
17 /// <summary>
18 /// 变更位置
19 /// </summary>
20 [DataMember]
21 public int ModifiedLineIndex { get; set; }
22 /// <summary>
23 /// Nuget版本
24 /// </summary>
25 [DataMember]
26 public string Version { get; set; }
27 [CanBeNull]
28 [DataMember]
29 public string TargetFramework { get; set; }
30 [CanBeNull]
31 [DataMember]
32 public string NugetDllPath { get; set; }
33 /// <summary>
34 /// Nuget引用方式
35 /// </summary>
36 [DataMember]
37 public string ReferenceType { get; set; }
38 }

Nuget工具:Nuget工具_1.0.3.1012.exe,最新版以及源代码详见:WindowsOrg/NugetEfficientTool: VisualStudio项目开发-Nuget操作效率工具 (github.com)

关键字:Nuget包管理、Nuget源代码调试、NugetEfficientTool

.NET 高效开发Nuget管理工具(开源)的更多相关文章

  1. PyRedisAdmin v1.0 Beta 发布,Redis 在线管理工具 - 开源中国社区

    PyRedisAdmin v1.0 Beta 发布,Redis 在线管理工具 - 开源中国社区 PyRedisAdmin v1.0 Beta 发布,Redis 在线管理工具

  2. IOS开发依赖管理工具CocoaPods

    CocoaPods IOS开发依赖管理工具 CocoaPods is a dependency manager for Swift and Objective-C Cocoa projects. It ...

  3. GIT: 分布式开发 代码管理工具使用命令大全

    代码管理工具: GIT     什么是GIT? Git是一款免费.开源的分布式版本控制系统,用于敏捷高效地处理任何或小或大的项目 Git是一个开源的分布式版本控制系统,用以有效.高速的处理从很小到非常 ...

  4. 我发起了一个用 .Net 编写的 源代码管理工具 开源项目 SourceKit

    发起这个 项目 的 起因 是 GitHub . Github 的 使用技能 俨然已经成了 一项新技术 , 这不是 工具 的 本意 . 我用过的 源代码 管理工具 不多,  SVN 我觉得不错 . 常用 ...

  5. Python之开发自动化管理工具paramiko

    一.paramiko模块使用 1)远程执行主机命令获取结果 方法一 import paramiko # 创建SSH对象 ssh = paramiko.SSHClient() # 允许连接不在know_ ...

  6. NuGet管理工具安装

    安装完成后VS重启即可

  7. 开发人员必备的几款bug管理工具

    Bug是软件开发过程中的“副产品”,也是开发人员最不想见到的状况.如果没有跟踪和梳理各种bug和问题并及时解决,项目就会花费非常多的时间,导致整个项目的重心偏移.如果在产品开发过程中,使用一个合适的B ...

  8. 打造Worktile敏捷开发管理工具的思与惑

    从2019年初,我们团队准备开发一款适合研发团队使用的敏捷开发管理工具,那时候我们也在思考,到底什么样的工具才算是优秀的研发管理工具,研发管理的场景.方法和流派有很多,市面上关于研发管理工具的产品也是 ...

  9. brew mac osx 上软件包管理工具

    今天推荐 Mac OSX 下,方便高效的包管理工具 brew brew 的全名叫做 Homebrew 它的功能类似于 ubuntu 下同下 apt-get ,或者 Cent OS 下的 yum 等包管 ...

  10. 使用Nuget管理dll

    前言 nuget 已经不是什么新东西,它是vs的一个扩展工具,可以让我们在项目中添加.删除.更新引用变得更加快捷方便.现在有许多传统公司对dll的管理还是很落后的,有些甚至时通过发送dll文件,这样做 ...

随机推荐

  1. textFieldShouldReturn: 方法无效化!

    问题描述 不管如何在键盘上点击return,textFieldShouldReturn:方法一直没有调用. 问题代码 @interface ViewController : UIViewControl ...

  2. echo输出带颜色的字

    文章目录 格式 所有颜色 字体样式 示例 格式 \033[A;F;Bm #放在文本的左边,可以影响后面所有字体的样式 解释: F代表字体颜色值(Font),颜色编号30~37. B代表背景颜色值(Ba ...

  3. Kubernetes(K8s)最新版搭建

    Kubernetes简单介绍 Kubernetes意为舵手,简称K8s. 前身是Google的Borg.所以一开源就吸引了一大批注意力. 因为谷歌,所以墙.在国内搭建K8s非常头疼. 下面我就来介绍一 ...

  4. BST-Treap名次树数组&指针实现板子 Ver1.0

    这里只有板子没有原理QWQ 可实现 1.插入 x 数 2.删除 x 数(若有多个相同的数,只删除一个) 3.查询 x 数的排名(排名定义为比当前数小的数的个数 +1) 4.查询排名为 x 的数 5.求 ...

  5. 实验6.交换机MAC地址表简单实验

    # 实验6.交换机Mac地址表 本实验用于验证和测试交换机的Mac地址表的特性. 实验组 测试 测试在PC1没有pingPC2时,此时mac表为空 当PC1ping一个其他的ip而不是PC2时,无论是 ...

  6. Stable Diffusion(二)WebUI使用指南

    1. 前言 基于 https://stable-diffusion-art.com/ 内的教程进行翻译与整理,帮助快速上手 stable-diffusion 的使用. 2. 环境 AWS DeepLe ...

  7. 【Autoware】Autoware安装教程

    此篇主要是从自己的csdn copy 在博客园上备份一下哈~ 前提:大家需要换源[软件源和pip源]:git clone的时候走博主给的Gitee的链接吧 不然得等个十万年... 如果想看最终是啥样的 ...

  8. Java(screw)生成数据库表结构

    数据库支持 MySQL MariaDB TIDB Oracle SqlServer PostgreSQL Cache DB(2016) 文档生成支持 html word markdown 方式一:代码 ...

  9. Mac Vue-cli脚手架搭建

    安装node环境 官网地址:http://nodejs.cn/download/ 我选择版本:v16.16.0 修改npm镜像地址 # 查看镜像地址 npm config get registry # ...

  10. MySQL 索引失效

    全列匹配 最佳左前缀法则 不在索引列上做任何操作(计算.函数.自动.手动类型转换),会导致索引失效 存储引擎不能使用索引中范围条件右边的列 尽量使用覆盖索引(只访问索引的查询(索引和查询列一致)),少 ...