You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Directory.Build.props实现集中式程序集版本控制的问题

使用Directory.Build.props实现集中式程序集版本控制的问题

我最近在尝试给解决方案做部署构建时的自动统一版本控制,思路是用Deploy配置区分部署构建和内部测试构建,结果遇到了一个奇怪的问题——明明看起来配置都更新了,但最终编译的程序集还是旧版本号。先说说我做了哪些操作,再讲讲问题和解决办法:

我的操作步骤

1. 区分构建配置

基于Release创建了Deploy解决方案配置,只有用这个配置构建时,才会给所有程序集统一设置FileVersion和AssemblyVersion。

2. 统一程序集基础配置

创建了Default.targets文件,定义所有项目通用的程序集信息,所有项目都导入这个文件:

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <GenerateAssemblyInfo>true</GenerateAssemblyInfo>
    <AssemblyTitle>$(ProjectName)</AssemblyTitle>
    <Company>Company Name</Company>
    <Copyright>Copyright</Copyright>
    <Product>$(ProjectName)</Product>
    <GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
    <GenerateAssemblyInformationalVersionAttribute>false</GenerateAssemblyInformationalVersionAttribute>
    <AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
  </PropertyGroup>
</Project>

3. 集中管理版本号

在解决方案根目录创建Directory.Build.props,初始版本号如下:

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <FileVersion>25.7.16.17977</FileVersion>
    <AssemblyVersion>2025.7.3.0</AssemblyVersion>
  </PropertyGroup>
</Project>

4. 动态更新版本号

在构建序列的第一个项目里,加了一个UpdateVersion目标,用自定义工具修改Directory.Build.props的版本号,这个目标只在Deploy配置下执行,且在PreBuildEvent之前运行:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <Configurations>Debug;Release;Deploy</Configurations>
    <Platforms>AnyCPU</Platforms>
    <TargetFramework>net462</TargetFramework>
    <OutputType>Library</OutputType>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Deploy|AnyCPU'">
    <OutputPath>$(SolutionDir)Deploy\</OutputPath>
    <DebugType>none</DebugType>
    <Optimize>true</Optimize>
  </PropertyGroup>
  <ItemGroup>
    <Reference Include="System.Data.DataSetExtensions" />
    <Reference Include="Microsoft.CSharp" />
  </ItemGroup>
  <ItemGroup>
    <Folder Include="Properties\" />
  </ItemGroup>
  <Import Project="$(SolutionDir)Default.targets" />
  <Target Name="UpdateVersion" BeforeTargets="PreBuildEvent" Condition="'$(Configuration)' == 'Deploy'">
    <Message Importance="High" Text="Update assembly version ..." />
    <Exec Command="&quot;$(SolutionDir)UpdateAssemblyVersion\UpdateAssemblyVersion.exe&quot;" EchoOff="false" />
  </Target>
</Project>

遇到的问题

构建时,UpdateVersion确实成功把Directory.Build.props的版本号更新成了新值(比如FileVersion:25.7.16.19196、AssemblyVersion:2025.7.4.0),而且项目obj\Deploy目录下自动生成的AssemblyInfo.cs里也显示的是新版本号:

//------------------------------------------------------------------------------
// <auto-generated>
// Dieser Code wurde von einem Tool generiert.
// Laufzeitversion:4.0.30319.42000
//
// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn
// der Code erneut generiert wird.
// </auto-generated>
//------------------------------------------------------------------------------
using System;
using System.Reflection;

[assembly: System.Reflection.AssemblyCompanyAttribute("Company Name")]
[assembly: System.Reflection.AssemblyCopyrightAttribute("Copyright")]
[assembly: System.Reflection.AssemblyFileVersionAttribute("25.7.16.19196")]
[assembly: System.Reflection.AssemblyProductAttribute("MyLibrary")]
[assembly: System.Reflection.AssemblyTitleAttribute("MyLibrary")]
[assembly: System.Reflection.AssemblyVersionAttribute("2025.7.4.0")]

// Von der MSBuild WriteCodeFragment-Klasse generiert.

但最终编译出来的程序集,版本号还是旧的初始值,完全没用到新的版本号。

问题原因

