dotnet-project-structure

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

.NET Project Structure and Build Configuration

.NET项目结构与构建配置

When to Use This Skill

何时使用此技能

Use this skill when:
  • Setting up a new .NET solution with modern best practices
  • Configuring centralized build properties across multiple projects
  • Implementing central package version management
  • Setting up SourceLink for debugging and NuGet packages
  • Automating version management with release notes
  • Pinning SDK versions for consistent builds
在以下场景使用此技能:
  • 采用现代化最佳实践搭建新的.NET解决方案
  • 为多个项目配置集中式构建属性
  • 实现集中式包版本管理
  • 为调试和NuGet包设置SourceLink
  • 通过发布说明自动化版本管理
  • 固定SDK版本以确保构建一致性

Related Skills

相关技能

  • dotnet-local-tools
    - Managing local .NET tools with dotnet-tools.json
  • microsoft-extensions-configuration
    - Configuration validation patterns

  • dotnet-local-tools
    - 使用dotnet-tools.json管理本地.NET工具
  • microsoft-extensions-configuration
    - 配置验证模式

Solution File Format (.slnx)

解决方案文件格式(.slnx)

The
.slnx
format is the modern XML-based solution file format introduced in .NET 9. It replaces the traditional
.sln
format.
.slnx
是.NET 9中引入的现代化XML格式解决方案文件,替代了传统的
.sln
格式。

Benefits Over Traditional .sln

相较于传统.sln的优势

Aspect.sln (Legacy).slnx (Modern)
FormatCustom text formatStandard XML
ReadabilityGUIDs, cryptic syntaxClean, human-readable
Version controlHard to diff/mergeEasy to diff/merge
EditingIDE requiredAny text editor
方面.sln(传统版).slnx(现代版)
格式自定义文本格式标准XML格式
可读性包含GUID、语法晦涩清晰易读,适合人类阅读
版本控制难以对比/合并易于对比/合并
编辑方式需要IDE可使用任何文本编辑器

Version Requirements

版本要求

ToolMinimum Version
.NET SDK9.0.200
Visual Studio17.13
MSBuildVisual Studio Build Tools 17.13
Note: Starting with .NET 10,
dotnet new sln
creates
.slnx
files by default. In .NET 9, you must explicitly migrate or specify the format.
工具最低版本
.NET SDK9.0.200
Visual Studio17.13
MSBuildVisual Studio Build Tools 17.13
注意: 从.NET 10开始,
dotnet new sln
默认创建
.slnx
文件。在.NET 9中,你需要显式迁移或指定格式。

Example .slnx File

.slnx文件示例

xml
<Solution>
  <Folder Name="/build/">
    <File Path="Directory.Build.props" />
    <File Path="Directory.Packages.props" />
    <File Path="global.json" />
    <File Path="NuGet.Config" />
    <File Path="README.md" />
  </Folder>
  <Folder Name="/src/">
    <Project Path="src/MyApp/MyApp.csproj" />
    <Project Path="src/MyApp.Core/MyApp.Core.csproj" />
  </Folder>
  <Folder Name="/tests/">
    <Project Path="tests/MyApp.Tests/MyApp.Tests.csproj" />
  </Folder>
</Solution>
xml
<Solution>
  <Folder Name="/build/">
    <File Path="Directory.Build.props" />
    <File Path="Directory.Packages.props" />
    <File Path="global.json" />
    <File Path="NuGet.Config" />
    <File Path="README.md" />
  </Folder>
  <Folder Name="/src/">
    <Project Path="src/MyApp/MyApp.csproj" />
    <Project Path="src/MyApp.Core/MyApp.Core.csproj" />
  </Folder>
  <Folder Name="/tests/">
    <Project Path="tests/MyApp.Tests/MyApp.Tests.csproj" />
  </Folder>
</Solution>

Migrating from .sln to .slnx

从.sln迁移到.slnx

