DirectorySearcher.Asynchronousプロパティをtrueとすると、検索処理が非同期に実行されますが、検索するクライアントプログラム自体は同期的に処理されます。今回はSystem.DirectoryServices.Protocols名前空間のクラスを使用して、検索を非同期に行います。非同期処理は、.NETの非同期パターンと同じ方法(IAsyncResultを使用して非同期結果を受け取る)を使用しますが、紹介する方法では部分検索結果を受け取ることもできるので、.NETの非同期パターン(処理が完全に完了してから呼び出される)とは異なります。

確認環境

  • Windows Server 2003 AD環境
  • 開発環境:Visual Studio 2008
  • .NET 2.0

1. ソリューションの作成

コンソールプロジェクトを作成し、System.DirectoryServices.dllとSystem.DirectoryServices.Protocols.dllをプロジェクトの参照に追加します。

1.1 プログラムの作成

非同期検索を行うプログラムを掲載します。GetLdapConnection()をKerberos認証を使用して現在実行中のユーザでLDAPに接続する処理です。別のユーザの資格情報を利用する場合はコメントされているCredentialに資格情報を設定します。

サンプルでは、AsyncCallbackとAsyncCallback2というコールバックメソッドを使用して非同期検索のコールバックを受け取るようにしています。AsyncCallback2の場合は、非同期部分検索結果も受け取るように実装されています。非同期検索結果のみを受けとるAsyncCallbackを使用する場合は、コメントにされている_connect.BeginSendRequestの箇所のコメントを解除し、コールバックにAsyncCallback2を指定しているステートメントをコメントにします。非同期検索中に部分検索をコールバックメソッドから受け取れるようにするかはLdapConnection.BeginSendRequestを呼び出し時に指定するPartialResultProcessingに適切な値を指定します。

/// <summary>
/// System.DirectoryServices.Protocols名前空間のクラスを使用
/// して非同期検索を実施する。.NETの非同期実行パターンと異なり、
/// 全検索結果を待機せず、検索結果の一部を取得できる。
/// System.DirectoryServices.Protocols.dllを参照に追加
/// </summary>
class Program
{
    static LdapConnection _connect;

    static void Main(string[] args)
    {
        _connect = GetLdapConnection();
        _connect.Bind();

        SearchRequest request = new SearchRequest("DC=crm1,DC=local", "(objectClass=user)", SearchScope.Subtree, null);
        request.SizeLimit = 255;

        // 非同期検索のみ行う場合
        //IAsyncResult result = _connect.BeginSendRequest(request
        //    , PartialResultProcessing.NoPartialResultSupport, new AsyncCallback(AsyncCallback), null);
        // 非同期検索を行い、部分検索結果を受け取る場合
        IAsyncResult result = _connect.BeginSendRequest(request, PartialResultProcessing.ReturnPartialResultsAndNotifyCallback,
            new AsyncCallback(AsyncCallback2), null);
         result.AsyncWaitHandle.WaitOne();

        Console.WriteLine("メインスレッド終了.検索完了後Enterクリック");

        Console.ReadLine();
    }
    static LdapConnection GetLdapConnection()
    {
        LdapConnection con = new LdapConnection(new LdapDirectoryIdentifier("crm1.local:389"));
        //con.Credential = new NetworkCredential("Administrator", "password", "crm1");
        // ADのKerberos認証を使用する場合
        con.SessionOptions.Sealing = true;
        // SSLを使用する場合
         //con.SessionOptions.SecureSocketLayer = true;

        return con;
    }
    static void AsyncCallback(IAsyncResult result)
    {
        Console.WriteLine("検索完了");
        SearchResponse response = _connect.EndSendRequest(result) as SearchResponse;

        foreach (SearchResultEntry entry in response.Entries)
        {
            Console.WriteLine(entry.DistinguishedName);
        }
    }
    static void AsyncCallback2(IAsyncResult result)
    {
        Console.WriteLine("コールバックスレッド");
        if (!result.IsCompleted)
        {
            PartialResultsCollection partialResultCollection = _connect.GetPartialResults(result);
            Console.WriteLine("部分検索結果");
            for (int i = 0; i < partialResultCollection.Count; ++i)
            {
                SearchResultEntry entry = partialResultCollection[i] as SearchResultEntry;
                if (entry != null)
                {
                    Console.WriteLine(entry.DistinguishedName);
                }
            }
        }
        else
        {
            Console.WriteLine("検索完了");
            SearchResponse response = _connect.EndSendRequest(result) as SearchResponse;
            foreach (SearchResultEntry entry in response.Entries)
            {
                Console.WriteLine(entry.DistinguishedName);
            }
        }
    }
}

2.おわりに

説明は以上です。SDS.Pを使用すると、非同期検索を行え、部分検索結果を受け取ることもできます。

間違い、指摘点がありましたらご連絡ください。