11

このリンクhttp://msdn.microsoft.com/en-us/library/aa772153(VS.85).aspxによると:

1つのLDAP接続で最大5つの通知要求を登録できます。通知を待機して迅速に処理する専用のスレッドが必要です。ldap_search_ext関数を呼び出して通知要求を登録すると、関数はその要求を識別するメッセージIDを返します。次に、ldap_result関数を使用して、変更通知を待ちます。変更が発生すると、サーバーは、通知を生成した通知要求のメッセージIDを含むLDAPメッセージを送信します。これにより、ldap_result関数は、変更されたオブジェクトを識別する検索結果を返します。

.NETのドキュメントを調べても、同様の動作は見つかりません。誰かがC#でこれを行う方法を知っているなら、私は知ってとても感謝しています。システム内のすべてのユーザーの属性がいつ変更されるかを確認して、変更内容に応じてカスタムアクションを実行できるようにします。

運が悪かったので、stackoverflowやその他のソースを調べました。

ありがとう。

4

1 に答える 1

22

必要な処理が行われるかどうかはわかりませんが、http://dunnry.com/blog/ImplementingChangeNotificationsInNET.aspxをご覧ください。

編集:記事からテキストとコードを追加しました:



Active Directory(またはADAM)で変更されたものを把握する方法は3つあります。これらは、MSDNの「変更追跡手法の概要」という適切なタイトルでしばらくの間文書化されています。要約すれば:

  1. uSNChangedを使用した変更のポーリング。この手法では、「highestCommittedUSN」値をチェックして開始し、その後、より高い「uSNChanged」値の検索を実行します。'uSNChanged'属性はドメインコントローラー間で複製されないため、一貫性を保つために毎回同じドメインコントローラーに戻る必要があります。基本的に、最大の「uSNChanged」値+ 1を探して検索を実行し、結果を読み取って、任意の方法でそれらを追跡します。
    • 利点
      • これが最も互換性のある方法です。.NETは単純な検索であるため、すべての言語とすべてのバージョンの.NETがこの方法をサポートしています。
    • 短所
      • ここには、開発者が世話をすることがたくさんあります。オブジェクト全体を取り戻し、オブジェクトで何が変更されたかを判断する必要があります(そしてその変更を気にするかどうか)。
      • 削除されたオブジェクトを処理するのは面倒です。
      • これはポーリング手法であるため、クエリを実行する頻度と同じくらいリアルタイムです。これは、アプリケーションによっては良いことです。ここでも中間値は追跡されないことに注意してください。
  2. DirSyncコントロールを使用した変更のポーリング。この手法では、ADSIのADS_SEARCHPREF_DIRSYNCオプションと、内部のLDAP_SERVER_DIRSYNC_OIDコントロールを使用します。最初の検索を行い、Cookieを保存してから、後でもう一度検索してCookieを送信するだけです。変更されたオブジェクトのみが返されます。
    • 利点
      • これは従うのが簡単なモデルです。System.DirectoryServicesとSystem.DirectoryServices.Protocolsの両方がこのオプションをサポートしています。
      • フィルタリングにより、煩わしいことを減らすことができます。たとえば、最初の検索がすべてのユーザー "(objectClass = user)"である場合、その後、 "(sn = dunn)"でポーリングをフィルタリングし、両方のフィルターの組み合わせのみを取得できます。初期フィルターからすべて。
      • Windows 2003+オプションは、このオプション(オブジェクトセキュリティ)を使用するための管理上の制限を取り除きます。
      • Windows 2003+オプションでは、大きな複数値属性で変更された増分値のみを返す機能も提供されます。これは本当に素晴らしい機能です。
      • 削除されたオブジェクトをうまく処理します。
    • 短所
      • これは.NET2.0以降のみのオプションです。.NET 1.1のユーザーは、uSNChangedTrackingを使用する必要があります。スクリプト言語はこの方法を使用できません。
      • 検索のスコープはパーティションにのみ指定できます。特定のOUまたはオブジェクトのみを追跡する場合は、後でそれらの結果を自分で分類する必要があります。
      • これをWindows2003以外のモードのドメインで使用すると、使用するレプリケーションの変更権限(デフォルトでは管理者のみ)を取得する必要があるという制限があります。
      • これはポーリング手法です。中間値も追跡しません。したがって、検索間の変更を複数回追跡するオブジェクトの場合、最後の変更のみを取得します。これは、アプリケーションによっては利点になる場合があります。
  3. ActiveDirectoryの通知を変更します。この手法は、フィルターに一致するオブジェクトが変更されたときに通知を受け取る別のスレッドに検索を登録します。非同期接続ごとに最大5つの通知を登録できます。
    • 利点
      • 即時通知。他の手法では、ポーリングが必要です。
      • これは通知であるため、他の2つの手法で失われた中間の変更も含め、すべての変更を取得します。
    • 短所
      • 比較的リソースを大量に消費します。コントローラでスケーラビリティの問題が発生する可能性があるため、これらを大量に実行する必要はありません。
      • これは、オブジェクトが変更されたかどうかを通知するだけで、変更内容は通知しません。気になる属性が変更されたかどうかを把握する必要があります。そうは言っても、オブジェクトが削除されたかどうかを判断するのは非常に簡単です(少なくともuSNChangedポーリングよりも簡単です)。
      • これは、アンマネージコードまたはSystem.DirectoryServices.Protocolsでのみ実行できます。