Use the
dotnet sln migrate
command to convert existing solutions:
bash
undefined
使用
dotnet sln migrate
命令转换现有解决方案:
bash
undefined

Migrate a specific solution file

迁移指定的解决方案文件

dotnet sln MySolution.sln migrate
dotnet sln MySolution.sln migrate

Or if only one .sln exists in the directory, just run:

如果目录中只有一个.sln文件,直接运行:

dotnet sln migrate

**Important:** Do not keep both `.sln` and `.slnx` files in the same repository. This causes issues with automatic solution detection and can lead to sync problems. After migration, delete the old `.sln` file.

You can also migrate in Visual Studio:
1. Open the solution
2. Select the Solution in Solution Explorer
3. Go to **File > Save Solution As...**
4. Change "Save as type" to **Xml Solution File (*.slnx)**
dotnet sln migrate

**重要提示:** 不要在同一仓库中同时保留`.sln`和`.slnx`文件,这会导致自动解决方案检测出现问题,还可能引发同步错误。迁移完成后,请删除旧的`.sln`文件。

你也可以通过Visual Studio进行迁移:
1. 打开解决方案
2. 在解决方案资源管理器中选择解决方案
3. 进入 **文件 > 另存解决方案为...**
4. 将“保存类型”更改为 **Xml解决方案文件 (*.slnx)**

Creating a New .slnx Solution

创建新的.slnx解决方案

bash
undefined
bash
undefined

.NET 10+: Creates .slnx by default

.NET 10及以上版本:默认创建.slnx文件

dotnet new sln --name MySolution
dotnet new sln --name MySolution

.NET 9: Specify the format explicitly

.NET 9:显式指定格式

dotnet new sln --name MySolution --format slnx
dotnet new sln --name MySolution --format slnx

Add projects (works the same for both formats)

添加项目(两种格式操作相同)

dotnet sln add src/MyApp/MyApp.csproj
undefined
dotnet sln add src/MyApp/MyApp.csproj
undefined

Recommendation

推荐建议

If you're using .NET 9.0.200 or later, migrate your solutions to .slnx. The benefits are significant:
  • Dramatically fewer merge conflicts (no random GUIDs changing)
  • Human-readable and editable in any text editor
  • Consistent with modern
    .csproj
    format
  • Better diff/review experience in pull requests

如果你使用的是.NET 9.0.200或更高版本,请将解决方案迁移到.slnx。 其优势十分显著:
  • 大幅减少合并冲突(不会出现随机GUID变更)
  • 人类可读,可在任何文本编辑器中编辑
  • 与现代化
    .csproj
    格式保持一致
  • 在拉取请求中拥有更好的对比/审查体验

Directory.Build.props

Directory.Build.props

Directory.Build.props
provides centralized build configuration that applies to all projects in a directory tree. Place it at the solution root.
Directory.Build.props
提供集中式构建配置,可应用于目录树中的所有项目。请将其放置在解决方案根目录。

Complete Example

完整示例

