2008-10-10 5 views
11

Я пытаюсь получить все прямые отчеты пользователя через Active Directory, рекурсивно. Таким образом, для пользователя я получаю список всех пользователей, у которых есть это лицо в качестве менеджера, или у кого есть лицо в качестве менеджера, у которого есть лицо в качестве менеджера ... у которого в конечном итоге есть пользователь ввода в качестве менеджера.Получение всех прямых отчетов из Active Directory

Моя текущая попытка довольно медленно:

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; 
} 

По существу, эта функция принимает различающееся имя в качестве входных данных (CN = Michael Штум, OU = тест, DC = юг, DC = домен, DC = COM) , и при этом вызов ds.FindOne() выполняется медленно.

Я обнаружил, что поиск слова userPrincipalName намного быстрее. Моя проблема: sr.Properties ["directReports"] - это всего лишь список строк, и это выдающееся имя, которое кажется медленным для поиска.

Интересно, есть ли быстрый способ конвертировать между выдающимся именем и userPrincipalName? Или существует более быстрый способ поиска пользователя, если у меня есть только имя выдающегося?

Редактировать: Спасибо! Поиск в поле «Менеджер» улучшил функцию с 90 секунд до 4 секунд. Вот новый и улучшенный код, который быстрее и более удобным для чтения (обратите внимание, что, скорее всего, ошибка в функциональности ElapsedTime, но фактическое ядро ​​функции работы):

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; 
} 
+0

Вы можете получить дополнительную скорость, выбирая DirectoryEntry и DirectorySearcher из рекурсии. Они не меняются между ними, не так ли? – Tomalak 2008-10-10 09:35:19

+0

Больше нет. То, что я не сказал: я использую это в среде Sharepoint, где вызов завернут в вызов SPSecurity.RunWithElevatedPrivileges, что означает, что ref Параметры невозможны, и я не уверен, что передача его как нормального параметра работает (странно Sharepoint Security) – 2008-10-10 09:59:31

ответ

10

Во-первых, установка Scope для «поддерева» не требуется, если у вас уже есть DN, которое вы ищете.

Также, как найти все объекты, чье «управляющее» свойство является тем, кого вы ищете, а затем итерации. Это должно быть быстрее, чем наоборот.

(&(objectCategory=user)(manager=<user-dn-here>)) 

EDIT: важно следующее, но только упоминается в комментариях к этому ответу до сих пор:

Когда строка фильтра построена, как указано выше, существует риск нарушения его с символами, которые действительны для DN, но имеют особое значение в фильтре. Эти must be escaped:

* as \2a 
( as \28 
) as \29 
\ as \5c 
NUL as \00 
/ as \2f 

// Arbitrary binary data can be represented using the same scheme. 

EDIT: Установка SearchRoot в DN объекта, а SearchScope к Base также является быстрым способом вытащить один объект из AD.

Смежные вопросы