【问题标题】:Managed ODP.NET Calls to PUBLIC Database Link tables cause TNS errors对 PUBLIC 数据库链接表的托管 ODP.NET 调用导致 TNS 错误
【发布时间】:2016-12-16 20:52:59
【问题描述】:

我们的应用程序使用托管的 ODP.NET 代码来调用各种 Oracle 过程。

对于我们的一位使用公共数据库链接并在过程中引用链接表的客户,该调用失败。经过进一步测试,任何通过 ODP.NET 对链接表运行查询的尝试都会失败。

ORA-12154: TNS: 无法解析指定的连接标识符

全栈:

Oracle.ManagedDataAccess.Client.OracleException: ORA-12154: TNS:could not resolve the connect identifier specified 
at OracleInternal.ServiceObjects.OracleCommandImpl.VerifyExecution(OracleConnectionImpl connectionImpl, Int32& cursorId, Boolean bThrowArrayBindRelatedErrors, OracleException& exceptionForArrayBindDML, Boolean& hasMoreRowsInDB, Boolean bFirstIterationDone) 
at OracleInternal.ServiceObjects.OracleCommandImpl.ExecuteReader(String commandText, OracleParameterCollection paramColl, CommandType commandType, OracleConnectionImpl connectionImpl, OracleDataReaderImpl& rdrImpl, Int32 longFetchSize, Int64 clientInitialLOBFS, OracleDependencyImpl orclDependencyImpl, Int64[] scnForExecution, Int64[]& scnFromExecution, OracleParameterCollection& bindByPositionParamColl, Boolean& bBindParamPresent, Int64& internalInitialLOBFS, OracleException& exceptionForArrayBindDML, Boolean isDescribeOnly, Boolean isFromEF) 
at Oracle.ManagedDataAccess.Client.OracleCommand.ExecuteReader(Boolean requery, Boolean fillRequest, CommandBehavior behavior) at Oracle.ManagedDataAccess.Client.OracleCommand.ExecuteDbDataReader(CommandBehavior behavior) 
at System.Data.Common.DbCommand.System.Data.IDbCommand.ExecuteReader()
at [APPLICATION CODE]

这很奇怪,因为尝试使用 SQL PLUS 进行相同查询会成功,即使 客户端计算机上没有 TNS 名称或文件

我们的应用甚至不使用 TNS 名称,它使用明确的连接字符串详细信息。

我们甚至通过 DevArt oracle 驱动程序运行了测试查询,它们也很成功。

就好像专门通过托管 ODP.NET 进行调用使服务器以不同的方式对待数据库链接。

ODP.NET  Query (Client Machine A) > (Server A) > Table (Server B) = ERROR
SQL Plus Query (Client Machine A) > (Server A) > Table (Server B) = SUCCESS
DEV ART  Query (Client Machine A) > (Server A) > Table (Server B) = SUCCESS

我们现在使用的测试查询是一个简单的 SELECT 语句

有谁知道我们如何让 ODP.NET 像 SQL Plus 一样将这些信息交给服务器来获取?

数据库链接信息:

OWNER:    PUBLIC
USERNAME: [FIXED OTHER USER]

【问题讨论】:

  • 在服务器 A 上应该有 tnsnames.ora,其中包含服务器 B 上远程数据库的正确服务名称和连接字符串 (ADDRESS=(protocol_address_information)) (CONNECT_DATA= (SERVICE_NAME=service_name))) 检查此服务名称针对步骤 4 中使用的数据库链接的 DDL。触发器
  • 从 IDE 成功运行相同的 proc 不会排除服务器 A 上的配置问题吗?
  • ODP.NET 尝试连接服务器 A 时是否会出现 ORA-12154?还是它是执行后出现的错误堆栈的一部分?巨大差距。也许您可以分享完整的错误堆栈。此外,您应该三重检查 ODP.NET 连接字符串。您可能根本没有连接到服务器 A,而是连接到某个测试服务器或服务器 A 上的另一个架构。无论如何,ODP.NET 应该对数据库链接连接没有任何影响。
  • @ChristianShay 它仅在执行引用链接服务器的查询时发生,而不是在连接或执行非链接服务器查询时发生。今天我将尝试获取更多堆栈跟踪
  • 添加了错误堆栈

