2

我有一个从 ActiveDirectory 检索一些信息的循环。事实证明这是一个很大的性能瓶颈。

此代码段(在执行 31 次的循环内)耗时 00:01:14.6562500(1 分 14 秒):

SearchResult data = searcher.FindOne();
System.Diagnostics.Trace.WriteLine(PropsDump(data));

用此代码段替换它使其降至 00:00:03.1093750(3 秒):

searcher.SizeLimit = 1;
SearchResultCollection coll = searcher.FindAll();
foreach (SearchResult data in coll)
{
    System.Diagnostics.Trace.WriteLine(PropsDump(data));
}

结果完全相同,相同的属性以相同的顺序返回。我在另一个线程中发现了一些关于内存泄漏的信息,但他们没有提到性能(我在 .Net 3.5 上)。


以下实际上是一个不同的问题,但它提供了一些背景,说明我为什么首先要循环:

我想在一个查询中获取所有属性,但我无法让 DirectorySearcher 一次性返回所有想要的属性(它省略了 PropertiesToLoad 中指定的大约 30% 的属性(也尝试在构造函数中设置它)区别),我发现其他人也有同样的问题,这是他的解决方案(循环遍历它们)。当我像这样循环遍历它们时,使用 FindOne() 或 FindAll() 我确实得到了所有属性,但实际上它一切都感觉像是一种解决方法。

我错过了什么吗?


编辑:

似乎问题出在我获得第一个使用 DirectorySearcher 的 DirectoryEntry 的方式上。

这是导致 DirectorySearcher 只返回一些属性的代码:

private static DirectoryEntry GetEntry() {
    DirectoryContext dc = new DirectoryContext(DirectoryContextType.DirectoryServer, "SERVERNAME", "USERNAME", "PASSWORD");
    Forest forest = Forest.GetForest(dc);
    DirectorySearcher searcher = forest.GlobalCatalogs[0].GetDirectorySearcher();

    searcher.Filter = "OU=MyUnit";
    searcher.CacheResults = true;
    SearchResultCollection coll = searcher.FindAll();
    foreach (SearchResult m in coll)
    {
        return m.GetDirectoryEntry();
    }
    throw new Exception("DirectoryEntry not found");
}

仅用这一行替换了那一大口之后,DirectorySearcher 返回了所有属性并且不再需要循环:

private static DirectoryEntry GetEntry2()
{
    return new DirectoryEntry(@"LDAP://SERVERNAME/OU=MyUnit,DC=SERVERNAME,DC=local", "USERNAME", "PASSWORD");
}

现在只需不到 18 分之一秒即可获得 31 个条目的所有所需属性。因此,似乎同一个 DirectoryEntry 的两个不同实例会根据其构建方式给出不同的结果……感觉有点毛骨悚然!


编辑

使用JetBrains DotPeek查看实现。FindOne 函数的开头是这样的:

public SearchResult FindOne()
{
  SearchResult searchResult1 = (SearchResult) null;
  SearchResultCollection all = this.FindAll(false);
  ...

我的第一反应是啊!难怪......但后来我注意到了这个论点。FindAll 有一个接受布尔值的私有版本,这是 FindAll 的开始:

[TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
public SearchResultCollection FindAll()
{
  return this.FindAll(true);
}

private SearchResultCollection FindAll(bool findMoreThanOne)
{
  ... // other code
  this.SetSearchPreferences(adsSearch, findMoreThanOne);

因此,这提供了更多的见解,但并没有真正解释太多。

4

2 回答 2

2

新东西的新答案。您的第一种方法是使用全局目录,所以就像使用

private static DirectoryEntry GetEntry3()
{
    return new DirectoryEntry(@"GC://SERVERNAME/OU=MyUnit,DC=SERVERNAME,DC=local", "USERNAME", "PASSWORD");
}

此外,Microsoft LDAP 库通常有办法告诉它您是否提供服务器名称,因为如果您不说它是服务器名称,它会进行一些优化,这些优化可能会非常慢。对于 DirectoryEntry,它是具有最多参数的构造函数,并且AuthenticationTypes.ServerBind.

于 2013-09-04T12:17:45.313 回答
1

循环不是一个好主意。我要分析那个人的代码:

objGroupEntry = sr.GetDirectoryEntry();
dso = new DirectorySearcher(objGroupEntry);

dso.ClientTimeout = TimeSpan.FromSeconds(30);

dso.PropertiesToLoad.Add("physicalDeliveryOfficeName");
dso.PropertiesToLoad.Add("otherFacsimileTelephoneNumber");
dso.PropertiesToLoad.Add("otherTelephone");
dso.PropertiesToLoad.Add("postalCode");
dso.PropertiesToLoad.Add("postOfficeBox");
dso.PropertiesToLoad.Add("streetAddress");
dso.PropertiesToLoad.Add("distinguishedName");

dso.SearchScope = SearchScope.OneLevel;

dso.Filter = "(&(objectClass=top)(objectClass=person)(objectClass=organizationalPerson)(objectClass=user))";
dso.PropertyNamesOnly = false;

SearchResult pResult = dso.FindOne();

if (pResult != null)
{
    offEntry = pResult.GetDirectoryEntry();

    foreach (PropertyValueCollection o in offEntry.Properties)
    {
        this.Controls.Add(new LiteralControl(o.PropertyName + " = " + o.Value.ToString() + "<br/>"));
    }
}

我不知道他为什么要进行两次搜索,但让我们假设有一个很好的理由。他应该从 中获取这些属性SearchResult,而不是从 的返回值pResult.GetDirectoryEntry,因为它是一个全新的对象。

string postalCode = pResult.Properties["postalCode"][0] as string;
List<string> otherTelephones = new List<string>();
foreach(string otherTelephone in pResult.Properties["otherTelephone"])
{
    otherTelephones.Add(otherTelephone);
}

如果您坚持要获得DirectoryEntry,则使用以下命令一次要求所有属性RefreshCache

offEntry = pResult.GetDirectoryEntry();
offEntry.RefreshCache(propertyNameArray);

如果这些都没有帮助,请查看您的过滤器,看看您是否可以使用BaseLevel范围。

于 2013-09-04T00:09:06.227 回答