问题
Further to my previous Question, which I managed to answer myself with help from the Oracle forums, I now have another issue which follows on from the earlier one (provided for background).
I wish to query LDAP directly from my C# code to perform an LDAP lookup of an Oracle TNS hostname in order to get the connection string. This is normally stored in tnsnames.ora, and my organisation uses LDAP (via ldap.ora) to resolve hostnames from an LDAP server using Active Directory.
However, I am using ODP.NET, Managed Driver Beta (Oracle.ManagedDataAccess.dll) in my C# application which doesn't support LDAP as mentioned in the release notes pointed to by the Oracle forum reply I mentioned earlier. This is why I wish to query LDAP directly from C#.
I found a way to do this here using DirectoryEntry
and DirectorySearcher
, but I have no idea what to put as the parameters to DirectorySearcher
. I have access to ldap.ora which is in the following format:
# LDAP.ORA Configuration
# Generated by Oracle configuration tools.
DEFAULT_ADMIN_CONTEXT = "dc=xx,dc=mycompany,dc=com"
DIRECTORY_SERVERS = (ldap_server1.mycompany.com:389:636,ldap_server2.mycompany.com:389:636, ...) DIRECTORY_SERVER_TYPE = OID
But, how do I map this to setting up the LDAP query in my C# code?
回答1:
Judging by what I found in Oracle Database Name Resolution with OpenLDAP, the code should look something like this:
string directoryServer = "ldap_server1.mycompany.com:389";
string defaultAdminContext = "dc=xx,dc=mycompany,dc=com";
string oracleHostEntryPath = string.Format("LDAP://{0}/cn=OracleContext,{1}", directoryServer, defaultAdminContext);
var directoryEntry = new DirectoryEntry(oracleHostEntryPath) {AuthenticationType = AuthenticationTypes.None};
var directorySearcher = new DirectorySearcher(directoryEntry, "(&(objectclass=orclNetService)(cn=ABCDEFG1))", new[] { "orclnetdescstring" }, SearchScope.Subtree);
string oracleNetDescription = Encoding.Default.GetString(des.FindOne().Properties["orclnetdescstring"][0] as byte[]);
回答2:
Further to my second comment in the accepted Answer, this is the code for performing an LDAP lookup which improves the original version I found here. And it also handles server lists in the ldap.ora file that includes multiple delimited port numbers.
private static string ResolveServiceNameLdap(string serviceName)
{
string tnsAdminPath = Path.Combine(@"C:\Apps\oracle\network\admin", "ldap.ora");
string connectionString = string.Empty;
// 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 commetns
if (line.StartsWith("#"))
{
continue;
}
// Ignore empty lines
if (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 message
if (string.IsNullOrEmpty(connectionString))
{
connectionString = "TNS value not found in LDAP";
}
}
else
{
// If ldap.ora doesn't exist, then return error message
connectionString = "ldap.ora not found";
}
return connectionString;
}
回答3:
To get you started, try:
using System.DirectoryServices;
...
DirectoryEntry de = new DirectoryEntry();
de.Username = "username@mysite.com";
de.Password = "password";
de.Path = "LDAP://DC=mysite,DC=com";
de.AuthenticationType = AuthenticationTypes.Secure;
DirectorySearcher des = new DirectorySearcher(de);
Now you should be able to hit ldap using the DirectorySearcher (filter, findone, etc) described here
回答4:
Here a more complete sample based on the other answers:
using System;
using System.Data;
using System.DirectoryServices;
using System.Text;
using Oracle.ManagedDataAccess.Client;
namespace BAT
{
internal class Program
{
private static void Main(string[] args)
{
string directoryServer = "myServer:389";
string defaultAdminContext = "cn=OracleContext,dc=world";
string serviceName = "MYDB";
string userId = "MYUSER";
string password = "MYPW";
using (IDbConnection connection = GetConnection(directoryServer, defaultAdminContext, serviceName, userId,
password))
{
connection.Open();
connection.Close();
}
}
private static IDbConnection GetConnection(string directoryServer, string defaultAdminContext,
string serviceName, string userId, string password)
{
string descriptor = ConnectionDescriptor(directoryServer, defaultAdminContext, serviceName);
// Connect to Oracle
string connectionString = $"user id={userId};password={password};data source={descriptor}";
OracleConnection con = new OracleConnection(connectionString);
return con;
}
private static string ConnectionDescriptor(string directoryServer, string defaultAdminContext,
string serviceName)
{
string ldapAdress = $"LDAP://{directoryServer}/{defaultAdminContext}";
string query = $"(&(objectclass=orclNetService)(cn={serviceName}))";
string orclnetdescstring = "orclnetdescstring";
DirectoryEntry directoryEntry = new DirectoryEntry(ldapAdress, null, null, AuthenticationTypes.Anonymous);
DirectorySearcher directorySearcher = new DirectorySearcher(directoryEntry, query, new[] { orclnetdescstring },
SearchScope.Subtree);
SearchResult searchResult = directorySearcher.FindOne();
byte[] value = searchResult.Properties[orclnetdescstring][0] as byte[];
if (value != null)
{
string descriptor = Encoding.Default.GetString(value);
return descriptor;
}
throw new Exception("Error querying LDAP");
}
}
}
来源:https://stackoverflow.com/questions/14566632/how-do-i-query-ldap-from-c-sharp-to-resolve-oracle-tns-hostname-while-using-mana