PAYATHON 2026

旧式 sqlproj 如何固定 DacFx 并与 SqlPackage 同步版本?

数据小许

结论

非 SDK-style sqlproj 可以在本地和构建服务器上使用一致的构建工具链。一种可靠的做法是固定完整的 MSBuild、SSDT 和 DacFx 环境,并单独固定 SqlPackage 版本。

仅固定 Microsoft.SqlServer.DacFx NuGet 包,并不意味着旧式 sqlproj 的所有构建依赖都已固定。SQLDBExtensionsRefPath 可以指定 SQL 构建 targets 的位置,但不会下载依赖或自动同步 SqlPackage。

这里的“可重复”首先指相同输入使用相同工具链构建。若要求每次生成的 .dacpac 文件逐字节一致,还需另行验证,DacFx 版本相同本身不能保证这一点。

为什么只有 DacFx NuGet 包还不够?

旧式 sqlproj 会导入 Microsoft.Data.Tools.Schema.SqlTasks.targets,并依赖相应的 MSBuild task、DacFx 程序集和项目引用。Microsoft 的旧项目转换文档展示了这一导入结构;DacFx 下载文档则将 Microsoft.SqlServer.DacFx NuGet 包列为程序调用 DacServices API 的获取方式。两者用途不同,不能直接把 API 包视为完整的旧式 SSDT 构建发行包。旧项目结构说明、DacFx 与 SqlPackage 下载说明

把 SQLDBExtensionsRefPath 指向 NuGet 包的 lib 目录前,必须确认其中包含目标 targets 及其全部依赖。仅有 Microsoft.SqlServer.Dac.dll 等文件,不足以证明这一点。

方案一:固定完整构建环境

对于必须继续使用旧式 sqlproj 的项目,建议将经过验证的 Windows 构建环境制成版本固定的虚拟机镜像,或保留对应的 Visual Studio 安装布局。微软提供安装布局机制,可用于保存和复用安装文件。Visual Studio 安装布局说明

以下内容需要一并记录和管理:

  • Visual Studio 或 Build Tools 的具体版本,以及实际安装的 SSDT 组件。
  • MSBuild 的具体路径和版本。
  • SSDT 随附的 DacFx 版本。
  • master、msdb 等数据库引用、其他 .dacpac 引用及自定义构建扩展。
  • 构建配置、目标平台和会影响输出的项目属性。

本地和 CI 使用同一个构建脚本。如果开发者无法安装相同的工具链,可以在统一的虚拟机构建环境中运行脚本。发布产物应来自这套受控环境。

下面的 PowerShell 示例使用预先安装并固定的工具链,路径需要替换为实际位置:

$MsBuild = 'C:\Toolchains\VS2022\MSBuild\Current\Bin\MSBuild.exe'
$SsdtRoot = 'C:\Toolchains\VS2022\MSBuild\Microsoft\VisualStudio\v17.0\SSDT'

if (-not (Test-Path $MsBuild)) {
    throw "MSBuild not found: $MsBuild"
}

$SqlTargets = Join-Path $SsdtRoot 'Microsoft.Data.Tools.Schema.SqlTasks.targets'
if (-not (Test-Path $SqlTargets)) {
    throw "SQL build targets not found: $SqlTargets"
}

