この投稿によると: DirectorySearcher.FindAll() - PageSize=1000 にする必要があります
SizeLimit
この場合、デフォルトで 1000 件の結果になるサーバー側のデフォルトが使用されているため、これは問題ではありません。このようにページングする必要はありませんでしたが、最小サイズ制限が使用されていると思います(サイズ制限とサーバーサイズ制限の間-自分のADでテストしただけです)。あなたPageSize
は確かにページングですが、バックグラウンドでページングを行い、すべての結果を取得している理由を理解しているので、最終ストリームのみを返します。
あなたの最も簡単な解決策は、これに加えてLinqを使用し、.Take(2000)
結果を実行することだと思います。これにより、追加の帯域幅とサーバーでの処理を犠牲にして、目的の結果が得られます.
本当にそれを並べ替えたい場合は、サーバーのデフォルトのページングサイズを更新して大きくする必要があると思います(ただし、これが管理上の理由で実行可能かどうかは疑問です).
編集:
これを大まかに行う方法を次に示します (LinqPad からの簡単に動作するサンプル コード - 式から for ループを取り出してこれを行う方法とは異なり、実際にはすべてを引き継いでいることに注意してください)。
using(DirectoryEntry de = new DirectoryEntry("LDAP://domain.local/dc=domain,dc=local", "user", "password"))
using(DirectorySearcher ds = new DirectorySearcher(de))
{
ds.Filter="(&(objectCategory=user)(objectClass=user))";
ds.PageSize= 1000;
ds.PropertiesToLoad.Clear();
ds.PropertiesToLoad.Add("objectGuid");
var results = ds.FindAll();
var searchResults = results.Cast<System.DirectoryServices.SearchResult>().ToArray();
int myDesiredPageSize = 2000;
var upns = new StringCollection();
for(var step=0; step < Math.Ceiling((double)results.Count / myDesiredPageSize); step++)
{
Parallel.ForEach(searchResults.Skip(step*myDesiredPageSize).Take(myDesiredPageSize), result => {
using(var entry = result.GetDirectoryEntry())
{
entry.RefreshCache(new[]{ "userPrincipalName" });
if(entry.Properties.Contains("userPrincipalName"))
upns.Add(entry.Properties["userPrincipalName"][0] as string);
}
});
}
upns.Count.Dump();
}
これにより、私のテスト シナリオでは、LAN 接続を介して約 3 秒で約 1400 の結果が返されます。並行してクエリを実行しているため、より大きな数値は線形にスケーリングされません。また、これは容赦なくADに打撃を与えるため、ある程度並列化を含めることをお勧めします:)
私の通常の操作ではWhenChanged
、AD オブジェクトの属性を使用したキャッシュを使用して、実際のクエリを変更されたオブジェクトのみに削減し、同じものを何度もロードするのではなく、最初のヒットのみを取得し、その後の結果を数分の 1 秒に短縮します。このアプローチを使用すると、ページングを完全に廃止し、開始時にプロパティをロードするだけで、変更されたエントリのみをプルすることができます。