tobinmarch
asked on
Get all AD users (ADODB versus DirectorySearcher) performance issue.
I have 2 serprate functions, One uses an ADODB connection to Active directory and the other is using a DirectorySearcher object to get a "list" of all the user records in our domain.
The intent of this code it to get all our users telephone information from the Cisco Call Manager (LDAP) and import it into Active Directory (Win 2003).
The older method using ADODB is much quicker than the DirectorySearcher method, and the application rusn fine with it enabled. I would like to use the DirectorySearcher method as it doesnt require me to distribute any extra dll's with my application.
What is wrong with this and how could it be optimized to run more efficiently?
We have 2000+ users in AD. The ADODB method takes aprox. 10 sec to populate the array, while the DirectorySearcher method takes over 60sec.
The intent of this code it to get all our users telephone information from the Cisco Call Manager (LDAP) and import it into Active Directory (Win 2003).
The older method using ADODB is much quicker than the DirectorySearcher method, and the application rusn fine with it enabled. I would like to use the DirectorySearcher method as it doesnt require me to distribute any extra dll's with my application.
What is wrong with this and how could it be optimized to run more efficiently?
We have 2000+ users in AD. The ADODB method takes aprox. 10 sec to populate the array, while the DirectorySearcher method takes over 60sec.
Imports System
Imports System.IO
Imports System.DirectoryServices
Imports System.DirectoryServices.Protocols
Imports System.Net
Imports System.Text
Imports System.Runtime.InteropServices.COMException
Imports ADODB 'added via reference: C:\Program Files\Microsoft.NET\Primary Interop Assemblies\adodb.dll
Module modMain
Sub Main()
getADUsers_ADODB() '~10sec
getADUsers_NEW() 'long time
End Main
Function getADUsers_ADODB()
Dim arrADUsers As New ArrayList
Try
Dim oConn As New ADODB.Connection
Dim oRS As New ADODB.Recordset
Dim oComm As New ADODB.Command
'Dim strComputerName As String = Nothing
Dim strDomainName As String = "saws.org"
' set connection properties
With oConn
.Provider = "ADsDSOObject"
.Open("Active Directory Provider")
End With
' set command properties
With oComm
.ActiveConnection = oConn
.CommandText = "<LDAP://DC=domain,DC=org>;(&(ObjectClass=user)(!ObjectClass=computer)(!description=Built-in*));sAMAccountName,employeeID,telephoneNumber,mail,distinguishedName"
End With
' open recordset
oRS = oComm.Execute
If Not oRS.EOF Then
oRS.MoveFirst()
Do Until oRS.EOF
Dim arrADUser As New ArrayList
If Not oRS.Fields(0).Value.ToString = "" Then
arrADUser.Add(oRS.Fields(0).Value)
If Not oRS.Fields(1).Value.ToString = "" Then
arrADUser.Add(oRS.Fields(1).Value)
Else
arrADUser.Add("null")
End If
If Not oRS.Fields(2).Value.ToString = "" Then
arrADUser.Add(oRS.Fields(2).Value)
Else
arrADUser.Add("null")
End If
If Not oRS.Fields(3).Value.ToString = "" Then
arrADUser.Add(oRS.Fields(3).Value)
Else
arrADUser.Add("null")
End If
If Not oRS.Fields(4).Value.ToString = "" Then
arrADUser.Add(oRS.Fields(4).Value)
Else
arrADUser.Add("null")
End If
End If
arrADUsers.Add(arrADUser)
oRS.MoveNext()
Loop
End If
' clean up objects
oRS.Close()
oConn.Close()
oRS = Nothing
oComm = Nothing
oConn = Nothing
Catch ex As Exception
Console.WriteLine()
Console.WriteLine(ex.Message)
Console.WriteLine()
Console.WriteLine(ex.ToString)
End Try
Return arrADUsers
End Function
Function getADUsers_NEW()
Dim arrADUsers As New ArrayList
Try
Dim de As DirectoryEntry = getDirectoryEntry()
Dim ds As New DirectorySearcher(de)
Dim filter As New StringBuilder
filter.Append("(&(objectCategory=Person)(objectClass=user))")
ds.Filter = filter.ToString
ds.SearchScope = DirectoryServices.SearchScope.Subtree
Dim results As SearchResultCollection = ds.FindAll
Console.WriteLine("User COUNT: {0}", results.Count)
Console.WriteLine()
For Each result As SearchResult In results
Dim dey As DirectoryEntry = getDirectoryEntry(result.Path)
Console.WriteLine("User : {0}", dey.Properties("sAMAccountName").Value)
Console.WriteLine("Phone : {0}", dey.Properties("telephoneNumber").Value)
Console.WriteLine()
dey.Close()
Next
de.Close()
Catch ex As Exception
Console.WriteLine()
Console.WriteLine(ex.Message)
Console.WriteLine()
Console.WriteLine(ex.ToString)
End Try
Return arrADUsers
End Function
Function getDirectoryEntry(Optional ByVal argPath As String = Nothing) As DirectoryEntry
Dim de As New DirectoryEntry
If argPath Is Nothing Then
de.Path = "LDAP://DC=domain,DC=org"
Else
de.Path = argPath
End If
de.Username = "domain\username"
de.Password = "password"
Return de
End Function
End Module
Okay, I've checked and I am as sure as I can be.
I expect you'll find that this bit below is very fast:
Dim results As SearchResultCollection = ds.FindAll
I know I did when I created a C# version.
However, in mine I'm using FindAll to find a small collection of results (5 to 10 at the most) so the overhead is much lower on retrieving the other attributes when I'm returning and handling the results.
Moving onto the next post while I play with the "make it faster" bit :)
Chris
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
I have tested the above response from Chris-Dent to my post and you are correct this is is much faster.
Before I accept your response answer me this:
By adding these lines;
ds.PropertiesToLoad.Add("s AMAccountN ame")
ds.PropertiesToLoad.Add("t elephoneNu mber")
are you specifying that these are the only LDAP properties that the SearchResultCollection should include?
Before I accept your response answer me this:
By adding these lines;
ds.PropertiesToLoad.Add("s
ds.PropertiesToLoad.Add("t
are you specifying that these are the only LDAP properties that the SearchResultCollection should include?
The search results collection, as far as I know, doesn't include any explicit values at all by default.
With the PropertiesToLoad specified it will grab those values as well (or populate a null field), it works in much the same way as you must define the fields to retrieve using the ADO query.
You should still find that you can use .Path to access the distinguishedName of the object without further work (if you need to access the object as a DirectoryEntry).
Chris
ASKER
Thanks for that info. I do need the DN of the entry for further processing.
Appreciate all your help.
Appreciate all your help.
Glad I could help :)
Chris
I suspect it's because of this:
Dim dey As DirectoryEntry = getDirectoryEntry(result.P
I'd have to test it to be sure, but that looks to me exactly like you're connecting to the user account then retrieving values, whereas in the ADO query you're just accessing an existing record set.
Chris