& $MsBuild '.\Database.sqlproj' `
    /t:Rebuild `
    /p:Configuration=Release `
    "/p:SQLDBExtensionsRefPath=$SsdtRoot" `
    /nr:false `
    /bl:database-build.binlog

if ($LASTEXITCODE -ne 0) {
    throw 'SQL project build failed.'
}

脚本的前提是项目通过 SQLDBExtensionsRefPath 条件导入 targets。应检查 .sqlproj 是否包含对应逻辑:

<Import
  Condition="'$(SQLDBExtensionsRefPath)' != ''"
  Project="$(SQLDBExtensionsRefPath)\Microsoft.Data.Tools.Schema.SqlTasks.targets" />
<Import
  Condition="'$(SQLDBExtensionsRefPath)' == ''"
  Project="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v$(VisualStudioVersion)\SSDT\Microsoft.Data.Tools.Schema.SqlTasks.targets" />

这个属性只覆盖该处导入路径。导入后的 targets 如何查找程序集,以及项目是否还有其他绝对路径依赖,仍需结合具体工具链和构建日志检查。

方案二:在隔离环境中替换 DacFx

如果固定的 SSDT 自带 DacFx 不满足需求,可以评估微软文档中的替换方法。建议在专用构建环境中操作并保存结果,避免每位开发者手工修改自己的安装目录。

微软明确要求在同一 DacFx 主版本内替换,例如用另一个 162.x 版本替换现有的 162.x,并要求替换后执行 Clean 或 Rebuild。文档列出了需要复制的文件及对应的框架目录,操作时应以目标版本文档和包内容为准。SSDT 的 DacFx 替换说明

因此,不能认为任意主版本都可以混用。若要从 162 升到更高主版本,应优先选择已集成目标版本的 SSDT 工具链,再验证项目兼容性。

团队也可以评估将整套构建文件整理到仓库 tools 目录或内部制品中,但需要自行维护目录布局、依赖、分发许可和兼容性。仅凭 DacFx NuGet 包,不能宣称已恢复 Microsoft.Data.Tools.Msbuild 的完整能力。

如何固定 SqlPackage 并保持版本同步?

SqlPackage 应通过仓库级版本配置单独固定。微软说明,从 161 开始,SqlPackage 的版本号与其关联的 DacFx 版本号对应。可以据此选择同一发布版本,但安装 SqlPackage 不会修改 SSDT 使用的 DacFx。SqlPackage 版本说明

可以使用 .NET local tool manifest。首次配置时,在仓库根目录执行以下 PowerShell 命令。团队必须将 PINNED_SQLPACKAGE_VERSION 设置为已经验证的精确包版本:

$SqlPackageVersion = $env:PINNED_SQLPACKAGE_VERSION
if ([string]::IsNullOrWhiteSpace($SqlPackageVersion)) {
    throw 'PINNED_SQLPACKAGE_VERSION must specify an exact package version.'
}

# 已有 tool manifest 时,跳过这一行。
dotnet new tool-manifest

dotnet tool install microsoft.sqlpackage --version $SqlPackageVersion

提交生成的 .config/dotnet-tools.json。之后,本地和 CI 都在仓库目录运行:

dotnet tool restore
dotnet tool run sqlpackage /Version

local tool manifest 记录工具版本,dotnet tool restore 按清单恢复工具,dotnet tool run 显式调用本地工具。.NET 本地工具说明

所选 SqlPackage 还必须满足相应的 .NET 运行时要求。不要直接将包版本号与 DLL 的 AssemblyVersion 字符串机械比较;应记录发布版本与实际文件版本的对应关系,并在升级时一起更新。

怎样确认本地和 CI 确实使用了同一套版本?

至少保存并检查以下证据:

  1. MSBuild 的路径和版本,以及实际导入的 SqlTasks.targets 路径。
  2. 构建任务实际加载的 DacFx 程序集位置与文件版本;必要时结合详细日志或程序集加载诊断确认。
  3. dotnet tool run sqlpackage /Version 的输出,以及仓库中的工具清单。
  4. 工具链文件和数据库引用的校验值。

文件存在检查只能发现路径缺失,不能证明兼容性。升级工具链后,还应执行干净构建、比较数据库模型,并针对代表性数据库检查生成的部署脚本。

如果希望“升级一个版本声明,就自动获得匹配的 SQL 构建依赖”,可以在后续评估迁移到 Microsoft.Build.Sql。微软目前仍区分旧式项目和 SDK-style 项目,迁移需要修改项目结构;不能简单地给旧式项目添加一个 SDK 包,就认为迁移完成。SQL 项目格式说明

备注:内容仅供参考。