Active Directory搜索的有效分页

时间:2014-02-07 12:37:49

标签: c# .net active-directory

在.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,但我唯一能做的就是指定页面大小。

这是获取客户端上所有结果并在那里进行跳过和计数的唯一方法吗?我真的希望有更有效的方法直接在域控制器上实现这一点。

4 个答案:

答案 0 :(得分:7)

您可以尝试虚拟列表视图搜索。下面按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的文档非常有限。您可能需要对其行为进行一些实验。

答案 1 :(得分:4)

如果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

答案 2 :(得分:0)

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

我在FindOne()上使用FindAll()代替member;range=<start>-<end>PropertiesToLoad

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;
}

答案 3 :(得分:-1)

    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;
    }