Search code examples
vb.netldapldap-queryprincipalcontext

Using PrincipalContext to search LDAP (Active Directory) for all usernames, and their information


I'm having problems with this code... apparently, it works, but is too slow for me, does anyone have any ideas? It slows down when I try to get access the following

Dim u As UserPrincipal = UserPrincipal.FindByIdentity(ctx, p.SamAccountName)

Again, the following code works just fine, but it's slow as crap. If I take out the above, piece of code and just search for p.SamAccountName it's done within 1 second, so I'm sure I'm doing something incorrect.

Dim sw As New Stopwatch

Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load

    sw.Start()

    DataGridView1.ColumnCount = 3
    DataGridView1.Columns(0).Name = "Account Name"
    DataGridView1.Columns(1).Name = "First Name"
    DataGridView1.Columns(2).Name = "Last Name"

    Dim ctx = New PrincipalContext(ContextType.Domain, "JOI", DomainName)
    Dim userPrin As New UserPrincipal(ctx)
    userPrin.Name = "*"
    Dim searcher = New System.DirectoryServices.AccountManagement.PrincipalSearcher()
    searcher.QueryFilter = userPrin
    Dim results = searcher.FindAll()

    For Each p As Principal In results
        Dim u As UserPrincipal = UserPrincipal.FindByIdentity(ctx, p.SamAccountName)
        Dim row As String() = New String() {u.SamAccountName, u.GivenName, u.Surname}
        DataGridView1.Rows.Add(row)
    Next

    sw.Stop()
    MessageBox.Show("Finished in :" & sw.Elapsed.Duration.Seconds & " seconds")

End Sub

Private Shared Function DomainName() As String

    Dim objRootDSE As New DirectoryEntry("LDAP://RootDSE")
    DomainName = objRootDSE.Properties("defaultNamingContext")(0)

End Function

Solution

  • You already have access to the UserPrincipal in your result from the search and then you are performing another new search for each item in the result. That is exactly what is slowing down your function.

    I usually filter the filter (LINQ) and only take result items that are of UserPrincipal type. That way it's just one pass through search

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    
        sw.Start()
    
        DataGridView1.ColumnCount = 3
        DataGridView1.Columns(0).Name = "Account Name"
        DataGridView1.Columns(1).Name = "First Name"
        DataGridView1.Columns(2).Name = "Last Name"
    
        Using context As PrincipalContext = New PrincipalContext(ContextType.Domain, "JOI", DomainName)
            Using userPrin As UserPrincipal = New UserPrincipal(context)
                userPrin.Name = "*"
                Using searcher As PrincipalSearcher = New PrincipalSearcher(userPrin)
    
                    Dim results = searcher _
                        .FindAll() _
                        .OfType(Of UserPrincipal)()
    
                    For Each p As UserPrincipal In results
                        Dim row As String() = New String() {p.SamAccountName, p.GivenName, p.Surname}
                        DataGridView1.Rows.Add(row)
                    Next
                End Using
            End Using
        End Using
    
        sw.Stop()
        MessageBox.Show("Finished in :" & sw.Elapsed.Duration.Seconds & " seconds")
    
    End Sub