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());
}
私はこれら2つの違いを分析するのに多くの時間を費やしてきました。これが私が学んだことです。
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;
DirectorySearcher
を使用すると、PrincipalSearcher
はPropertiesToLoad
よりも大幅に高速になります。
DirectorySearcher
などのクラスはAD内のすべてのオブジェクトで機能しますが、PrincipalSearcher
はより制限されています。たとえば、PrincipalSearcher
などのクラスを使用して組織単位を変更することはできません。
以下は、PrincipalSearcher
、DirectorySearcher
を使用せずにPropertiesToLoad
、DirectorySearcher
を使用してPropertiesToLoad
を使用して分析するために作成したチャートです。すべてのテスト...
PageSize
of 1000
を使用します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;
PrincipalSearcher
は、グループまたはユーザーのディレクトリを照会するために使用されます。 DirectorySearcher
は、あらゆる種類のオブジェクトのクエリに使用されます。
グループを取得する前にDirectorySearcher
を使用してPrincipalSearcher
を発見したため、前者を後者に置き換えると、プログラムの速度が向上しました(作成されたのはPrincipalSearcher
だけでした)私にとってはより良いクエリです。私が気にしているのは、PrincipalSearcher
の方が使いやすく、プリンシパルを取得するタスクに適していました。
一方、DirectorySearcher
は他の種類のオブジェクトを取得できるため、より一般的です。これが、コメントで述べられているように強く型付けできない理由です。 PrincipalSearcher
はすべてプリンシパルに関するものであるため、プリンシパルに関連する強く型付けされたオブジェクトが含まれます。これが、親切なユーザーまたはグループのオブジェクトを取得するために指示する必要がない理由でもあります。あなたが使用するプリンシパルクラスによって。
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++;
}
}