Ответ 1
Я потратил много времени на анализ различий между этими двумя. Вот что я узнал.
-
DirectorySearcher
происходит изSystem.DirectoryServices
именSystem.DirectoryServices
. -
PrincipalSearcher
происходит изSystem.DirectoryServices.AccountManagement
именSystem.DirectoryServices.AccountManagement
, которое построено поверхSystem.DirectoryServices
.PrincipalSearcher
внутренне используетDirectorySearcher
. -
Пространство имен
AccountManagement
(то естьPrincipalSearcher
) было разработано для упрощения управления объектами User, Group и Computer (т.е. Principals). Теоретически, его использование должно быть проще для понимания и создавать меньше строк кода. Хотя в моей практике до сих пор она сильно зависит от того, что вы делаете. -
DirectorySearcher
более низкоуровневый и может иметь дело не только с объектами User, Group и Computer. -
Для общего использования, когда вы работаете с базовыми атрибутами и несколькими объектами,
PrincipalSearcher
приведет к сокращению числа строк кода и ускорению работы. -
Преимущество, похоже, исчезает из-за более сложных задач, которые вы делаете. Например, если вы ожидаете более нескольких сотен результатов, вам придется получить базовый
DirectorySearcher
и установить параметрPageSize
DirectorySearcher ds = search.GetUnderlyingSearcher() as DirectorySearcher; if( ds != null ) ds.PageSize = 1000;
-
DirectorySearcher
может быть значительно быстрее, чемPrincipalSearcher
если вы используетеPropertiesToLoad
. -
DirectorySearcher
и подобные классы могут работать со всеми объектами в AD, тогда какPrincipalSearcher
гораздо более ограничен. Например, вы не можете изменить организационную единицу, используяPrincipalSearcher
и подобные классы.
Вот диаграмма, которую я сделал для анализа с использованием PrincipalSearcher
, DirectorySearcher
без использования PropertiesToLoad
и DirectorySearcher
с использованием PropertiesToLoad
. Все тесты...
- Использовать
PageSize
1000
- Запросите в общей сложности 4 278 пользовательских объектов
- Укажите следующие критерии
-
objectClass=user
-
objectCategory=person
- Не ресурс планирования (т.е.
!msExchResourceMetaData=ResourceType:Room
) - Включено (т.е.
!userAccountControl:1.2.840.113556.1.4.803:=2
)
-
Код для каждого теста
Использование 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);
}
Использование 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);
}
Использование DirectorySearcher
с PropertiesToLoad
То же, что и "Использование DirectorySearcher
но добавьте эту строку
search.PropertiesToLoad.AddRange(new string[] { "canonicalName" });
После
search.PageSize = 1000;