ほとんどの場合、DirSyncは事実上すべての状況で私に適していることがわかりました。私は他のテクニックを試すことを決して気にしませんでした。ただし、ある読者から、.NETで変更通知を行う方法があるかどうか尋ねられました。SDS.Pを使えば可能だと思いましたが、試したことはありませんでした。結局のところ、それは可能であり、実際にはそれほど難しいことではありません。

これを書くことについての私の最初の考えは、MSDNにある(そしてオプション#3から参照されている)サンプルコードを使用し、これをSystem.DirectoryServices.Protocolsに変換することでした。これは行き止まりであることが判明しました。SDS.Pでそれを行う方法とサンプルコードの動作方法は十分に異なるため、役に立ちません。これが私が思いついた解決策です:

public class ChangeNotifier : IDisposable
{
    LdapConnection _connection;
    HashSet<IAsyncResult> _results = new HashSet<IAsyncResult>();

    public ChangeNotifier(LdapConnection connection)
    {
        _connection = connection;
        _connection.AutoBind = true;
    }

    public void Register(string dn, SearchScope scope)
    {
        SearchRequest request = new SearchRequest(
            dn, //root the search here
            "(objectClass=*)", //very inclusive
            scope, //any scope works
            null //we are interested in all attributes
            );

        //register our search
        request.Controls.Add(new DirectoryNotificationControl());

        //we will send this async and register our callback
        //note how we would like to have partial results

        IAsyncResult result = _connection.BeginSendRequest(
            request,
            TimeSpan.FromDays(1), //set timeout to a day...
            PartialResultProcessing.ReturnPartialResultsAndNotifyCallback,
            Notify,
            request);

        //store the hash for disposal later

        _results.Add(result);
    }

    private void Notify(IAsyncResult result)
    {
        //since our search is long running, we don't want to use EndSendRequest
        PartialResultsCollection prc = _connection.GetPartialResults(result);

        foreach (SearchResultEntry entry in prc)
        {
            OnObjectChanged(new ObjectChangedEventArgs(entry));
        }
    }

    private void OnObjectChanged(ObjectChangedEventArgs args)
    {
        if (ObjectChanged != null)
        {
            ObjectChanged(this, args);
        }
    }

    public event EventHandler<ObjectChangedEventArgs> ObjectChanged;

    #region IDisposable Members

    public void Dispose()
    {
        foreach (var result in _results)
        {
            //end each async search
            _connection.Abort(result);

       }
    }

    #endregion
}


public class ObjectChangedEventArgs : EventArgs
{
    public ObjectChangedEventArgs(SearchResultEntry entry)
    {
        Result = entry;
    }

    public SearchResultEntry Result { get; set;}
}

これは、検索の登録に使用できる比較的単純なクラスです。秘訣は、コールバックメソッドでGetPartialResultsメソッドを使用して、発生したばかりの変更のみを取得することです。また、結果を返すために使用している非常に単純化されたEventArgsクラスも含めました。ここではスレッド化について何もしておらず、エラー処理もありません(これは単なるサンプルです)。このクラスは次のように使用できます。

static void Main(string[] args)
{
    using (LdapConnection connect = CreateConnection("localhost"))
    {
        using (ChangeNotifier notifier = new ChangeNotifier(connect))
        {
            //register some objects for notifications (limit 5)
            notifier.Register("dc=dunnry,dc=net", SearchScope.OneLevel);
            notifier.Register("cn=testuser1,ou=users,dc=dunnry,dc=net", SearchScope.Base);

            notifier.ObjectChanged += new EventHandler<ObjectChangedEventArgs>(notifier_ObjectChanged);

            Console.WriteLine("Waiting for changes...");
            Console.WriteLine();
            Console.ReadLine();
        }
    }
}


static void notifier_ObjectChanged(object sender, ObjectChangedEventArgs e)
{
    Console.WriteLine(e.Result.DistinguishedName);

    foreach (string attrib in e.Result.Attributes.AttributeNames)
    {
        foreach (var item in e.Result.Attributes[attrib].GetValues(typeof(string)))
        {
            Console.WriteLine("\t{0}: {1}", attrib, item);
        }
    }
    Console.WriteLine();
    Console.WriteLine("====================");
    Console.WriteLine();
}
于 2010-01-04T22:33:45.063 に答える