xml
<Project>
  <!-- Metadata -->
  <PropertyGroup>
    <Authors>Your Team</Authors>
    <Company>Your Company</Company>
    <!-- Dynamic copyright year - updates automatically -->
    <Copyright>Copyright © 2020-$([System.DateTime]::Now.Year) Your Company</Copyright>
    <Product>Your Product</Product>
    <PackageProjectUrl>https://github.com/yourorg/yourrepo</PackageProjectUrl>
    <RepositoryUrl>https://github.com/yourorg/yourrepo</RepositoryUrl>
    <PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
    <PackageTags>your;tags;here</PackageTags>
  </PropertyGroup>

  <!-- C# Language Settings -->
  <PropertyGroup>
    <LangVersion>latest</LangVersion>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
    <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
    <NoWarn>$(NoWarn);CS1591</NoWarn> <!-- Missing XML comments -->
  </PropertyGroup>

  <!-- Version Management -->
  <PropertyGroup>
    <VersionPrefix>1.0.0</VersionPrefix>
    <PackageReleaseNotes>See RELEASE_NOTES.md</PackageReleaseNotes>
  </PropertyGroup>

  <!-- Target Framework Definitions (reusable properties) -->
  <PropertyGroup>
    <NetStandardLibVersion>netstandard2.0</NetStandardLibVersion>
    <NetLibVersion>net8.0</NetLibVersion>
    <NetTestVersion>net9.0</NetTestVersion>
  </PropertyGroup>

  <!-- SourceLink Configuration -->
  <PropertyGroup>
    <PublishRepositoryUrl>true</PublishRepositoryUrl>
    <EmbedUntrackedSources>true</EmbedUntrackedSources>
    <IncludeSymbols>true</IncludeSymbols>
    <SymbolPackageFormat>snupkg</SymbolPackageFormat>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.SourceLink.GitHub" PrivateAssets="All" />
  </ItemGroup>

  <!-- NuGet Package Assets -->
  <ItemGroup>
    <None Include="$(MSBuildThisFileDirectory)logo.png" Pack="true" PackagePath="\" />
    <None Include="$(MSBuildThisFileDirectory)README.md" Pack="true" PackagePath="\" />
  </ItemGroup>

  <PropertyGroup>
    <PackageIcon>logo.png</PackageIcon>
    <PackageReadmeFile>README.md</PackageReadmeFile>
  </PropertyGroup>

  <!-- Global Using Statements -->
  <ItemGroup>
    <Using Include="System.Collections.Immutable" />
  </ItemGroup>
</Project>
xml
<Project>
  <!-- 元数据 -->
  <PropertyGroup>
    <Authors>你的团队</Authors>
    <Company>你的公司</Company>
    <!-- 动态版权年份 - 自动更新 -->
    <Copyright>Copyright © 2020-$([System.DateTime]::Now.Year) 你的公司</Copyright>
    <Product>你的产品</Product>
    <PackageProjectUrl>https://github.com/yourorg/yourrepo</PackageProjectUrl>
    <RepositoryUrl>https://github.com/yourorg/yourrepo</RepositoryUrl>
    <PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
    <PackageTags>your;tags;here</PackageTags>
  </PropertyGroup>

  <!-- C#语言设置 -->
  <PropertyGroup>
    <LangVersion>latest</LangVersion>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
    <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
    <NoWarn>$(NoWarn);CS1591</NoWarn> <!-- 缺少XML注释 -->
  </PropertyGroup>

  <!-- 版本管理 -->
  <PropertyGroup>
    <VersionPrefix>1.0.0</VersionPrefix>
    <PackageReleaseNotes>参见RELEASE_NOTES.md</PackageReleaseNotes>
  </PropertyGroup>

  <!-- 目标框架定义(可复用属性) -->
  <PropertyGroup>
    <NetStandardLibVersion>netstandard2.0</NetStandardLibVersion>
    <NetLibVersion>net8.0</NetLibVersion>
    <NetTestVersion>net9.0</NetTestVersion>
  </PropertyGroup>

  <!-- SourceLink配置 -->
  <PropertyGroup>
    <PublishRepositoryUrl>true</PublishRepositoryUrl>
    <EmbedUntrackedSources>true</EmbedUntrackedSources>
    <IncludeSymbols>true</IncludeSymbols>
    <SymbolPackageFormat>snupkg</SymbolPackageFormat>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.SourceLink.GitHub" PrivateAssets="All" />
  </ItemGroup>

  <!-- NuGet包资产 -->
  <ItemGroup>
    <None Include="$(MSBuildThisFileDirectory)logo.png" Pack="true" PackagePath="\" />
    <None Include="$(MSBuildThisFileDirectory)README.md" Pack="true" PackagePath="\" />
  </ItemGroup>

  <PropertyGroup>
    <PackageIcon>logo.png</PackageIcon>
    <PackageReadmeFile>README.md</PackageReadmeFile>
  </PropertyGroup>

  <!-- 全局Using语句 -->
  <ItemGroup>
    <Using Include="System.Collections.Immutable" />
  </ItemGroup>
