使用 SqlCommand 执行时,什么可能导致链接服务器查询失败?
What might cause linked server query to fail when executed using SqlCommand?
背景
我的 C# 应用程序是一个 Windows 服务,它允许用户配置 SQL 涉及链接服务器的查询,以类似于 ETL(提取转换加载)应用程序来移动数据。这些查询已直接在 SSMS 中测试并且没有任何问题。
下面是一个示例查询,将数据从本地服务器上的 LocalDB 数据库插入到 RemoteServer 上的 RemoteDB 数据库。 RemoteServer 配置与它一起列出:
EXEC master.dbo.sp_addlinkedserver @server = N'RemoteServer', @srvproduct=N'SQL Server'
INSERT INTO RemoteServer.RemoteDB.dbo.tst_Address (Address, FirstName, LastName, DateOfBirth)
SELECT t1.Address, t1.FirstName, t1.LastName, t1.DateOfBirth
FROM LocalDB.dbo.tst_Address t1
LEFT JOIN RemoteServer.RemoteDB.dbo.tst_Address t2 ON t2.Address = t1.Address
WHERE t2.Address IS NULL
问题
当直接从 SSMS 执行时没有问题,但是当使用 SqlCommand.ExecuteNonQuery()
从 c# 应用程序执行时,我会发现各种错误,具体取决于我 运行 应用程序来自哪台计算机。例如,我手头的一个例外是
System.Data.SqlClient.SqlException (0x80131904): Execution Timeout Expired. The timeout period elapsed prior to completion of the operation or the server is not responding.
System.ComponentModel.Win32Exception (0x80004005): The wait operation timed out
另一台电脑给出:
Named Pipes Provider: Could not open a connection to SQL Server [5].
OLE DB provider "SQLNCLI11" for linked server "RemoteServer" returned message "Login timeout expired".
OLE DB provider "SQLNCLI11" for linked server "RemoteServer" returned message "A network-related or instance-specific error has occurred while establishing a connection to SQL Server. Server is not found or not accessible. Check if instance name is correct and if SQL Server is configured to allow remote connections. For more information see SQL Server Books Online.".
命名管道已启用,超时时间大约为 5-10 秒。
登录名是 windows 用户,在两台服务器上都是系统管理员角色,但我也尝试通过 sp_addlinkedsrvlogin
设置 SQL 用户,但这对抛出的异常。
我不明白为什么会有任何差异,因为我希望 SqlCommand
将 SQL 发送到 SQL 服务器服务进行处理。我错过了什么?
看来这里有两个问题:
- 通过
SqlCommand
和 在 SSMS 中执行的查询与在 .NET 中执行的查询的行为不同
- MSDTC 需要更多的知识,而不仅仅是在哪里打勾
SSMS 与 .NET
当我通过 SSMS 进行测试时,我试图尽可能地复制 .NET 方法。
.NET代码
var queryText = @"INSERT INTO RemoteServer.RemoteDB.dbo.tst_Address (Address, FirstName, LastName, DateOfBirth)
SELECT t1.Address, t1.FirstName, t1.LastName, t1.DateOfBirth
FROM LocalDB.dbo.tst_Address t1
LEFT JOIN RemoteServer.RemoteDB.dbo.tst_Address t2 ON t2.Address = t1.Address
WHERE t2.Address IS NULL"
using (var trans = cxn.BeginTransaction())
{
try
{
var cmd = this.BuildSqlCommand(queryText, parameters);
cmd.ExecuteNonQuery();
trans.Commit();
}
catch (Exception ex)
{
logger.Error(ex);
throw;
}
}
SSMS 查询
begin tran
begin try
INSERT INTO RemoteServer.RemoteDB.dbo.tst_Address (Address, FirstName, LastName, DateOfBirth)
SELECT t1.Address, t1.FirstName, t1.LastName, t1.DateOfBirth
FROM LocalDB.dbo.tst_Address t1
LEFT JOIN RemoteServer.RemoteDB.dbo.tst_Address t2 ON t2.Address = t1.Address
WHERE t2.Address IS NULL
commit
end try
begin catch
SELECT
ERROR_NUMBER() AS ErrorNumber
,ERROR_SEVERITY() AS ErrorSeverity
,ERROR_STATE() AS ErrorState
,ERROR_PROCEDURE() AS ErrorProcedure
,ERROR_LINE() AS ErrorLine
,ERROR_MESSAGE() AS ErrorMessage;
if @@TRANCOUNT > 0
rollback
end catch
SSMS 的问题是,默认情况下不需要分发查询,并且不涉及 DTC,除非使用 begin distributed tran
明确告知。一旦我添加了 distributed 关键字,SSMS 也会失败并出现与 .NET 类似的错误。
故障码配置
在组件服务(命令行中的 dcomcnfg)中找到了足够详细的记录复选框,典型设置如下所示。
让我感到惊讶的是 Windows 防火墙规则虽然预先存在,但默认情况下是禁用的。试图访问 RemoteServer 的两台计算机之间的潜在区别在于,其中一台禁用了防火墙,因此未被出站规则阻止,但两台计算机都被 RemoteServer 上禁用的入站规则阻止。以下是出站规则
显示的入站规则现已启用。
背景
我的 C# 应用程序是一个 Windows 服务,它允许用户配置 SQL 涉及链接服务器的查询,以类似于 ETL(提取转换加载)应用程序来移动数据。这些查询已直接在 SSMS 中测试并且没有任何问题。
下面是一个示例查询,将数据从本地服务器上的 LocalDB 数据库插入到 RemoteServer 上的 RemoteDB 数据库。 RemoteServer 配置与它一起列出:
EXEC master.dbo.sp_addlinkedserver @server = N'RemoteServer', @srvproduct=N'SQL Server'
INSERT INTO RemoteServer.RemoteDB.dbo.tst_Address (Address, FirstName, LastName, DateOfBirth)
SELECT t1.Address, t1.FirstName, t1.LastName, t1.DateOfBirth
FROM LocalDB.dbo.tst_Address t1
LEFT JOIN RemoteServer.RemoteDB.dbo.tst_Address t2 ON t2.Address = t1.Address
WHERE t2.Address IS NULL
问题
当直接从 SSMS 执行时没有问题,但是当使用 SqlCommand.ExecuteNonQuery()
从 c# 应用程序执行时,我会发现各种错误,具体取决于我 运行 应用程序来自哪台计算机。例如,我手头的一个例外是
System.Data.SqlClient.SqlException (0x80131904): Execution Timeout Expired. The timeout period elapsed prior to completion of the operation or the server is not responding.
System.ComponentModel.Win32Exception (0x80004005): The wait operation timed out
另一台电脑给出:
Named Pipes Provider: Could not open a connection to SQL Server [5]. OLE DB provider "SQLNCLI11" for linked server "RemoteServer" returned message "Login timeout expired". OLE DB provider "SQLNCLI11" for linked server "RemoteServer" returned message "A network-related or instance-specific error has occurred while establishing a connection to SQL Server. Server is not found or not accessible. Check if instance name is correct and if SQL Server is configured to allow remote connections. For more information see SQL Server Books Online.".
命名管道已启用,超时时间大约为 5-10 秒。
登录名是 windows 用户,在两台服务器上都是系统管理员角色,但我也尝试通过 sp_addlinkedsrvlogin
设置 SQL 用户,但这对抛出的异常。
我不明白为什么会有任何差异,因为我希望 SqlCommand
将 SQL 发送到 SQL 服务器服务进行处理。我错过了什么?
看来这里有两个问题:
- 通过
SqlCommand
和 在 SSMS 中执行的查询与在 .NET 中执行的查询的行为不同
- MSDTC 需要更多的知识,而不仅仅是在哪里打勾
SSMS 与 .NET
当我通过 SSMS 进行测试时,我试图尽可能地复制 .NET 方法。
.NET代码
var queryText = @"INSERT INTO RemoteServer.RemoteDB.dbo.tst_Address (Address, FirstName, LastName, DateOfBirth)
SELECT t1.Address, t1.FirstName, t1.LastName, t1.DateOfBirth
FROM LocalDB.dbo.tst_Address t1
LEFT JOIN RemoteServer.RemoteDB.dbo.tst_Address t2 ON t2.Address = t1.Address
WHERE t2.Address IS NULL"
using (var trans = cxn.BeginTransaction())
{
try
{
var cmd = this.BuildSqlCommand(queryText, parameters);
cmd.ExecuteNonQuery();
trans.Commit();
}
catch (Exception ex)
{
logger.Error(ex);
throw;
}
}
SSMS 查询
begin tran
begin try
INSERT INTO RemoteServer.RemoteDB.dbo.tst_Address (Address, FirstName, LastName, DateOfBirth)
SELECT t1.Address, t1.FirstName, t1.LastName, t1.DateOfBirth
FROM LocalDB.dbo.tst_Address t1
LEFT JOIN RemoteServer.RemoteDB.dbo.tst_Address t2 ON t2.Address = t1.Address
WHERE t2.Address IS NULL
commit
end try
begin catch
SELECT
ERROR_NUMBER() AS ErrorNumber
,ERROR_SEVERITY() AS ErrorSeverity
,ERROR_STATE() AS ErrorState
,ERROR_PROCEDURE() AS ErrorProcedure
,ERROR_LINE() AS ErrorLine
,ERROR_MESSAGE() AS ErrorMessage;
if @@TRANCOUNT > 0
rollback
end catch
SSMS 的问题是,默认情况下不需要分发查询,并且不涉及 DTC,除非使用 begin distributed tran
明确告知。一旦我添加了 distributed 关键字,SSMS 也会失败并出现与 .NET 类似的错误。
故障码配置
在组件服务(命令行中的 dcomcnfg)中找到了足够详细的记录复选框,典型设置如下所示。
让我感到惊讶的是 Windows 防火墙规则虽然预先存在,但默认情况下是禁用的。试图访问 RemoteServer 的两台计算机之间的潜在区别在于,其中一台禁用了防火墙,因此未被出站规则阻止,但两台计算机都被 RemoteServer 上禁用的入站规则阻止。以下是出站规则
显示的入站规则现已启用。