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

Visual Studio 2017 以前的旧格式的 csproj Import 进来的 targets 文件有时不能正确计算属性(PropertyGroup)和集合(ItemGroup)

我在之前的博客中有教大家如何编写 NuGet 工具包,其中就有编写 .targets 文件。

我在实际的使用中,发现 Visual Studio 2017 带来的含 Sdk 的新 csproj 格式基本上没有多少坑;然而旧的 csproj 文件却总是不能完美的运行,总是出错。关键是,不是每台电脑都出错,不是每个时机都出错。

本文将讲一些坑。


本文内容

      • 本文的前置知识
      • 问题
      • 原因
      • 解决办法
      • 衍生知识

本文的前置知识

你可能需要了解 csproj 文件的格式和编译过程,才可能读懂本文,所以需要先阅读:

  • 理解 C# 项目 csproj 文件格式的本质和编译流程

问题

下面的代码来自 SourceFusion 项目的早期版本。

这是一个 .targets 文件,项目安装此 NuGet 包之后就会自动 Import 这个 targets 文件。

<Project>
    <PropertyGroup>
        <_DefaultSourceFusionWorkingFolder Condition="'$(_DefaultSourceFusionWorkingFolder)' == ''">obj\$(Configuration)\</_DefaultSourceFusionWorkingFolder>
        <SourceFusionWorkingFolder Condition="'$(SourceFusionWorkingFolder)' == ''">$(_DefaultSourceFusionWorkingFolder)</SourceFusionWorkingFolder>
        <SourceFusionToolsFolder>$(SourceFusionWorkingFolder)SourceFusion.Tools\</SourceFusionToolsFolder>
        <SourceFusionGeneratedCodeFolder>$(SourceFusionWorkingFolder)SourceFusion.GeneratedCodes\</SourceFusionGeneratedCodeFolder>
    </PropertyGroup>
  
    <Target Name="_SourceFusionCreateDirectories" BeforeTargets="_SourceFusionWriteCompilingArgs;_SourceFusionWriteFilterArgs">
        <ItemGroup>
            <SourceFusionDirectory Include="$(SourceFusionWorkingFolder)" />
            <SourceFusionDirectory Include="$(SourceFusionToolsFolder)" />
            <SourceFusionDirectory Include="$(SourceFusionGeneratedCodeFolder)" />
        </ItemGroup>
        <MakeDir Directories="@(SourceFusionDirectory)" ContinueOnError="false" />
    </Target>
</Project>

代码的解读如下:

  1. 创建了一个私有属性 _DefaultSourceFusionWorkingFolder,三个公有属性 SourceFusionWorkingFolderSourceFusionToolsFolderSourceFusionGeneratedCodeFolder
  2. 在编译期间,执行一个私有的 Target,收集所有收集到的文件夹,形成一个 SourceFusionDirectory 集合。然后将集合中的所有字符串视为文件夹,创建这几个文件夹。

在新的有 Sdk 的 csproj 中,这个 targets 文件的执行没有问题。但是,对于旧的 csproj 来说,就经常出现这几个属性为空或者部分为空的情况。额外的,就算修改这个文件,上面的属性也不会生效。

不过,如果使用命令行进行编译,这个却又是生效的。

原因

究其原因,这是 MSBuild 对项目文件(csproj)的解析和 Visual Studio 对项目文件的解析是不同的。命令行使用的是 MSBuild 解析 csproj,而 Visual Studio 使用的是 VSProjectSystem。

对于 VSProjectSystem 来说,Project 根节点下的 PropertyGroupItemGroup 对不会更新。有时清除 Visual Studio 的项目缓存可以解决这个问题,但有时清除也不能解决。

真实的原因我并没有调查出来。但以上代码在大多数开发者的 Visual Studio 中是可以正常使用的,但有少数开发者使用这个会出现错误(没有创建任何文件夹)。

解决办法

既然问题出在 MSBuild 和 VSProjectSystem 对属性和集合处理的不同,那么我就不要创建动态的集合,而是在 Target 内部编写属性和集合。

在 Target 内部的属性和集合将在编译期间进行计算,而不是在 Visual Studio 打开的时候就计算好。于是我们每次编译的时候都可以获得最新的属性和集合的值。

衍生知识