</Project>

Key Patterns

关键模式

Dynamic Copyright Year

动态版权年份

xml
<Copyright>Copyright © 2020-$([System.DateTime]::Now.Year) Your Company</Copyright>
Uses MSBuild property functions to insert current year at build time. No manual updates needed.
xml
<Copyright>Copyright © 2020-$([System.DateTime]::Now.Year) 你的公司</Copyright>
使用MSBuild属性函数在构建时插入当前年份,无需手动更新。

Reusable Target Framework Properties

可复用目标框架属性

Define target frameworks once, reference everywhere:
xml
<!-- In Directory.Build.props -->
<PropertyGroup>
  <NetLibVersion>net8.0</NetLibVersion>
  <NetTestVersion>net9.0</NetTestVersion>
</PropertyGroup>

<!-- In MyApp.csproj -->
<PropertyGroup>
  <TargetFramework>$(NetLibVersion)</TargetFramework>
</PropertyGroup>

<!-- In MyApp.Tests.csproj -->
<PropertyGroup>
  <TargetFramework>$(NetTestVersion)</TargetFramework>
</PropertyGroup>
只需定义一次目标框架,即可在各处引用:
xml
<!-- 在Directory.Build.props中 -->
<PropertyGroup>
  <NetLibVersion>net8.0</NetLibVersion>
  <NetTestVersion>net9.0</NetTestVersion>
</PropertyGroup>

<!-- 在MyApp.csproj中 -->
<PropertyGroup>
  <TargetFramework>$(NetLibVersion)</TargetFramework>
</PropertyGroup>

<!-- 在MyApp.Tests.csproj中 -->
<PropertyGroup>
  <TargetFramework>$(NetTestVersion)</TargetFramework>
</PropertyGroup>

SourceLink for NuGet Packages

针对NuGet包的SourceLink配置

SourceLink enables step-through debugging of NuGet packages:
xml
<PropertyGroup>
  <PublishRepositoryUrl>true</PublishRepositoryUrl>
  <EmbedUntrackedSources>true</EmbedUntrackedSources>
  <IncludeSymbols>true</IncludeSymbols>
  <SymbolPackageFormat>snupkg</SymbolPackageFormat>
</PropertyGroup>

<ItemGroup>
  <!-- Choose the right provider for your source control -->
  <PackageReference Include="Microsoft.SourceLink.GitHub" PrivateAssets="All" />
  <!-- Or: Microsoft.SourceLink.AzureRepos.Git -->
  <!-- Or: Microsoft.SourceLink.GitLab -->
  <!-- Or: Microsoft.SourceLink.Bitbucket.Git -->
</ItemGroup>

SourceLink支持对NuGet包进行单步调试:
xml
<PropertyGroup>
  <PublishRepositoryUrl>true</PublishRepositoryUrl>
  <EmbedUntrackedSources>true</EmbedUntrackedSources>
  <IncludeSymbols>true</IncludeSymbols>
  <SymbolPackageFormat>snupkg</SymbolPackageFormat>
</PropertyGroup>

<ItemGroup>
  <!-- 根据你的源代码管理选择合适的提供方 -->
  <PackageReference Include="Microsoft.SourceLink.GitHub" PrivateAssets="All" />
  <!-- 或:Microsoft.SourceLink.AzureRepos.Git -->
  <!-- 或:Microsoft.SourceLink.GitLab -->
  <!-- 或:Microsoft.SourceLink.Bitbucket.Git -->
</ItemGroup>

Directory.Packages.props - Central Package Management

Directory.Packages.props - 集中式包管理

Central Package Management (CPM) provides a single source of truth for all NuGet package versions.
集中式包管理(CPM)为所有NuGet包版本提供单一可信来源。

Setup

设置步骤

