Je vois des exemples Active Directory qui utilisent PrincipalSearcher
et d'autres exemples qui font la même chose mais utilisent DirectorySearcher
. Quelle est la différence entre ces deux exemples?
Exemple utilisant PrincipalSearcher
PrincipalContext context = new PrincipalContext(ContextType.Domain);
PrincipalSearcher search = new PrincipalSearcher(new UserPrincipal(context));
foreach( UserPrincipal user in search.FindAll() )
{
if( null != user )
Console.WriteLine(user.DistinguishedName);
}
Exemple utilisant DirectorySearcher
DirectorySearcher search = new DirectorySearcher("(&(objectClass=user)(objectCategory=person))");
search.PageSize = 1000;
foreach( SearchResult result in search.FindAll() )
{
DirectoryEntry user = result.GetDirectoryEntry();
if( null != user )
Console.WriteLine(user.Properties["distinguishedName"].Value.ToString());
}
J'ai passé beaucoup de temps à analyser les différences entre ces deux. Voici ce que j'ai appris.
DirectorySearcher
provient du System.DirectoryServices
espace de noms.
PrincipalSearcher
provient du System.DirectoryServices.AccountManagement
espace de noms, qui est construit au-dessus de System.DirectoryServices
. PrincipalSearcher
utilise en interne DirectorySearcher
.
L'espace de noms AccountManagement
(c'est-à-dire PrincipalSearcher
) a été conçu pour simplifier la gestion des objets Utilisateur, Groupe et Ordinateur (c'est-à-dire les principaux). En théorie, son utilisation devrait être plus facile à comprendre et produire moins de lignes de code. Bien que dans ma pratique jusqu'à présent, cela semble dépendre fortement de ce que vous faites.
DirectorySearcher
est plus bas niveau et peut traiter plus que des objets Utilisateur, Groupe et Ordinateur.
Pour une utilisation générale, lorsque vous travaillez avec des attributs de base et seulement quelques objets, PrincipalSearcher
entraînera moins de lignes de code et un temps d'exécution plus rapide.
L'avantage semble disparaître à mesure que les tâches que vous effectuez deviennent plus avancées. Par exemple, si vous attendez plus de quelques centaines de résultats, vous devrez obtenir le DirectorySearcher
sous-jacent et définir le PageSize
DirectorySearcher ds = search.GetUnderlyingSearcher() as DirectorySearcher;
if( ds != null )
ds.PageSize = 1000;
DirectorySearcher
peut être beaucoup plus rapide que PrincipalSearcher
si vous utilisez PropertiesToLoad
.
DirectorySearcher
et les classes similaires peuvent fonctionner avec tous les objets dans AD, tandis que PrincipalSearcher
est beaucoup plus limité. Par exemple, vous ne pouvez pas modifier une unité d'organisation à l'aide de PrincipalSearcher
et de classes similaires.
Voici un graphique que j'ai fait pour analyser en utilisant PrincipalSearcher
, DirectorySearcher
sans utiliser PropertiesToLoad
, et DirectorySearcher
avec en utilisant PropertiesToLoad
. Tous les tests ...
PageSize
de 1000
objectClass=user
objectCategory=person
!msExchResourceMetaData=ResourceType:Room
)!userAccountControl:1.2.840.113556.1.4.803:=2
)en utilisant PrincipalSearcher
[DirectoryRdnPrefix("CN")]
[DirectoryObjectClass("Person")]
public class UserPrincipalEx: UserPrincipal
{
private AdvancedFiltersEx _advancedFilters;
public UserPrincipalEx( PrincipalContext context ): base(context)
{
this.ExtensionSet("objectCategory","User");
}
public new AdvancedFiltersEx AdvancedSearchFilter
{
get {
if( null == _advancedFilters )
_advancedFilters = new AdvancedFiltersEx(this);
return _advancedFilters;
}
}
}
public class AdvancedFiltersEx: AdvancedFilters
{
public AdvancedFiltersEx( Principal principal ):
base(principal) { }
public void Person()
{
this.AdvancedFilterSet("objectCategory", "person", typeof(string), MatchType.Equals);
this.AdvancedFilterSet("msExchResourceMetaData", "ResourceType:Room", typeof(string), MatchType.NotEquals);
}
}
//...
for( int i = 0; i < 10; i++ )
{
uint count = 0;
Stopwatch timer = Stopwatch.StartNew();
PrincipalContext context = new PrincipalContext(ContextType.Domain);
UserPrincipalEx filter = new UserPrincipalEx(context);
filter.Enabled = true;
filter.AdvancedSearchFilter.Person();
PrincipalSearcher search = new PrincipalSearcher(filter);
DirectorySearcher ds = search.GetUnderlyingSearcher() as DirectorySearcher;
if( ds != null )
ds.PageSize = 1000;
foreach( UserPrincipalEx result in search.FindAll() )
{
string canonicalName = result.CanonicalName;
count++;
}
timer.Stop();
Console.WriteLine("{0}, {1} ms", count, timer.ElapsedMilliseconds);
}
en utilisant DirectorySearcher
for( int i = 0; i < 10; i++ )
{
uint count = 0;
string queryString = "(&(objectClass=user)(objectCategory=person)(!msExchResourceMetaData=ResourceType:Room)(!userAccountControl:1.2.840.113556.1.4.803:=2))";
Stopwatch timer = Stopwatch.StartNew();
DirectoryEntry entry = new DirectoryEntry();
DirectorySearcher search = new DirectorySearcher(entry,queryString);
search.PageSize = 1000;
foreach( SearchResult result in search.FindAll() )
{
DirectoryEntry user = result.GetDirectoryEntry();
if( user != null )
{
user.RefreshCache(new string[]{"canonicalName"});
string canonicalName = user.Properties["canonicalName"].Value.ToString();
count++;
}
}
timer.Stop();
Console.WriteLine("{0}, {1} ms", count, timer.ElapsedMilliseconds);
}
en utilisant DirectorySearcher
avec PropertiesToLoad
Identique à "Utilisation de DirectorySearcher
mais ajoutez cette ligne
search.PropertiesToLoad.AddRange(new string[] { "canonicalName" });
Après
search.PageSize = 1000;
PrincipalSearcher
est utilisé pour interroger le répertoire des groupes ou des utilisateurs. DirectorySearcher
est utilisé pour interroger toutes sortes d'objets.
J'ai utilisé DirectorySearcher
pour obtenir des groupes avant de découvrir PrincipalSearcher
donc quand j'ai remplacé le premier par le second, la vitesse de mon programme s'est améliorée (peut-être que c'était juste PrincipalSearcher
qui a créé une meilleure requête pour moi. Pour ce qui m'importe, PrincipalSearcher
était juste plus facile à utiliser et plus adapté à la tâche d'obtenir des pricipaux.
DirectorySearcher
d'autre part est plus général car il peut obtenir d'autres types d'objets. C'est pourquoi il ne peut pas être fortement tapé comme mentionné dans les commentaires. PrincipalSearcher
concerne les principaux donc il aura des objets fortement typés qui se rapportent aux principaux, et c'est pourquoi vous n'avez pas besoin de le dire pour vous obtenir un objet de type utilisateur ou groupe, cela sera implicite par les classes principales que vous utilisez.
DirectorySearcher est de loin plus rapide. L'exemple de @DrewChapin peut être pris encore plus loin. D'après mes tests, environ 10 fois plus loin/plus vite. J'ai pu extraire 721 ordinateurs CN en 3,8 secondes avec son code. Assez vite. Avec mes changements, je l'ai fait en 0,38 seconde. Selon ce que vous faites, cela pourrait être énorme. Je l'ai utilisé dans une recherche de compte prédictive (commencez à taper le nom et une zone de liste déroulante se remplit. Un système très court. )
DirectoryEntry entry = new DirectoryEntry();
DirectorySearcher search = new DirectorySearcher(entry,queryString);
search.PageSize = 1000;
// *** Added following line
search.PropertiesToLoad.AddRange(new string[] { "canonicalName" });
foreach( SearchResult result in search.FindAll() )
{
//DirectoryEntry user = result.GetDirectoryEntry();
// *** Work directly with result instead of user
if( result != null )
{
//user.RefreshCache(new string[]{"canonicalName"});
// *** Following line modified
string canonicalName = result.Properties["canonicalName"][0].ToString();
count++;
}
}