在 .NET 中实现 Active Directory 更改通知

发布于 2024-12-21 06:19:03 字数 4893 浏览 1 评论 0原文

我正在尝试从活动目录获取更改通知,以便在我的 AD 中发生任何变化时可以更新数据库中的数据。我搜索并找到了 Ryan Dunn 的很好的示例

我尝试实现他的代码。应用程序启动时没有任何错误,但它没有向我生成任何通知。有人可以帮我吗?

我的域是 win 2008 服务器计算机上的 corp.am2k8vm.com,并且出于测试目的,我在 Active Directory 上几乎没有用户。

using System;
using System.Collections.Generic;
using System.DirectoryServices.Protocols;
using System.DirectoryServices;
namespace ChangeNotifications
{
    class Program
    {
        static void Main(string[] args)
        {
            using (LdapConnection connect = CreateConnection("192.168.182.209"))                //can also use localhost
            {
                using (ChangeNotifier notifier = new ChangeNotifier(connect))
                {
                    //register some objects for notifications (limit 5)
                    notifier.Register("dc=am2k8vm,dc=com", SearchScope.OneLevel);                     //not sure if the parameters are correct here as i am new to active directory stuff
                    notifier.Register("cn=Andy Main,ou=users,dc=am2k8vm,dc=com", SearchScope.Base); //not sure if the parameters are correct here as i am new to active directory stuff
                    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();
        }
        static private LdapConnection CreateConnection(string server)
        {
            LdapConnection connect = new LdapConnection(server);
            connect.SessionOptions.ProtocolVersion = 3;
            connect.AuthType = AuthType.Negotiate;  //use my current credentials
            return connect;
        }
    }
    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;}
    }
}

I am trying to get change notifications from active directory, so that I can update data in database if anythinghanges in my AD. I searched and found a good example by Ryan Dunn.

I tried implementing his code. The application starts without any errors but it is not generating me any notification. Can someone help me out?

My domain is corp.am2k8vm.com on win 2008 server machine and i have few users on active directory for testing purposes.

using System;
using System.Collections.Generic;
using System.DirectoryServices.Protocols;
using System.DirectoryServices;
namespace ChangeNotifications
{
    class Program
    {
        static void Main(string[] args)
        {
            using (LdapConnection connect = CreateConnection("192.168.182.209"))                //can also use localhost
            {
                using (ChangeNotifier notifier = new ChangeNotifier(connect))
                {
                    //register some objects for notifications (limit 5)
                    notifier.Register("dc=am2k8vm,dc=com", SearchScope.OneLevel);                     //not sure if the parameters are correct here as i am new to active directory stuff
                    notifier.Register("cn=Andy Main,ou=users,dc=am2k8vm,dc=com", SearchScope.Base); //not sure if the parameters are correct here as i am new to active directory stuff
                    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();
        }
        static private LdapConnection CreateConnection(string server)
        {
            LdapConnection connect = new LdapConnection(server);
            connect.SessionOptions.ProtocolVersion = 3;
            connect.AuthType = AuthType.Negotiate;  //use my current credentials
            return connect;
        }
    }
    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;}
    }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

瘫痪情歌 2024-12-28 06:19:03

尽管我对您的应用程序一无所知,但我将敦促您考虑完全不同的路径。

更改通知固然很好,但也有一些缺点。 AD 无法扩展到大量的数据。如果您离线一段时间,您会错过一些更改。等等。

我鼓励您考虑另一种机制,名为 DirSync。将 DirSync 视为 AD 内部复制协议的“原始暴露”,通过 LDAP 提供给您。 DirSync 的想法是,您可以发出查询并询问“发生了什么变化?” AD 会回答。答案是一个不透明的cookie。当您下次再次发出查询时,您再次提供 cookie,它会告诉您自上次发出 cookie 以来发生了什么变化。

其中有很多不错的元素:

  • DirSync 有一个很好的规模故事。您可以要求更改 1 个对象或 100 万个对象,我们知道 DirSync 将根据您的需求进行扩展。
  • DirSync 有一个关于离线一段时间的清晰故事。您可以断开连接一秒钟或一周,然后回来补上您错过的所有内容。
  • DirSync 查询速度非常快。每分钟发出一个或类似的东西应该没问题。
  • DirSync 有一个干净的多 DC 故事。您可以跨 DC 使用 cookie,它(大部分)只适合您。 (我说主要是因为您可能会得到重复的更改,但仅此而已)。
  • 也许最重要的是,DirSync 有一个非常清晰的一致性故事。我敦促使用 DirSync 的客户在大多数调用中执行高效的 DirSync 查询,但时不时地(每天?每周?每月?取决于应用程序...)您可以直接扔掉 cookie 并完全同步。这本质上迫使您真正设计一个干净的 e2e 解决方案,始终确保您有一种良好、安全的方式让您的离线数据库与 AD 中的事实保持一致,并且在 99% 以上的时间里保持高效。 “哦,出了问题”代码路径经过了很好的测试,因为它是主线代码路径!而且恰好和正常的代码路径是一样的。

假设您得到了重复更改,您需要进行防御性编码,但这对于大多数应用程序来说是一个合理的假设。

希望这有帮助。

I'm going to push you to consider a different path altogether, even though I know nothing about your app.

Change notifications are all well and good, but there are some downsides. AD doesn't scale to huge numbers of them. If you are offline for a while you miss some changes. Etc.

There is another mechanism I would encourage you to consider named DirSync. Think of DirSync as a "raw expose" of the AD internal replication protocol, given to you over LDAP. The idea of DirSync is that you can issue a query and say "what's changed?" and AD will answer. In with the answer is an opaque cookie. When you issue the query again next time, you provide the cookie again and it will tell you what has changed since the last cookie was issued.

Lots of nice elements of this:

  • DirSync has a nice scale story. You can ask for changes to 1 object or 1 million and we know DirSync will scale to your needs.
  • DirSync has a clean story for being offline for a period of time. You can be disconnected for a second or a week, come back and catch up with all that you missed.
  • DirSync queries are super fast. Issuing one every minute or something like that should be a-ok.
  • DirSync has a clean multi-DC story. You can use the cookie across DCs and it will (mostly) just work for you. (I say mostly as you might get dup changes, but that's about it).
  • Perhaps most of all, DirSync has a very clean consistency story. I push clients who use DirSync to do the efficient DirSync query in most calls, but every now and then (daily? weekly? monthly? Depends upon the app...) you can just throw the cookie away and full sync. This essentially forces you to really design a clean e2e solution that always ensures you have a nice, safe way to get your offline db to align with the truth in AD and also be efficient 99%+ of the time. And the "oh something went wrong" code path is super well tested as it is a mainline code path! And it happens to be the same as the normal code path.

You would need to defensively code assuming you get dup changes, but that's a reasonable assumption for most apps.

Hope this helps.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文