Obtendo todos os relatórios diretos do Active Directory

Eu estou tentando obter todos os relatórios diretos de um usuário através do Active Directory, recursivamente. Então, dado um usuário, eu acabarei com uma lista de todos os usuários que têm essa pessoa como gerente ou que têm uma pessoa como gerente que tem uma pessoa como gerente ... que eventualmente tem o usuário de entrada como gerente.

Minha tentativa atual é bastante lenta:

private static Collection<string> GetDirectReportsInternal(string userDN, out long elapsedTime)
{
    Collection<string> result = new Collection<string>();
    Collection<string> reports = new Collection<string>();

    Stopwatch sw = new Stopwatch();
    sw.Start();

    long allSubElapsed = 0;
    string principalname = string.Empty;

    using (DirectoryEntry directoryEntry = new DirectoryEntry(string.Format("LDAP://{0}",userDN)))
    {
        using (DirectorySearcher ds = new DirectorySearcher(directoryEntry))
        {
            ds.SearchScope = SearchScope.Subtree;
            ds.PropertiesToLoad.Clear();
            ds.PropertiesToLoad.Add("directReports");
            ds.PropertiesToLoad.Add("userPrincipalName");
            ds.PageSize = 10;
            ds.ServerPageTimeLimit = TimeSpan.FromSeconds(2);
            SearchResult sr = ds.FindOne();
            if (sr != null)
            {
                principalname = (string)sr.Properties["userPrincipalName"][0];
                foreach (string s in sr.Properties["directReports"])
                {
                    reports.Add(s);
                }
            }
        }
    }

    if (!string.IsNullOrEmpty(principalname))
    {
        result.Add(principalname);
    }

    foreach (string s in reports)
    {
        long subElapsed = 0;
        Collection<string> subResult = GetDirectReportsInternal(s, out subElapsed);
        allSubElapsed += subElapsed;

        foreach (string s2 in subResult)
        {
        result.Add(s2);
        }
    }



    sw.Stop();
    elapsedTime = sw.ElapsedMilliseconds + allSubElapsed;
    return result;
}

Essencialmente, essa função recebe um nome distinto como entrada (CN = Michael Stum, OU = teste, DC = sub, DC = domínio, DC = com) e, com isso, a chamada para ds.FindOne () é lenta.

Eu achei que é muito mais rápido procurar o userPrincipalName. Meu problema: sr.Properties ["directReports"] é apenas uma lista de strings, e esse é o distinguishedName, que parece lento para procurar.

Eu me pergunto, existe uma maneira rápida de converter entre distinguishedName e userPrincipalName? Ou existe uma maneira mais rápida de procurar um usuário se eu tiver apenas o distinguishedName para trabalhar?

Editar: Graças à resposta! A pesquisa no campo Gerenciador melhorou a função de 90 segundos para 4 segundos. Aqui está o código novo e melhorado, que é mais rápido e mais legível (note que provavelmente existe um bug na funcionalidade elapsedTime, mas o núcleo real da função funciona):

private static Collection<string> GetDirectReportsInternal(string ldapBase, string userDN, out long elapsedTime)
{
    Collection<string> result = new Collection<string>();

    Stopwatch sw = new Stopwatch();
    sw.Start();
    string principalname = string.Empty;

    using (DirectoryEntry directoryEntry = new DirectoryEntry(ldapBase))
    {
        using (DirectorySearcher ds = new DirectorySearcher(directoryEntry))
        {
            ds.SearchScope = SearchScope.Subtree;
            ds.PropertiesToLoad.Clear();
            ds.PropertiesToLoad.Add("userPrincipalName");
            ds.PropertiesToLoad.Add("distinguishedName");
            ds.PageSize = 10;
            ds.ServerPageTimeLimit = TimeSpan.FromSeconds(2);
            ds.Filter = string.Format("(&(objectCategory=user)(manager={0}))",userDN);

            using (SearchResultCollection src = ds.FindAll())
            {
                Collection<string> tmp = null;
                long subElapsed = 0;
                foreach (SearchResult sr in src)
                {
                    result.Add((string)sr.Properties["userPrincipalName"][0]);
                    tmp = GetDirectReportsInternal(ldapBase, (string)sr.Properties["distinguishedName"][0], out subElapsed);
                    foreach (string s in tmp)
                    {
                    result.Add(s);
                    }
                }
            }
          }
        }
    sw.Stop();
    elapsedTime = sw.ElapsedMilliseconds;
    return result;
}

questionAnswers(1)

yourAnswerToTheQuestion