如何在SQL项目中处理指向同一dacpac的多个链接服务器定义?
问题背景
我有多台数据库加入了Always On可用性组,主副本用于读写操作,辅助副本配置为只读查询。为此设置了两个链接服务器:
LINKED_SERVER_NAME指向主副本(读写)节点LINKED_SERVER_NAME_READ_ONLY指向辅助副本(只读)节点
但在将配置纳入源代码管理时遇到了问题:Visual Studio的SQL项目不允许添加多个指向同一dacpac文件的数据库引用,会报错**“此项目已包含此数据库引用”**。虽然实际服务器上这个配置完全可行,但无法在SQL项目中还原,而且保存两份相同的dacpac到源代码管理显然不合理。目前想到的生成后脚本复制dacpac的方法太繁琐,想找更优解。
可行解决方案
1. 用SQLCMD变量动态配置链接服务器
这是最简洁的方案,不用添加重复的数据库引用,而是通过变量区分链接服务器的目标节点。
在SQL项目中添加链接服务器的创建脚本,用SQLCMD变量定义主/辅节点地址:
-- 创建主链接服务器(读写) EXEC sp_addlinkedserver @server = N'LINKED_SERVER_NAME', @srvproduct = N'', @provider = N'SQLNCLI', @datasrc = N'$(PrimaryAGNode)', -- 主节点地址变量 @catalog = N'TargetDB'; -- 创建只读链接服务器 EXEC sp_addlinkedserver @server = N'LINKED_SERVER_NAME_READ_ONLY', @srvproduct = N'', @provider = N'SQLNCLI', @datasrc = N'$(SecondaryAGNode)', -- 辅节点地址变量 @catalog = N'TargetDB';
然后在项目的发布配置中,分别设置PrimaryAGNode和SecondaryAGNode的具体值(比如主节点的实例名/IP、辅节点的实例名/IP)。这样项目里只需要添加一次目标数据库的引用即可,因为两个链接服务器指向的是同一个数据库,只是连接的节点不同,完全符合SQL项目的引用规则。
2. 通用MSBuild目标自动复制dacpac(优化现有方法)
如果不想用SQLCMD变量,可以把生成后脚本改成通用的MSBuild目标,避免每个项目手动修改。
- 创建一个名为
CopyReadOnlyDacpac.targets的文件,内容如下:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Target Name="CopyReadOnlyDacpac" AfterTargets="Build"> <Copy SourceFiles="$(OutputPath)$(ProjectName).dacpac" DestinationFiles="$(OutputPath)$(ProjectName)_ReadOnly.dacpac" OverwriteReadOnlyFiles="true" /> </Target> </Project>
- 在需要的数据库项目的
.sqlproj文件中导入这个目标(可以放在文件末尾):
<Import Project="..\CopyReadOnlyDacpac.targets" />
这样每次构建项目时,会自动在输出目录生成一个带_ReadOnly后缀的dacpac副本,之后就可以在项目中分别添加原dacpac和副本的引用,无需手动复制。
3. 将链接服务器配置移到服务器级别管理
把链接服务器的创建从数据库项目中剥离,放在服务器级别的部署脚本里(比如用PowerShell或SQL脚本)。这样数据库项目不需要包含链接服务器的定义,自然也就不会有引用冲突的问题。
示例PowerShell脚本:
# 创建主链接服务器 Invoke-SqlCmd -ServerInstance "LocalServer" -Query @" EXEC sp_addlinkedserver @server = N'LINKED_SERVER_NAME', @srvproduct = N'', @provider = N'SQLNCLI', @datasrc = N'PrimaryAGNodeInstance', @catalog = N'TargetDB'; "@ # 创建只读链接服务器 Invoke-SqlCmd -ServerInstance "LocalServer" -Query @" EXEC sp_addlinkedserver @server = N'LINKED_SERVER_NAME_READ_ONLY', @srvproduct = N'', @provider = N'SQLNCLI', @datasrc = N'SecondaryAGNodeInstance', @catalog = N'TargetDB'; "@
这种方式适合多个数据库共用同一组链接服务器的场景,缺点是需要单独维护服务器级别的部署脚本,和数据库项目的发布流程分开。
内容的提问来源于stack exchange,提问作者nicedream