xml
<Project>
  <PropertyGroup>
    <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
  </PropertyGroup>

  <!-- Define version variables for related packages -->
  <PropertyGroup>
    <AkkaVersion>1.5.35</AkkaVersion>
    <AspireVersion>9.1.0</AspireVersion>
  </PropertyGroup>

  <!-- Application Dependencies -->
  <ItemGroup Label="App Dependencies">
    <PackageVersion Include="Akka" Version="$(AkkaVersion)" />
    <PackageVersion Include="Akka.Cluster" Version="$(AkkaVersion)" />
    <PackageVersion Include="Akka.Persistence" Version="$(AkkaVersion)" />
    <PackageVersion Include="Microsoft.Extensions.Hosting" Version="9.0.0" />
  </ItemGroup>

  <!-- Build/Tooling Dependencies -->
  <ItemGroup Label="Build Dependencies">
    <PackageVersion Include="Microsoft.SourceLink.GitHub" Version="8.0.0" />
  </ItemGroup>

  <!-- Test Dependencies -->
  <ItemGroup Label="Test Dependencies">
    <PackageVersion Include="xunit" Version="2.9.3" />
    <PackageVersion Include="xunit.runner.visualstudio" Version="3.0.1" />
    <PackageVersion Include="FluentAssertions" Version="7.0.0" />
    <PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
    <PackageVersion Include="coverlet.collector" Version="6.0.3" />
  </ItemGroup>
</Project>
xml
<Project>
  <PropertyGroup>
    <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
  </PropertyGroup>

  <!-- 为相关包定义版本变量 -->
  <PropertyGroup>
    <AkkaVersion>1.5.35</AkkaVersion>
    <AspireVersion>9.1.0</AspireVersion>
  </PropertyGroup>

  <!-- 应用程序依赖项 -->
  <ItemGroup Label="App Dependencies">
    <PackageVersion Include="Akka" Version="$(AkkaVersion)" />
    <PackageVersion Include="Akka.Cluster" Version="$(AkkaVersion)" />
    <PackageVersion Include="Akka.Persistence" Version="$(AkkaVersion)" />
    <PackageVersion Include="Microsoft.Extensions.Hosting" Version="9.0.0" />
  </ItemGroup>

  <!-- 构建/工具依赖项 -->
  <ItemGroup Label="Build Dependencies">
    <PackageVersion Include="Microsoft.SourceLink.GitHub" Version="8.0.0" />
  </ItemGroup>

  <!-- 测试依赖项 -->
  <ItemGroup Label="Test Dependencies">
    <PackageVersion Include="xunit" Version="2.9.3" />
    <PackageVersion Include="xunit.runner.visualstudio" Version="3.0.1" />
    <PackageVersion Include="FluentAssertions" Version="7.0.0" />
    <PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
    <PackageVersion Include="coverlet.collector" Version="6.0.3" />
  </ItemGroup>
</Project>

Consuming Packages (No Version Needed)

引用包(无需指定版本)

xml
<!-- In MyApp.csproj -->
<ItemGroup>
  <PackageReference Include="Akka" />
  <PackageReference Include="Akka.Cluster" />
  <PackageReference Include="Microsoft.Extensions.Hosting" />
</ItemGroup>

<!-- In MyApp.Tests.csproj -->
<ItemGroup>
  <PackageReference Include="xunit" />
  <PackageReference Include="FluentAssertions" />
  <PackageReference Include="Microsoft.NET.Test.Sdk" />
</ItemGroup>
xml
<!-- 在MyApp.csproj中 -->
<ItemGroup>
  <PackageReference Include="Akka" />
  <PackageReference Include="Akka.Cluster" />
  <PackageReference Include="Microsoft.Extensions.Hosting" />
</ItemGroup>

<!-- 在MyApp.Tests.csproj中 -->
<ItemGroup>
  <PackageReference Include="xunit" />
  <PackageReference Include="FluentAssertions" />
  <PackageReference Include="Microsoft.NET.Test.Sdk" />
</ItemGroup>

Benefits

