4

我们的应用程序使用托管的 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]
4

3 回答 3

1

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

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

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

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

于 2016-12-16T21:55:25.193 回答
0

根据您的描述,可能性是:

  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% 中的位置以外的其他方式来选择您想要的值。如果有兴趣,请参阅使用多个 Oracle 主目录。当然不要忘记将你的 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>

后一种设置显示在此处或稍有不同的替代设置在这里

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

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

于 2016-12-20T08:57:29.663 回答
0

我们的客户端能够在服务器端解决此问题。问题是 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

于 2017-01-24T14:20:05.943 回答