Estoy tratando de obtener todos los informes directos de un Usuario a través de Active Directory, recursivamente. Por lo tanto, dado un usuario, terminaré con una lista de todos los usuarios que tienen a esta persona como gerente o que tienen una persona como gerente que tiene una persona como gerente ... que finalmente tiene el usuario de entrada como administrador.Obteniendo todos los Informes Directos desde el Directorio Activo
Mi intento actual es bastante lento:
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;
}
En esencia, esta función toma un nombre distinguido como entrada (CN = Michael Stum, OU = prueba, DC = sub DC = dominio, DC = com) , y con eso, la llamada a ds.FindOne() es lenta.
Descubrí que es mucho más rápido buscar userPrincipalName. Mi problema: sr.Properties ["directReports"] es solo una lista de cadenas, y ese es el distinguishedName, que parece lento de buscar.
Me pregunto, ¿hay una manera rápida de convertir entre distinguishedName y userPrincipalName? ¿O hay una manera más rápida de buscar un usuario si solo tengo el distinguishedName para trabajar?
Editar: ¡Gracias por la respuesta! La búsqueda en el Manager-Field mejoró la función de 90 Segundos a 4 Segundos. Esta es la nueva y mejorada código, que es más rápido y más fácil de leer (tenga en cuenta que no es muy probable que un error en la funcionalidad elapsedTime, pero el núcleo real de la función trabaja):
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;
}
Puede obtener velocidad adicional si toma DirectoryEntry y DirectorySearcher fuera de la recursión. No cambian en el medio, ¿verdad? – Tomalak
ya no. Lo que no dije: estoy usando esto en un entorno Sharepoint donde la llamada está envuelta en una llamada SPSecurity.RunWithElevatedPrivileges, lo que significa que los parámetros ref no son posibles, y no estoy seguro si pasarlo como un parámetro normal funciona (raro Sharepoint Security) –
Creo que debería funcionar. Los objetos siempre pasan como ref, AFAIK. Ver: http://stackoverflow.com/questions/186891/why-use-ref-keyword-when-passing-an-object – Tomalak