标签: oracle odp.net dblink tnsnames odp.net-managed


【解决方案1】:

这肯定是服务器 A 和服务器 B 之间的连接(数据库链接)有问题。

服务器 A 的 tnsnames.ora 文件中可能缺少服务器 B 的 TNS 名称,或者服务器 A 在其数据库链接中的 TNS 名称可能有误。

如果这是客户端问题,我可以想象会收到 ORA-12154 错误,但不会收到 ORA-04088:如果您可以进入触发器,显然您与服务器 A 的连接是正常的。

我想说下一步是直接连接到服务器 A 上的数据库并尝试通过数据库链接查询服务器 B 上的数据库中的表。我希望这会失败并出现相同的 ORA-12154 错误。

【讨论】:

  • 从我们的测试来看,情况似乎并非如此。在同一台客户端计算机上,使用 SQL Plus 成功,而 ODP.Net 失败。我已经用更详细的信息更新了问题
【解决方案2】:

根据您的描述,可能是:

  1. 这是您的 ODP.NET 客户端的配置问题。
  2. 此外,客户端可能一开始就没有连接到服务器 A,更不用说连接到服务器 B。

ODP.NET 是一个瘦客户端(不涉及本机代码)这一事实应该排除与 Oracle 客户端二进制文件相关的任何配置:驱动程序是“自包含的”并且完全独立于您的 SQLplus 安装。我的意思是,例如,如果你的机器的 %path% 变量有错误,它会影响数据的配置,而不是涉及的二进制文件。

可能,ODP.NET 可能看不到正确的 ORACLE_HOME 变量(例如 C:\u01\app\client\product\12.1.0\client_1\)。并且可能,配置错误体现在 ODP.NET 无法获取 TNS 名称文件的位置。

我建议:

  1. 尝试查看是否有另一个客户端首先出现在您的路径中,并隐藏您想要的目录。例如:C:\u01\app\client\product\11.2.1\client_1\bin;C:\u01\app\client\product\12.1.0\client_1\bin。在这种情况下,作为快速尝试,将您的 ODP.NET 客户端放在首位。这很可能使您的 ODP.NET 代码拾取指向正确 Oracle_HOME 的指针,然后 TNS 配置将随之而来。对于复杂的设置,请考虑 Oracle 将不同的 ORACLE_HOME 值存储在注册表中,并为您提供除 %path% 中的位置之外的其他方式来选择您想要的。如果有兴趣,请参阅Using Multiple Oracle Homes。当然不要忘记将您的 TNS 名称放在正确的位置,并且此 TNS 应指向服务器 A,而不是服务器 B。
  2. 或者尝试创建一个对正在执行 ODP.NET 代码的 Windows 帐户可见的附加环境变量;这个变量应该被称为TNS_ADMIN 并指向你的Oracle 客户端的管理目录(类似于..product\12.1.0\client_1\network\admin)。通过从 ODP.NET 客户端发出 string tns_admin = Environment.GetEnvironmentVariable("TNS_ADMIN") 来确保您的 ODP.NET 能够看到它。
  3. 如果上述方法不起作用,请尽量避免使用 TNS 内容,在连接字符串中指定所有信息,如下所示:

<connectionStrings> <add name="Server_A" connectionString="SERVER=(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=MyHost)(PORT=MyPort))(CONNECT_DATA=(SERVICE_NAME=MyOracleSID))); uid=myUsername;pwd=myPassword;" /> </connectionStrings>

后一种设置显示在here 或稍有不同的替代设置here

