我花了很多时间分析这两者之间的差异。这是我学到的。
DirectorySearcher 来自 System.DirectoryServices 命名空间。
PrincipalSearcher 来自System.DirectoryServices.AccountManagement 命名空间,它建立在System.DirectoryServices 之上。 PrincipalSearcher 内部使用 DirectorySearcher。
AccountManagement 命名空间(即PrincipalSearcher)旨在简化用户、组和计算机对象(即主体)的管理。理论上,它的用法应该更容易理解,并且产生的代码行数更少。虽然到目前为止在我的实践中,它似乎在很大程度上取决于你在做什么。
DirectorySearcher 更底层,可以处理的不仅仅是用户、组和计算机对象。
对于一般用途,当您使用基本属性且只有少数对象时,PrincipalSearcher 将导致更少的代码行和更快的运行时间。
-
随着您正在做的任务变得越高级,这种优势似乎就消失了。例如,如果您期望超过几百个结果,则必须获取底层 DirectorySearcher 并设置 PageSize
DirectorySearcher ds = search.GetUnderlyingSearcher() as DirectorySearcher;
if( ds != null )
ds.PageSize = 1000;
如果使用PropertiesToLoad,DirectorySearcher 可以明显快于PrincipalSearcher。
DirectorySearcher 和类似的类可以与 AD 中的所有对象一起使用,而 PrincipalSearcher 则受到更多限制。例如,您不能使用PrincipalSearcher 和类似的类来修改组织单位。
这是我使用PrincipalSearcher、DirectorySearcher(不使用PropertiesToLoad)和DirectorySearcher(使用PropertiesToLoad)分析的图表。所有测试...
- 使用
PageSize 或1000
- 共查询4278个用户对象
- 指定以下条件
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;