优势

  1. Single source of truth - All versions in one file
  2. No version drift - All projects use same versions
  3. Easy updates - Change once, applies everywhere
  4. Grouped packages - Version variables for related packages (e.g., all Akka packages)

  1. 单一可信来源 - 所有版本集中在一个文件中
  2. 无版本漂移 - 所有项目使用相同版本
  3. 易于更新 - 一次修改,全局生效
  4. 分组管理包 - 为相关包设置版本变量(例如所有Akka包)

global.json - SDK Version Pinning

global.json - SDK版本固定

Pin the .NET SDK version for consistent builds across all environments.
json
{
  "sdk": {
    "version": "9.0.200",
    "rollForward": "latestFeature"
  }
}
固定.NET SDK版本,确保在所有环境中构建一致。
json
{
  "sdk": {
    "version": "9.0.200",
    "rollForward": "latestFeature"
  }
}

Roll Forward Policies

向前兼容策略

PolicyBehavior
disable
Exact version required
patch
Same major.minor, latest patch
feature
Same major, latest minor.patch
latestFeature
Same major, latest feature band
minor
Same major, latest minor
latestMinor
Same major, latest minor
major
Latest SDK (not recommended)
Recommended:
latestFeature
- Allows patch updates within the same feature band.

策略行为
disable
要求使用精确版本
patch
相同主版本.次版本,使用最新补丁版本
feature
相同主版本,使用最新次版本.补丁版本
latestFeature
相同主版本,使用最新功能带版本
minor
相同主版本,使用最新次版本
latestMinor
相同主版本,使用最新次版本
major
使用最新SDK(不推荐)
推荐:
latestFeature
- 允许在同一功能带内使用补丁更新。

Version Management with RELEASE_NOTES.md

基于RELEASE_NOTES.md的版本管理

Release Notes Format

发布说明格式

markdown
undefined
markdown
undefined

1.2.0 January 15th 2025

1.2.0 2025年1月15日

  • Added new feature X
  • Fixed bug in Y
  • Improved performance of Z
  • 新增功能X
  • 修复Y中的bug
  • 优化Z的性能

1.1.0 December 10th 2024

1.1.0 2024年12月10日

  • Initial release with features A, B, C
undefined
  • 初始版本,包含功能A、B、C
undefined

Parsing Script (getReleaseNotes.ps1)

解析脚本(getReleaseNotes.ps1)

powershell
function Get-ReleaseNotes {
    param (
        [Parameter(Mandatory=$true)]
        [string]$MarkdownFile
    )

    $content = Get-Content -Path $MarkdownFile -Raw
    $sections = $content -split "####"

    $result = [PSCustomObject]@{
        Version      = $null
        Date         = $null
        ReleaseNotes = $null
    }

    if ($sections.Count -ge 3) {
        $header = $sections[1].Trim()
        $releaseNotes = $sections[2].Trim()

        $headerParts = $header -split " ", 2
        if ($headerParts.Count -eq 2) {
            $result.Version = $headerParts[0]
            $result.Date = $headerParts[1]
        }

        $result.ReleaseNotes = $releaseNotes
    }

    return $result
}
powershell
function Get-ReleaseNotes {
    param (
        [Parameter(Mandatory=$true)]
        [string]$MarkdownFile
    )

    $content = Get-Content -Path $MarkdownFile -Raw
    $sections = $content -split "####"

    $result = [PSCustomObject]@{
        Version      = $null
        Date         = $null
        ReleaseNotes = $null
    }

    if ($sections.Count -ge 3) {
        $header = $sections[1].Trim()
        $releaseNotes = $sections[2].Trim()

        $headerParts = $header -split " ", 2
        if ($headerParts.Count -eq 2) {
            $result.Version = $headerParts[0]
            $result.Date = $headerParts[1]
        }

        $result.ReleaseNotes = $releaseNotes
    }

    return $result
}

Version Bump Script (bumpVersion.ps1)

版本更新脚本(bumpVersion.ps1)

