【问题标题】:Difference between PrincipalSearcher and DirectorySearcherPrincipalSearcher 和 DirectorySearcher 的区别
【发布时间】:2014-04-19 22:26:40
【问题描述】:

我看到了使用 PrincipalSearcher 的 Active Directory 示例和其他执行相同操作但使用 DirectorySearcher 的示例。这两个例子有什么区别?

使用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);
}

使用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());
}

【问题讨论】:

  • 对我来说这将是强类型?,更好的抽象。 Principal Searcher 不存在于 Active Directory 管理扩展库或类似的库中。社区对此了解得越多越好。我用它来避免在处理 AD 时通常遇到的粗糙语法。
  • System.DirectoryServices.AccountManagement 是库 fwiw。
  • +1 不错的代码示例!看看我的回答也许会帮助回答这个问题

标签: c# active-directory directoryservices


【解决方案1】:

我花了很多时间分析这两者之间的差异。这是我学到的。

  • 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;
    
  • 如果使用PropertiesToLoadDirectorySearcher 可以明显快于PrincipalSearcher

  • DirectorySearcher 和类似的类可以与 AD 中的所有对象一起使用,而 PrincipalSearcher 则受到更多限制。例如,您不能使用PrincipalSearcher 和类似的类来修改组织单位。

这是我使用PrincipalSearcherDirectorySearcher(不使用PropertiesToLoad)和DirectorySearcher(使用PropertiesToLoad)分析的图表。所有测试...

  • 使用PageSize1000
  • 共查询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);
}


DirectorySearcherPropertiesToLoad 一起使用

同“使用DirectorySearcher,但添加这一行

search.PropertiesToLoad.AddRange(new string[] { "canonicalName" });

之后

search.PageSize = 1000;

【讨论】:

  • 这是一个很好的答案。虽然目录搜索比主要搜索更快,但我发现使用 principalsearcher 进行后续搜索更快。我猜在引擎盖下会有某种缓存。
  • 投入了大量精力.. +1
  • 优秀的...教程? ;) 做得好! +1
  • 什么是 UserPrincipalEx?
  • 它是UserPrinciple 的扩展,它利用AdvancedFiltersEx 类,因此它可以过滤掉基于msExchResourceMetaDataobjectCategory
【解决方案2】:

PrincipalSearcher 用于查询目录中的组或用户。 DirectorySearcher用于查询各类对象。

我之前使用DirectorySearcher 获取组,然后我发现PrincipalSearcher 所以当我用后者替换前者时,我的程序速度提高了(也许只是PrincipalSearcher 为我创建了一个更好的查询。就我而言,PrincipalSearcher 更易于使用,更适合获取委托人的任务。

另一方面,DirectorySearcher 更通用,因为它可以获取其他类型的对象。这就是为什么它不能像 cmets 中提到的那样被强类型化。 PrincipalSearcher 是关于主体的,因此它将具有与主体相关的强类型对象,这就是为什么您也不需要告诉它为您获取一个用户或组的对象,它将由主体暗示你使用的类。

【讨论】:

    【解决方案3】:

    DirectorySearcher 的速度要快得多。来自@DrewChapin 的例子可以更进一步。根据我的测试,大约快 10 倍。我可以用他的代码在 3.8 秒内拉出 721 个计算机 cn。相当快。通过我的更改,我在 0.38 秒内完成了它。取决于你在做什么,这可能是巨大的。我在预测性帐户搜索中使用了它(开始输入名称并填充组合框。一个非常短的 System.Timer 在每次按键后启动并被按键取消。如果计时器过去,列表会更新。效率是巨大的。 )

    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++;
        }
    }
    

    【讨论】:

    • AuthenticationTypes.FastBind 大大提高了 DirectorySearcher 的性能。另请查看您的 ReferralChasingOptions。
    【解决方案4】:

    使我的 PrincipalSearcher 查询比以前快得多的一件事就是使用这种语法。返回 FindAll() 后,我将其转换为 Queryable。我在大约 2 秒内返回 3000 条记录。我也在异步方法中运行它。

    using (var ctx = new PrincipalContext(ContextType.Domain, "domainname", 
      "DOMAIN_DESCRIPTOR"))
    {
        using (var u = new UserPrincipal(ctx))
        {
              var result = new PrincipalSearcher(u)
                            .FindAll()
                            .AsQueryable()
                            .Take(3000)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-06-29
      • 2013-08-07
      • 2011-10-20
      • 2020-01-23
      • 1970-01-01
      • 1970-01-01
      • 2015-11-20
      • 2014-10-09
      相关资源
      最近更新 更多