Próbuję uzyskać wszystkie bezpośrednie raporty użytkownika za pośrednictwem Active Directory, rekursywnie. Tak więc, biorąc pod uwagę użytkownika, otrzymam listę wszystkich użytkowników, którzy mają tę osobę jako menedżera lub mają osobę jako menedżera, który ma osobę jako menedżera ... który ostatecznie ma użytkownika wejściowego jako menedżera.Uzyskiwanie wszystkich bezpośrednich raportów z Active Directory
Moja obecna próba jest raczej powolny:
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;
}
Zasadniczo funkcja ta przyjmuje nazwę wyróżniającą jako wejście (CN = Michael Stum, OU = test, DC = sub, DC = domena, DC = com) , a wraz z tym wywołanie ds.FindOne() jest powolne.
Stwierdziłem, że wyszukiwanie nazwy użytkownikaPrincipalName jest o wiele szybsze. Mój problem: sr.Properties ["directReports"] jest po prostu listą ciągów znaków, a jest to nazwa wyróżniająca, która wydaje się wolna w wyszukiwaniu.
Zastanawiam się, czy istnieje szybki sposób na konwersję pomiędzy distinguishedName a userPrincipalName? Czy istnieje szybszy sposób wyszukiwania użytkownika, jeśli mam tylko nazwę wyróżniającą do pracy?
Edytuj: Dzięki odpowiedzi! Przeszukanie pola menedżera poprawiło funkcję z 90 sekund do 4 sekund. Oto nowy i ulepszony kod, który jest szybszy i bardziej czytelne (należy pamiętać, że jest najprawdopodobniej błąd w funkcjonalności ElapsedTime, ale rzeczywista rdzeń funkcji działa):
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;
}
Możesz uzyskać dodatkową prędkość, wyłączając program DirectoryEntry i DirectorySearcher z rekursji. Nie zmieniają się między nimi, prawda? – Tomalak
Już nie. Czego nie powiedziałem: Używam tego w środowisku SharePoint, w którym wywołanie jest zawijane w wywołaniu SPSecurity.RunWithElevatedPrivileges, co oznacza, że parametry ref nie są możliwe, i nie jestem pewien, czy przekazanie go jako normalnego parametru działa (dziwne Sharepoint Security) –
Myślę, że to powinno działać. Obiekty są zawsze przekazywane jako ref, AFAIK. Zobacz: http://stackoverflow.com/questions/186891/why-use-ref-keyword-when-passing-an-object – Tomalak