powershell
function UpdateVersionAndReleaseNotes {
    param (
        [Parameter(Mandatory=$true)]
        [PSCustomObject]$ReleaseNotesResult,
        [Parameter(Mandatory=$true)]
        [string]$XmlFilePath
    )

    $xmlContent = New-Object XML
    $xmlContent.Load($XmlFilePath)

    # Update VersionPrefix
    $versionElement = $xmlContent.SelectSingleNode("//VersionPrefix")
    $versionElement.InnerText = $ReleaseNotesResult.Version

    # Update PackageReleaseNotes
    $notesElement = $xmlContent.SelectSingleNode("//PackageReleaseNotes")
    $notesElement.InnerText = $ReleaseNotesResult.ReleaseNotes

    $xmlContent.Save($XmlFilePath)
}
powershell
function UpdateVersionAndReleaseNotes {
    param (
        [Parameter(Mandatory=$true)]
        [PSCustomObject]$ReleaseNotesResult,
        [Parameter(Mandatory=$true)]
        [string]$XmlFilePath
    )

    $xmlContent = New-Object XML
    $xmlContent.Load($XmlFilePath)

    # 更新VersionPrefix
    $versionElement = $xmlContent.SelectSingleNode("//VersionPrefix")
    $versionElement.InnerText = $ReleaseNotesResult.Version

    # 更新PackageReleaseNotes
    $notesElement = $xmlContent.SelectSingleNode("//PackageReleaseNotes")
    $notesElement.InnerText = $ReleaseNotesResult.ReleaseNotes

    $xmlContent.Save($XmlFilePath)
}

Build Script (build.ps1)

构建脚本(build.ps1)

powershell
undefined
powershell
undefined

Load helper scripts

加载辅助脚本

. "$PSScriptRoot\scripts\getReleaseNotes.ps1" . "$PSScriptRoot\scripts\bumpVersion.ps1"
. "$PSScriptRoot\scripts\getReleaseNotes.ps1" . "$PSScriptRoot\scripts\bumpVersion.ps1"

Parse release notes and update Directory.Build.props

解析发布说明并更新Directory.Build.props

$releaseNotes = Get-ReleaseNotes -MarkdownFile (Join-Path -Path $PSScriptRoot -ChildPath "RELEASE_NOTES.md") UpdateVersionAndReleaseNotes -ReleaseNotesResult $releaseNotes -XmlFilePath (Join-Path -Path $PSScriptRoot -ChildPath "Directory.Build.props")
Write-Output "Updated to version $($releaseNotes.Version)"
undefined
$releaseNotes = Get-ReleaseNotes -MarkdownFile (Join-Path -Path $PSScriptRoot -ChildPath "RELEASE_NOTES.md") UpdateVersionAndReleaseNotes -ReleaseNotesResult $releaseNotes -XmlFilePath (Join-Path -Path $PSScriptRoot -ChildPath "Directory.Build.props")
Write-Output "已更新至版本 $($releaseNotes.Version)"
undefined

CI/CD Integration

CI/CD集成

yaml
undefined
yaml
undefined

GitHub Actions example