旧格式的 csproj 是不会自动计算属性和集合的变更的,这也是为什么项目文件改变的时候,Visual Studio 需要重新加载项目才可以正常显示和编译项目。同时,如果编辑旧格式的 csproj 文件,也需要先卸载掉项目才可以。而新格式的 csproj 是可以直接编辑而不需要卸载项目的,同时如果被外部改变,也不需要重新加载项目,而是可以直接计算出来新的属性和集合。

相关文章:

  • 使用 ReSharper,输入即遵循 StyleCop 的代码格式化规范
  • StyleCop 是什么,可以帮助团队带来什么价值?
  • 文件和文件夹不存在的时候,FileSystemWatcher 监听不到文件的改变?如果递归地监听就可以了
  • C#/.NET 使用 CommandLineParser 来标准化地解析命令行
  • .NET 中使用 TaskCompletionSource 作为线程同步互斥或异步操作的事件
  • 使用 WPF 开发一个 Windows 屏幕保护程序
  • 在 Windows 10 中开启移动 WLAN 热点
  • .NET/C# 项目如何优雅地设置条件编译符号?
  • 在 Roslyn 分析语法树时添加条件编译符号的支持
  • 自然码的形码
  • 出于迁移项目的考虑,GitHub 中 Fork 出来的项目,如何与原项目断开 Fork 关系?
  • 只需 5 秒钟,你就能取到 WPF 程序的超高分辨率超高清截图
  • 谨慎使用 FileInfo.Exists 实例方法,而是使用 File.Exists 静态方法替代
  • UWP 在 WebView 中执行 JavaScript 代码(用于模拟用户输入等)
  • .NET 中使用 Mutex 进行跨越进程边界的同步
  • 【node学习】协程
  • macOS 中 shell 创建文件夹及文件并 VS Code 打开
  • pdf文件如何在线转换为jpg图片
  • Ruby 2.x 源代码分析:扩展 概述
  • UEditor初始化失败(实例已存在,但视图未渲染出来,单页化)
  • use Google search engine
  • vue 个人积累(使用工具,组件)
  • 手机app有了短信验证码还有没必要有图片验证码?
  • 思考 CSS 架构
  • 小程序滚动组件,左边导航栏与右边内容联动效果实现
  • TPG领衔财团投资轻奢珠宝品牌APM Monaco
  • 新年再起“裁员潮”,“钢铁侠”马斯克要一举裁掉SpaceX 600余名员工 ...
  • ​LeetCode解法汇总2304. 网格中的最小路径代价
  • ​一帧图像的Android之旅 :应用的首个绘制请求
  • # Python csv、xlsx、json、二进制(MP3) 文件读写基本使用
  • (11)工业界推荐系统-小红书推荐场景及内部实践【粗排三塔模型】
  • (done) ROC曲线 和 AUC值 分别是什么?
  • (Matalb时序预测)WOA-BP鲸鱼算法优化BP神经网络的多维时序回归预测
  • (分享)自己整理的一些简单awk实用语句
  • (原創) 物件導向與老子思想 (OO)
  • (转)人的集合论——移山之道
  • (最完美)小米手机6X的Usb调试模式在哪里打开的流程
  • *setTimeout实现text输入在用户停顿时才调用事件!*
  • .apk文件,IIS不支持下载解决
  • .Net Attribute详解(上)-Attribute本质以及一个简单示例
  • .NET DevOps 接入指南 | 1. GitLab 安装
  • .NET 使用配置文件
  • .NET8.0 AOT 经验分享 FreeSql/FreeRedis/FreeScheduler 均已通过测试
  • .NET微信公众号开发-2.0创建自定义菜单
  • .NET下ASPX编程的几个小问题
  • .py文件应该怎样打开?
  • /var/log/cvslog 太大
  • @hook扩展分析
  • [ vulhub漏洞复现篇 ] JBOSS AS 5.x/6.x反序列化远程代码执行漏洞CVE-2017-12149
  • [2018/11/18] Java数据结构(2) 简单排序 冒泡排序 选择排序 插入排序
  • [8-27]正则表达式、扩展表达式以及相关实战
  • [Android] Implementation vs API dependency
  • [AX]AX2012 AIF(四):文档服务应用实例
  • [C++] new和delete
  • [C++数据结构](31)哈夫曼树,哈夫曼编码与解码