这其实是踩了MSBuild的一个常见坑:Directory.Build.props是在解决方案刚加载的时候就被读取并解析的,这个时机远早于任何项目级的目标(包括PreBuildEvent)执行。也就是说,当你的UpdateVersion目标修改Directory.Build.props时,MSBuild早就把旧版本号加载到内存里了,后续编译全程用的都是旧值——哪怕生成的AssemblyInfo.cs看起来是新的,实际编译时因为MSBuild的属性缓存机制,并没有真正生效。

解决办法

既然Directory.Build.props的加载时机太早,动态修改它在当前构建会话里没用,我们换几种思路来实现:

方案1:动态传递版本属性(推荐)

不要修改文件,直接在构建时让版本工具输出版本号,然后把版本号作为全局属性传递给所有项目:

  1. 先把Directory.Build.props里的FileVersion和AssemblyVersion删掉,或者留个默认值当备选。
  2. 在解决方案根目录创建Directory.Build.targets,用来统一设置版本属性:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup Condition="'$(Configuration)' == 'Deploy'">
    <FileVersion>$(_DeployFileVersion)</FileVersion>
    <AssemblyVersion>$(_DeployAssemblyVersion)</AssemblyVersion>
  </PropertyGroup>
</Project>
  1. 修改第一个项目的UpdateVersion目标,让版本工具输出版本号,然后解析成MSBuild属性:
<Target Name="UpdateVersion" BeforeTargets="Build" Condition="'$(Configuration)' == 'Deploy'">
  <Message Importance="High" Text="Update assembly version ..." />
  <!-- 假设你的工具可以通过参数输出类似"FileVersion=25.7.16.19196;AssemblyVersion=2025.7.4.0"的内容到控制台 -->
  <Exec Command="&quot;$(SolutionDir)UpdateAssemblyVersion\UpdateAssemblyVersion.exe&quot; -outputVersion" 
        EchoOff="false"
        ConsoleToMSBuild="true">
    <Output TaskParameter="ConsoleOutput" PropertyName="_VersionOutput" />
  </Exec>
  <!-- 解析输出的版本号 -->
  <PropertyGroup>
    <_FileVersionPart>$([System.String]::Copy('$(_VersionOutput)').Split(';')[0].Split('=')[1])</_FileVersionPart>
    <_AssemblyVersionPart>$([System.String]::Copy('$(_VersionOutput)').Split(';')[1].Split('=')[1])</_AssemblyVersionPart>
  </PropertyGroup>
  <!-- 设置全局属性,让所有项目都能拿到 -->
  <CreateProperty Value="$(_FileVersionPart)">
    <Output TaskParameter="Value" PropertyName="_DeployFileVersion" />
  </CreateProperty>
  <CreateProperty Value="$(_AssemblyVersionPart)">
    <Output TaskParameter="Value" PropertyName="_DeployAssemblyVersion" />
  </CreateProperty>
</Target>

这样版本号会直接在构建过程中动态传递,不需要修改任何文件,也能确保所有项目用的都是最新值。

方案2:构建前先更新版本文件(适合脚本/CI场景)

如果一定要用修改Directory.Build.props的方式,那必须把版本工具的执行时机提前到MSBuild开始构建之前。比如写个PowerShell脚本:

# 先运行版本工具更新Directory.Build.props
& "$PSScriptRoot\UpdateAssemblyVersion\UpdateAssemblyVersion.exe"
# 再执行构建
msbuild YourSolution.sln /p:Configuration=Deploy

这样MSBuild加载Directory.Build.props时,版本号已经是新的了,自然会用新值编译。

方案3:CI/CD直接传参(最简单)

如果是在CI/CD管道里做Deploy构建,直接在调用MSBuild时传递版本号参数就行:

msbuild YourSolution.sln /p:Configuration=Deploy /p:FileVersion=25.7.16.19196 /p:AssemblyVersion=2025.7.4.0

这种方式完全不需要修改任何文件,直接覆盖所有项目的版本属性。

额外提醒

  • 确保所有项目都没有手动写的AssemblyInfo.cs里的版本属性,不然手动的属性会覆盖自动生成的。
  • 你已经在Default.targets里开了GenerateAssemblyInfo=true,这个是对的,确保MSBuild自动生成包含版本号的程序集信息。

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.07 11:54:34