2010-01-04 158 views
8

此链接http://msdn.microsoft.com/en-us/library/aa772153(VS.85).aspx注册变更通知说:使用Active Directory的C#

您可以备份到一个LDAP连接五个通知请求注册。您必须有一个专用线程,等待通知并快速处理它们。当您调用ldap_search_ext函数来注册通知请求时,该函数将返回标识该请求的消息标识符。然后使用ldap_result函数等待更改通知。发生更改时,服务器向您发送一条LDAP消息,其中包含生成通知的通知请求的消息标识符。这会导致ldap_result函数返回搜索结果,以识别发生更改的对象。

我无法通过.NET文档找到类似的行为。如果有人知道如何在C#中做到这一点,我会非常感激。我正在查看系统中所有用户的属性何时更改,以便我可以根据更改的内容执行自定义操作。

我已经看了通过stackoverflow和其他来源没有运气。

谢谢。

回答

12

我不知道它做你需要什么,但看看http://dunnry.com/blog/ImplementingChangeNotificationsInNET.aspx

编辑:添加的文本,然后从文章代码:



有三种方式弄清楚在Active Directory(或ADAM)中发生了变化的事情。 。

  1. Polling for Changes using uSNChanged该技术检查‘highestCommittedUSN’值开始,然后关于‘uSNChanged’值是高随后执行搜索: 这些都在恰当地名为“Overview of Change Tracking Techniques"  总之被记录在一段时间上在MSDN  'uSNChanged'属性不在域控制器之间复制,因此每次必须返回到相同的域控制器才能保持一致性。 本质上,您执行搜索以查找最高'uSNChanged'值+ 1,然后读入结果以您希望的任何方式跟踪它们
    • 好处
      • 这是最兼容的方式 所有语言和a因为这是一个简单的搜索,因此这种方式支持.NET的.NET版本。
    • 缺点
      • 开发者有很多需要照顾的地方。  您得到整个对象,并且必须确定对象上发生了什么变化(并且如果您关心该变化)。
      • 处理已删除的对象是一种痛苦。
      • 这是一种轮询技术,因此它只与您查询的频率一样实时。  这可能是一件好事,取决于应用程序。请注意,中间值也不在这里跟踪。
  2. Polling for Changes Using the DirSync Control。  此技术使用ADSI中的ADS_SEARCHPREF_DIRSYNC选项和封面下的LDAP_SERVER_DIRSYNC_OID控制。  只需进行初步搜索,存储cookie,然后再次搜索并发送cookie。  它只会返回已更改的对象。
    • 好处
      • 这是一个容易遵循的模型。  这两个系统。DirectoryServices和System.DirectoryServices.Protocols支持此选项。
      • 过滤可以减少你需要打扰。  作为一个例子,如果我的初始搜索是针对所有用户的“(objectClass = user)”,我可以随后使用“(sn = dunn)”过滤轮询,并且只返回两个过滤器的组合,而不必处理与初始过滤器的一切。
      • Windows 2003+选项将删除使用此选项的管理限制(对象安全性)。
      • Windows 2003+选项还将使您能够仅返回在大型多值属性中发生更改的增量值。  这是一个非常棒的功能。
      • 交易与删除的对象。
    • 缺点
      • 这是.NET 2.0+或更高版本的唯一选择。   .NET 1.1的用户需要使用uSNChanged Tracking。  脚本语言不能使用此方法。
      • 您只能将搜索范围限定到分区。  如果您只想跟踪特定的OU或对象,则必须稍后自行整理这些结果。
      • 在非Windows 2003模式域中使用此限制带有限制,即必须具有要使用的复制获取更改权限(仅默认为admin)。
      • 这是一个轮询技术。  它也不跟踪中间值。  因此,如果您想追踪多次搜索之间的变化,只会得到最后一次更改。  这可能是一个优势,取决于应用程序。
  3. Change Notifications in Active Directory。  此技术在单独的线程上注册搜索,当任何对象与筛选器匹配发生更改时将收到通知。  每个异步连接最多可以注册5个通知。
    • 好处
      • 即时通知。  其他技术需要轮询。
      • 因为这是一个通知,所以你会得到所有的改变,甚至是在其他两种技术中会丢失的中间变化。
    • 缺点
      • 资源相对。  您不希望完成这些操作,因为这可能会导致控制器出现可扩展性问题。
      • 这只会告诉你对象是否已经改变,但它不会告诉你改变是什么。  您需要弄清楚您关心的属性是否已更改。  这就是说,很容易判断对象是否已被删除(至少比uSNChanged轮询更容易)。
      • 您只能在非托管代码或System.DirectoryServices.Protocols中执行此操作。

大多数情况下,我发现DirSync几乎适用于所有情况下的账单。  我从来没有想过尝试任何其他技术。  但是,读者询问是否有方法在.NET中执行更改通知。  我觉得可以使用SDS.P,但从未尝试过。  原来,这可能并且实际上并不难。

我写这篇文章的第一个想法是使用MSDN上的sample code(并从选项#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(); 
} 
+0

这是完美的 - 非常感谢你的这个链接。 – Sam 2010-01-05 15:02:12

+0

@stuartd所以我有这个工作,RXing更改通知和所有。但是我回到了30个属性,其中没有一个与他们当前的配置不同。我是否错过了我们只能接收更改项目的部分? – Wjdavis5 2014-07-18 11:25:14

+0

@ Wjdavis5你应该问一个新问题。 – stuartd 2014-07-18 13:57:17