ODP.NET托管库确实解析别名,但32位库确实如此

我的盒子上安装了32位驱动程序(它们是由一些DBA安装和配置的)

我写了一个简单的脚本来测试驱动程序,几乎如下

using (DataTable table = new DataTable()) { using (OracleConnection connection = new OracleConnection()) { connection.ConnectionString = "Data Source=alias;User id=user;Password=password"; connection.Open(); using (OracleCommand command = connection.CreateCommand()) { command.CommandType = CommandType.Text; command.CommandText = "SELECT * FROM DUAL"; table.Load(command.ExecuteReader()); } } } 

当我使用32位Oracle.DataAccess.dll将此应用程序编译为32位时,它会毫无障碍地执行。

但是,如果我使用Oracle.ManagedDataAccess.dll将应用程序编译为AnyCPU,则会收到ORA-12154(无法解析指定的连接标识符)错误。

如果我tnsping别名,它可以正常工作,并告诉我连接标识符与真实的数据库名称。

如果我然后更改连接字符串以使用真实数据库名称而不是别名,然后再次尝试使用托管库,它会毫不费力地执行。

我一直在阅读并发现这个答案,说托管驱动程序依赖于tnsnames.ora文件来解析别名,但我依赖于sqlnet.ora和ldap.ora中定义的LDAP服务器。

当我tnsping时,它说它使用sqlnet.ora来解析名称。

那么管理的司机怎么办不起作用呢?

愿这个解决方法适合你。 您可以自己查询LDAP服务器,并将完整的连接字符串放入代码中。

您可以使用以下代码从LDAP解析连接字符串:

 using (OracleConnection connection = new OracleConnection()) { connection.ConnectionString = "Data Source=" + ResolveServiceNameLdap("alias") + ";User id=user;Password=password"; connection.Open(); } 

  private static string ResolveServiceNameLdap(string serviceName) { string tnsAdminPath = Path.Combine(@"C:\oracle\network", "ldap.ora"); // or something more advanced... // ldap.ora can contain many LDAP servers IEnumerable directoryServers = null; if ( File.Exists(tnsAdminPath) ) { string defaultAdminContext = string.Empty; using ( var sr = File.OpenText(tnsAdminPath) ) { string line; while ( ( line = sr.ReadLine() ) != null ) { // Ignore comments or empty lines if ( line.StartsWith("#") || line == string.Empty ) continue; // If line starts with DEFAULT_ADMIN_CONTEXT then get its value if ( line.StartsWith("DEFAULT_ADMIN_CONTEXT") ) defaultAdminContext = line.Substring(line.IndexOf('=') + 1).Trim(new[] { '\"', ' ' }); // If line starts with DIRECTORY_SERVERS then get its value if ( line.StartsWith("DIRECTORY_SERVERS") ) { string[] serversPorts = line.Substring(line.IndexOf('=') + 1).Trim(new[] { '(', ')', ' ' }).Split(','); directoryServers = serversPorts.SelectMany(x => { // If the server includes multiple port numbers, this needs to be handled string[] serverPorts = x.Split(':'); if ( serverPorts.Count() > 1 ) return serverPorts.Skip(1).Select(y => string.Format("{0}:{1}", serverPorts.First(), y)); return new[] { x }; }); } } } // Iterate through each LDAP server, and try to connect foreach ( string directoryServer in directoryServers ) { // Try to connect to LDAP server with using default admin contact try { var directoryEntry = new DirectoryEntry("LDAP://" + directoryServer + "/" + defaultAdminContext, null, null, AuthenticationTypes.Anonymous); var directorySearcher = new DirectorySearcher(directoryEntry, "(&(objectclass=orclNetService)(cn=" + serviceName + "))", new[] { "orclnetdescstring" }, SearchScope.Subtree); SearchResult searchResult = directorySearcher.FindOne(); var value = searchResult.Properties["orclnetdescstring"][0] as byte[]; if ( value != null ) connectionString = Encoding.Default.GetString(value); // If the connection was successful, then not necessary to try other LDAP servers break; } catch { // If the connection to LDAP server not successful, try to connect to the next LDAP server continue; } } // If casting was not successful, or not found any TNS value, then result is an error if ( string.IsNullOrEmpty(connectionString) ) throw new Exception("TNS value not found in LDAP"); } else { // If ldap.ora doesn't exist, then throw error throw new FileNotFoundException("ldap.ora not found"); } return connectionString; } 

ODP.NET托管驱动程序依赖于在配置文件中设置的TNS_ADMIN值来查找TNSNAMES.ORA文件。 如果您不想设置该值,可以将TNSNAMES.ORA文件包含在可执行文件的工作目录中,或者直接在配置文件中创建TNS别名。

编辑:如果您依赖SQLNET.ORA和LDAP.ORA,您也可以将它们复制到工作目录中,或者在配置文件中设置LDAP参数。 有关LDAP可用的配置文件参数,请参阅ODP.NET doc。