我认为在您的 ODP.NET 配置中嵌入 TNS 信息实际上比任何其他依赖主机操作系统配置的解决方案更干净,因为使您的 ODP.NET 真正可移植(部署到 TEST 或 PRODUCTION 是更改ODP.NET 配置,而不是主机变量)。有关如何关联 TNS 和 ODP.NET 配置的更多示例,请参阅Oracle Managed ODP.NET | Vijay's blog

最后,SQLPlus 正在工作并不令人惊讶,因为它可能会看到不同的环境变量来获取连接标识符或ORACLE_HOME
根本没有 tnsnames.ora 文件这一事实可能表明

【讨论】:

  • 所以我们已经在使用 TNS-less 连接,我们在连接字符串中指定所有内容的方式与您在 #3 中描述的方式相同,除了此实例并且仅在查询链接数据库表
  • 对不起,我收回我说的话。几个问题: 1. 最后,你知道SQLplus是如何连接数据库的吗?是本地实例还是以某种神奇的方式解析名称? 2. SELECT * from dba_db_links 返回什么?您是否有可能使用全局 DBLink(即不是真正的 DBlink)? 3.当然,从sqldeveloper和odp.net连接时,您使用的是同一个用户。例如,在您的 odp.net 客户端中禁用了操作系统身份验证,不是吗?
  • 4.也发送有问题的查询。我最感兴趣的是from table@dblink 部分,以及它与SELECT * from dba_db_links 的结果之间的关系。 5. 从 odp.net 和 sqldeveloper 发出 select user from dual。很明显,它们返回的是同一个用户,但我们想排除由于用户授权而造成的任何差异。
  • 很好的故障排除指导@Antonio,等待 DBLinks 查询的结果。 SQL Plus 连接使用运行命令中的凭据。我可以确认我们使用相同的帐户来运行测试。
  • 数据库链接类型为 PUBLIC,固定用户不是我们正在执行的帐户。在我们的帐户下不允许查询 DBA_DB_LINKS,并且 USER_DB_LINKS 什么也不返回,所以我们必须以管理员身份运行查询
【解决方案3】:

我们的客户端能够在服务器端解决此问题。问题在于 DB Link 的格式。

原始数据库链接:

CREATE PUBLIC DATABASE LINK [LINK_NAME] 
CONNECT TO [USER] 
IDENTIFIED BY [PASSWORD] 
USING [TNS_NAME]

显然在 ODP.NET 上,TNS 名称参考是不够的。一旦客户端切换到完整的连接细节,问题就解决了。

更新的数据库链接:

CREATE PUBLIC DATABASE LINK [LINK_NAME] 
CONNECT TO [USER] 
IDENTIFIED BY [PASSWORD]
USING
'(DESCRIPTION = 
     (SDU=[SDU])
     (ADDRESS_LIST = 
          (ADDRESS = (PROTOCOL = TCP)(HOST = [HOSTNAME])(PORT = [PORT]))
     (CONNECT_DATA = (SID=[SID])
)'

更多细节在这里:

https://docs.oracle.com/cd/B19306_01/server.102/b14200/statements_5005.htm

【讨论】:

  • 在数据库服务器上,TNSNAMES.ORA 中是否有一个名为 TNS_NAME 的别名设置为您在第二个数据库链接示例中使用的值?数据库链接是在数据库服务器中创建的,因此它引用该服务器本地的 TNSNAMES.ORA。它无法访问客户端(ODP.NET 所在的位置)上的 TNSNAMES.ORA。
  • @ChristianShay 一定有吗?因为使用 SQL PLUS 从同一台客户端计算机连接到同一台服务器并运行相同的查询从一开始就成功。我真的不认为这是 DB Link 设置是否“正确”的问题,因为它可以用于各种其他目的,它看起来非常像 ODP.NET 特有的错误或限制。
  • 难以置信,我比以前更加困惑,但感谢您发布答案。显然,这个特性对客户端应该是完全透明的(连接到一个 dblinked 表)。这一定是从服务器的实现冒泡到客户端的错误。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-05-22
  • 2013-02-20
  • 1970-01-01
  • 2011-10-08
相关资源
最近更新 更多