web-dev-qa-db-ja.com

PrincipalSearcherとDirectorySearcherの違い

PrincipalSearcherを使用するActive Directoryの例と、同じことを行うがDirectorySearcherを使用する他の例を確認しました。これら2つの例の違いは何ですか?

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());
}
35
Drew Chapin

私はこれら2つの違いを分析するのに多くの時間を費やしてきました。これが私が学んだことです。

  • DirectorySearcherSystem.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;
    
  • DirectorySearcherを使用すると、PrincipalSearcherPropertiesToLoadよりも大幅に高速になります。

  • DirectorySearcherなどのクラスはAD内のすべてのオブジェクトで機能しますが、PrincipalSearcherはより制限されています。たとえば、PrincipalSearcherなどのクラスを使用して組織単位を変更することはできません。

以下は、PrincipalSearcherDirectorySearcherを使用せずにPropertiesToLoadDirectorySearcherを使用してPropertiesToLoadを使用して分析するために作成したチャートです。すべてのテスト...

  • PageSize of 1000を使用します
  • 合計4,278のユーザーオブジェクトをクエリする
  • 次の基準を指定します
    • objectClass=user
    • objectCategory=person
    • スケジューリングリソースではありません(例:!msExchResourceMetaData=ResourceType:Room
    • 有効(つまり、!userAccountControl:1.2.840.113556.1.4.803:=2

DirectorySearcher vs. PrincipalSearcher Performance Chart


各テストのコード


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;
87
Drew Chapin

PrincipalSearcherは、グループまたはユーザーのディレクトリを照会するために使用されます。 DirectorySearcherは、あらゆる種類のオブジェクトのクエリに使用されます。

グループを取得する前にDirectorySearcherを使用してPrincipalSearcherを発見したため、前者を後者に置き換えると、プログラムの速度が向上しました(作成されたのはPrincipalSearcherだけでした)私にとってはより良いクエリです。私が気にしているのは、PrincipalSearcherの方が使いやすく、プリンシパルを取得するタスクに適していました。

一方、DirectorySearcherは他の種類のオブジェクトを取得できるため、より一般的です。これが、コメントで述べられているように強く型付けできない理由です。 PrincipalSearcherはすべてプリンシパルに関するものであるため、プリンシパルに関連する強く型付けされたオブジェクトが含まれます。これが、親切なユーザーまたはグループのオブジェクトを取得するために指示する必要がない理由でもあります。あなたが使用するプリンシパルクラスによって。

3
Mzn

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