GitHub Actions示例

  • name: Update version from release notes shell: pwsh run: ./build.ps1
  • name: Build run: dotnet build -c Release
  • name: Pack with tag version run: dotnet pack -c Release /p:PackageVersion=${{ github.ref_name }}
  • name: Push to NuGet run: dotnet nuget push **/*.nupkg --api-key ${{ secrets.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json

---
  • name: 从发布说明更新版本 shell: pwsh run: ./build.ps1
  • name: 构建 run: dotnet build -c Release
  • name: 使用标签版本打包 run: dotnet pack -c Release /p:PackageVersion=${{ github.ref_name }}
  • name: 推送到NuGet run: dotnet nuget push **/*.nupkg --api-key ${{ secrets.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json

---

NuGet.Config

NuGet.Config

Configure NuGet sources and behavior:
xml
<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <solution>
    <add key="disableSourceControlIntegration" value="true" />
  </solution>

  <packageSources>
    <clear />
    <add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
    <!-- Add private feeds if needed -->
    <!-- <add key="MyCompany" value="https://pkgs.dev.azure.com/myorg/_packaging/myfeed/nuget/v3/index.json" /> -->
  </packageSources>
</configuration>
Key Settings:
  • <clear />
    - Remove inherited/default sources for reproducible builds
  • disableSourceControlIntegration
    - Prevents TFS/Git integration issues

配置NuGet源和行为:
xml
<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <solution>
    <add key="disableSourceControlIntegration" value="true" />
  </solution>

  <packageSources>
    <clear />
    <add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
    <!-- 如有需要,添加私有源 -->
    <!-- <add key="MyCompany" value="https://pkgs.dev.azure.com/myorg/_packaging/myfeed/nuget/v3/index.json" /> -->
  </packageSources>
</configuration>
关键设置:
  • <clear />
    - 移除继承的/默认源,确保构建可复现
  • disableSourceControlIntegration
    - 避免TFS/Git集成问题

Complete Project Structure

完整项目结构

MySolution/
├── .config/
│   └── dotnet-tools.json           # Local .NET tools
├── .github/
│   └── workflows/
│       ├── pr-validation.yml       # PR checks
│       └── release.yml             # NuGet publishing
├── scripts/
│   ├── getReleaseNotes.ps1         # Parse RELEASE_NOTES.md
│   └── bumpVersion.ps1             # Update Directory.Build.props
├── src/
│   ├── MyApp/
│   │   └── MyApp.csproj
│   └── MyApp.Core/
│       └── MyApp.Core.csproj
├── tests/
│   └── MyApp.Tests/
│       └── MyApp.Tests.csproj
├── Directory.Build.props           # Centralized build config
├── Directory.Packages.props        # Central package versions
├── MySolution.slnx                 # Modern solution file
├── global.json                     # SDK version pinning
├── NuGet.Config                    # Package source config
├── build.ps1                       # Build orchestration
├── RELEASE_NOTES.md                # Version history
├── README.md                       # Project documentation
└── logo.png                        # Package icon

MySolution/
├── .config/
│   └── dotnet-tools.json           # 本地.NET工具
├── .github/
│   └── workflows/
│       ├── pr-validation.yml       # PR检查
│       └── release.yml             # NuGet发布
├── scripts/
│   ├── getReleaseNotes.ps1         # 解析RELEASE_NOTES.md
│   └── bumpVersion.ps1             # 更新Directory.Build.props
├── src/
│   ├── MyApp/
│   │   └── MyApp.csproj
│   └── MyApp.Core/
│       └── MyApp.Core.csproj
├── tests/
│   └── MyApp.Tests/
│       └── MyApp.Tests.csproj
├── Directory.Build.props           # 集中式构建配置
├── Directory.Packages.props        # 集中式包版本管理
├── MySolution.slnx                 # 现代化解决方案文件
├── global.json                     # SDK版本固定
├── NuGet.Config                    # 包源配置
├── build.ps1                       # 构建编排脚本
├── RELEASE_NOTES.md                # 版本历史
├── README.md                       # 项目文档
└── logo.png                        # 包图标

Quick Reference

快速参考

FilePurpose
MySolution.slnx
Modern XML solution file
Directory.Build.props
Centralized build properties
Directory.Packages.props
Central package version management
global.json
SDK version pinning
NuGet.Config
Package source configuration
RELEASE_NOTES.md
Version history (parsed by build)
build.ps1
Build orchestration script
.config/dotnet-tools.json
Local .NET tools
文件用途
MySolution.slnx
现代化XML解决方案文件
Directory.Build.props
集中式构建属性
Directory.Packages.props
集中式包版本管理
global.json
SDK版本固定
NuGet.Config
包源配置
RELEASE_NOTES.md
版本历史(由构建脚本解析)
build.ps1
构建编排脚本
.config/dotnet-tools.json
本地.NET工具