【发布时间】:2026-02-17 10:35:01
【问题描述】:
我正在用 C# 编写一个方法,它应该查询 Active Directory 并查找所有用户和组的显示名称格式为 {displayName}(通配符搜索带有前导通配符和尾随通配符),方法将用于自动完成字段。
问题是我写的方法的性能真的很差,尝试查询 AD 需要 30 秒到一整分钟之间的任何时间,具体取决于查询字符串。
我的组织的广告非常大,但如果需要这么长时间,自动完成字段将毫无意义。
这是我现在使用的代码:
// Intialize the results list.
result.queryResult = new List<Classses.ADSearchObject>();
// Set up domain context.
PrincipalContext pc = new PrincipalContext(ContextType.Domain, Domain, Constants.adQueryUser, Constants.adQueryPassword);
// Set up a directory searcher.
DirectorySearcher dSearcher = new DirectorySearcher();
// Define a SearchCollection to store the results.
SearchResultsCollection searchCol;
// Define returned result paging for performance.
dSearcher.PageSize = 1000;
// Define the properties to retrieve
dSearcher.PropertiesToLoad.Add("sAMAccountName");
dSearcher.PropertiesToLoad.Add("displayName");
// Define the filter for users.
dSearcher.Filter = $"(|(&(displayName = {result.querystring}*)(objectCategory=person))(&(displayName=*{result.querystring})(objectCategory=person)))";
// Search based in filter and save the results.
searchCol = dSearcher.FindAll();
// Add the results to the returned object
foreach (SearchResult searchResult in searchCol)
{
DirectoryEntry de = searchResult.GetDirectoryEntry();
// Code to get data from the results...
}
// Define the filter for groups.
dSearcher.Filter = $"(|(&(displayName={result.querystring}*)(objectCategory=person))(&(displayName=*{result.querystring})(objectCategory=person)))";
// Search based in filter and save the results.
searchCol = dSearcher.FindAll();
// Add the results to the returned object
foreach (SearchResult searchResult in searchCol)
{
DirectoryEntry de = searchResult.GetDirectoryEntry();
// Code to get data from the results...
}
目前搜索分为用户和组,以便于区分它们,但如果它显着提高性能,我会将它们统一为一个搜索。
编辑:正如用户 rene 建议的那样,我使用 Stopwatch 来检查 FindAll 所需的时间,并且我还检查了我的 foreach 循环需要多长时间。
我发现 FindAll 调用大约需要 100 毫秒(非常快),即使使用 AD 索引的前导通配符(不是)进行搜索。
显然,耗时最长的调用是我的 foreach 循环,大约需要 40 秒(40,000 毫秒)。
我正在使用 foreach 循环中的代码块更新问题,因为我还没有弄清楚如何提高其性能:
// --- I started a stopwatch here
foreach (SearchResult searchResult in searchCol)
{
// --- I stopped the stopwatch here and noticed it takes about 30,000ms
result.code = 0;
DirectoryEntry de = searchResult.GetDirectoryEntry();
ADSearchObject adObj = new ADSearchObject();
adObj.code = 0;
if (de.Properties.Contains("displayName")
{
adObj.displayName = de.Properties["displayName"].Value.ToString();
}
adObj.type = "user";
result.queryResults.Add(adObj);
}
请注意我在更新的代码中开始和停止“秒表”的位置,我不知道为什么开始循环需要这么长时间。
【问题讨论】:
-
这是处理微软广告时的工作方式。也许您可以考虑将 AD 树与某种 DB 同步,这样您就可以在几毫秒内执行查询,而不是每次都查询 AD。
-
(&(displayName=*{result.querystring}*)(objectCategory=person))不会和你的过滤器一样吗?你能秒表FindAll电话吗?那些花时间最多的人吗? -
@rene 请查看我对帖子所做的编辑,我添加了来自
Stopwatch我在代码的不同部分打开的时间 -
只需调用
FindAll()即可准确检索您需要的内容。不要无缘无故地使用GetDirectoryEntry()来一一检索完整条目。难怪盯着屏幕看会变老。 -
@marabu 请参考我的编辑,进入
foreach循环时会出现性能下降。在初始进入循环后,性能与您预期的一样快,并且对GetDirectoryEntry()的调用不会花费特别长的时间
标签: c# active-directory ldap ldap-query