【问题标题】:Effective pagination with Active Directory searches使用 Active Directory 搜索进行有效分页
【发布时间】:2014-03-04 21:07:34
【问题描述】:

在 .NET 中使用 Active Directory 搜索进行分页的有效方法是什么?在 AD 中搜索的方法有很多,但到目前为止我还没有找到有效的方法。我希望能够指出SkipTake 参数,并能够在结果中检索符合我的搜索条件的记录总数。

我尝试使用 PrincipalSearcher 类进行搜索:

using (var ctx = new PrincipalContext(ContextType.Domain, "FABRIKAM", "DC=fabrikam,DC=com"))
using (var criteria = new UserPrincipal(ctx))
{
    criteria.SamAccountName = "*foo*";

    using (var searcher = new PrincipalSearcher(criteria))
    {
        ((DirectorySearcher)searcher.GetUnderlyingSearcher()).SizeLimit = 3;
        var results = searcher.FindAll();
        foreach (var found in results)
        {
            Console.WriteLine(found.Name);
        }
    }
}

在这里,我能够将搜索结果限制为 3,但我无法获得与我的搜索条件相对应的记录总数(SamAccountName 包含 foo)我也无法向搜索者表明例如跳过前 50 条记录。

我也尝试过使用System.DirectoryServices.DirectoryEntrySystem.DirectoryServices.Protocols.SearchRequest,但我唯一能做的就是指定页面大小。

那么,在客户端获取所有结果并在那里执行 Skip and Count 的唯一方法是什么?真心希望有更有效的方法可以直接在域控制器上实现。

【问题讨论】:

标签: c# .net active-directory


【解决方案1】:

您可以尝试虚拟列表视图搜索。下面按照cn对用户进行排序,然后从第100个开始得到51个用户。

    DirectoryEntry rootEntry = new DirectoryEntry("LDAP://domain.com/dc=domain,dc=com", "user", "pwd");

    DirectorySearcher searcher = new DirectorySearcher(rootEntry);
    searcher.SearchScope = SearchScope.Subtree;
    searcher.Filter = "(&(objectCategory=person)(objectClass=user))";
    searcher.Sort = new SortOption("cn", SortDirection.Ascending);
    searcher.VirtualListView = new DirectoryVirtualListView(0, 50, 100);

    foreach (SearchResult result in searcher.FindAll())
    {
        Console.WriteLine(result.Path);
    }

对于您的用例,您只需要 DirectoryVirtualListView 的 BeforeCount、AfterCount 和 Offset 属性(DirectoryVirtualListView ctor 中的 3 个)。 DirectoryVirtualListView 的文档非常有限。您可能需要对其行为方式进行一些实验。

【讨论】:

    【解决方案2】:

    如果 SizeLimit 设置为零且 PageSize 设置为 500,则搜索将返回 500 个项目的页面中的所有 12,000 个结果,最后一页仅包含 200 个项目。分页对应用程序是透明的,除了将 PageSize 属性设置为适当的值之外,应用程序不需要执行任何特殊处理。

    SizeLimit 限制您一次可以检索的结果数量 - 因此您的 PageSize 需要小于或等于 1000(Active Directory 将搜索结果的最大数量限制为 1000。在这种情况下,将 SizeLimit 属性设置为大于 1000 的值无效。)。 当您调用 FindAll() 等时,分页会在后台自动完成。

    更多详情请参考MSDN

    https://msdn.microsoft.com/en-us/library/ms180880.aspx

    https://msdn.microsoft.com/en-us/library/system.directoryservices.directorysearcher.pagesize.aspx

    https://msdn.microsoft.com/en-us/library/system.directoryservices.directorysearcher.sizelimit.aspx

    【讨论】:

      【解决方案3】:

      派对迟到了,但这就是我正在做的事情:

      我在PropertiesToLoad 上使用FindOne() 而不是FindAll()member;range=<start>-<end>

      member;range有一个问题:当它是最后一页时,即使你通过member;range=1000-1999(例如),它也会返回member;range=1000-*,所以你必须检查最后的*才能知道如果有更多数据。

      public void List<string> PagedSearch()
      { 
          var list = new List<string>();
          bool lastPage = false;
          int start = 0, end = 0, step = 1000;
      
          var rootEntry = new DirectoryEntry("LDAP://domain.com/dc=domain,dc=com", "user", "pwd");
      
          var filter = "(&(objectCategory=person)(objectClass=user)(samAccountName=*foo*))";
      
          using (var memberSearcher = new DirectorySearcher(rootEntry, filter, null, SearchScope.Base))
          {
              while (!lastPage)
              {
                  start = end;
                  end = start + step - 1;
      
                  memberSearcher.PropertiesToLoad.Clear();
                  memberSearcher.PropertiesToLoad.Add(string.Format("member;range={0}-{1}", start, end));
      
                  var memberResult = memberSearcher.FindOne();
      
                  var membersProperty = memberResult.Properties.PropertyNames.Cast<string>().FirstOrDefault(p => p.StartsWith("member;range="));
      
                  if (membersProperty != null)
                  {
                      lastPage = membersProperty.EndsWith("-*");
                      list.AddRange(memberResult.Properties[membersProperty].Cast<string>());
                      end = list.Count;
                  }
                  else
                  {
                      lastPage = true;
                  }
              }
          }
          return list;
      }
      

      【讨论】:

        【解决方案4】:
            private static DirectoryEntry forestlocal = new DirectoryEntry(LocalGCUri, LocalGCUsername, LocalGCPassword);
            private DirectorySearcher localSearcher = new DirectorySearcher(forestlocal);
        
             public List<string> GetAllUsers() 
            {
                List<string> users = new List<string>();
        
                localSearcher.SizeLimit = 10000;
                localSearcher.PageSize = 250;
        
                string localFilter = string.Format(@"(&(objectClass=user)(objectCategory=person)(!(objectClass=contact))(msRTCSIP-PrimaryUserAddress=*))");
        
                localSearcher.Filter = localFilter;
        
                SearchResultCollection localForestResult;
        
                try
                {
                    localForestResult = localSearcher.FindAll();
        
                    if (resourceForestResult != null) 
                    {
        
                        foreach (SearchResult result in localForestResult) 
                        {
                            if (result.Properties.Contains("mail"))
                                users.Add((string)result.Properties["mail"][0]);
                        }
        
                    }
        
                }
                catch (Exception ex) 
                {
        
                }
        
                return users;
            }
        

        【讨论】:

        • 我不太明白这个 PageSize 和 SizeLimit 是如何工作的。假设我指定了SizeLimit=1000PageSize=5,并且有200 个用户符合我的搜索条件。现在我想让用户 195-200。此示例是否会通过向 AD 发出 40 个请求将所有 200 个用户返回给客户端?然后我必须在客户端对这 200 个用户进行分页,以便将用户从 195 提取到 200?如果这是真的,这是唯一的方法吗?我的意思是,如果我要丢弃用户 1-195 到客户端,这对我来说似乎完全是浪费,因为在我的 UI 中我想显示 195-200。
        • @DarinDimitrov 是的,不能跳过第一个条目的分页毫无价值
        • 是的,完全没有价值,因此我的问题是您是否可以使用 AD 进行有效的分页。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-02-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多