使用通配符的活动目录查询性能较差

时间:2017-02-05 13:14:22

标签: c# active-directory ldap ldap-query

我在C#中编写一个方法,该方法应该查询Active Directory并查找显示名称格式为 {displayName} 的所有用户和组(带有前导和尾随通配符的通配符搜索),方法将用于自动填充字段。

问题是我写的方法的性能非常差,尝试查询AD需要30秒到一分钟之间的任何内容,具体取决于查询字符串。

我的组织的AD非常大,但如果需要这么长时间,自动填充字段将毫无意义。

以下是我现在使用的代码:

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

请注意我在更新后的代码中启动并停止了“秒表”,我不知道为什么开始循环需要这么长时间。

1 个答案:

答案 0 :(得分:2)

当然,子串匹配比唯一值的相等匹配更昂贵。同样令人惊讶的是,大部分经过的时间都会落入你的迭代器块中,根据你的分析,它会消耗40秒。

如果你确信通过设置迭代器会导致性能大幅下降,那我就不是 - 那是因为你选择了时间点。

StartClock("foreach");
foreach (SearchResult searchResult in searchCol)
{
    // use an empty block to speed things up or
    StopClock("foreach");
    // whatever
    RestartClock("foreach");
}
StopClock("foreach");
LogClock("foreach");

如果您注意到我已经评论过的最佳做法,我希望获得巨大的性能提升(对于大型条目数量):向服务器发送一个请求,在搜索结果中收到您需要的所有信息,并且不要发送每个项目的另一个请求。虽然对GetDirectoryEntry()的单次调用仅消耗<1ms,但大量条目会使您的代码对应用程序自动完成功能无效。

感谢@rene提供该过滤器表达式的正常形式。我不知道Active Directory中的过滤器优化,所以我会采用确定的路径

(&(objectCategory=person)(displayName=*{result.querystring}*))