在.NET中对Active Directory搜索进行分页的有效方法是什么?在AD中搜索的方法有很多,但到目前为止我找不到如何有效地进行搜索。我希望能够指出Skip
和Take
参数,并能够在结果中检索与我的搜索条件匹配的记录总数。
我尝试使用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.DirectoryEntry
和System.DirectoryServices.Protocols.SearchRequest
,但我唯一能做的就是指定页面大小。
这是获取客户端上所有结果并在那里进行跳过和计数的唯一方法吗?我真的希望有更有效的方法直接在域控制器上实现这一点。
答案 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;
}