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

2024-03-19

我的机器上安装了 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 别名,它会正常工作并告诉我带有真实数据库名称的连接标识符。

如果我随后更改连接字符串以使用真实的数据库名称而不是别名,然后再次尝试使用托管库,它将顺利执行。

我一直在阅读并发现this https://stackoverflow.com/a/20050462/3736051答案说托管驱动程序依赖 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<string> 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;
  }
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

ODP.NET 托管库确实可以解析别名,但 32 位库可以 的相关文章

随机推荐