背景
我的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):执行超时已过期。在操作完成之前超时时间已过,或者服务器没有响应。
System.ComponentModel.Win32Exception(0x80004005):等待操作超时
另一台计算机给出:
命名管道提供者:无法打开与SQL Server [5]的连接。链接服务器“ RemoteServer”的OLE DB访问接口“ SQLNCLI11”返回消息“登录超时已过期”。链接服务器“ RemoteServer”的OLE DB提供程序“ SQLNCLI11”返回消息“建立与SQL Server的连接时发生了与网络相关或特定于实例的错误。找不到或无法访问服务器。请检查实例名称是否正确以及是否SQL Server配置为允许远程连接。有关更多信息,请参见SQL Server联机丛书。“]
已启用命名管道,并且超时发生在大约5-10秒内。
该登录名是Windows用户,在两台服务器上均具有sysadmin角色,但是我也尝试通过sp_addlinkedsrvlogin
设置SQL用户,但对抛出的异常没有影响。
我无法看到为什么应该有任何区别,因为我期望SqlCommand
将SQL发送到SQL Server服务进行处理。我想念什么?
所以看来这里有两个问题:
SqlCommand
在.NET中的查询有所不同,并且当我通过SSMS进行测试时,我试图尽可能地复制.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;
}
}
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
明确告知了该查询。一旦添加了分布式关键字,SSMS也会因与.NET类似的错误而失败。
在Component Services(从命令行dcomcnfg)中找到了记录充分的复选框,典型设置如下所示。
让我感到震惊的是,Windows防火墙规则虽然已存在,但默认情况下是禁用的。尝试访问RemoteServer的两台计算机之间的潜在差异是,一台计算机已禁用防火墙,因此不受出站规则的阻止,但两者均被RemoteServer上禁用的入站规则的阻止。这是出站规则
和现在显示的入站规则已启用。