C# ActiveDirectory DirectorySearcher:为什么FindOne()比FindAll()慢,为什么省略属性?

C# ActiveDirectory DirectorySearcher:为什么FindOne()比FindAll()慢,为什么省略属性?,c#,.net,active-directory,ldap,directorysearcher,C#,.net,Active Directory,Ldap,Directorysearcher,我有一个从ActiveDirectory检索一些信息的循环。这是一个很大的性能瓶颈 此代码段(在执行了31次的循环中)花费了00:01:14.6562500(1分14秒): 用此代码段替换它使其下降到00:00:03.1093750(3秒): 结果完全相同,以相同的顺序返回相同的属性。我在中找到了一些关于内存泄漏的信息,但没有提到性能(我在.NET3.5上) 以下是一个不同的问题,但它给出了一些我为什么首先循环的背景: 我想在一个查询中获得所有属性,但我无法让DirectorySearcher

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

此代码段(在执行了31次的循环中)花费了00:01:14.6562500(1分14秒):

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

结果完全相同,以相同的顺序返回相同的属性。我在中找到了一些关于内存泄漏的信息,但没有提到性能(我在.NET3.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");
}
现在只需不到十八分之一秒就可以获得31个条目的所有想要的属性。 因此,似乎同一DirectoryEntry的两个不同实例可以根据其构造方式给出不同的结果…感觉有点令人毛骨悚然


编辑

用于查看实现。FindOne函数的启动方式如下:

public SearchResult FindOne()
{
  SearchResult searchResult1 = (SearchResult) null;
  SearchResultCollection all = this.FindAll(false);
  ...
我的第一个反应是Argh!难怪……但后来我注意到了这个参数。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);

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

循环不是一个好主意。我将分析这家伙的代码:

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/>"));
    }
}
如果您坚持要获取
目录条目
,请立即使用以下命令请求所有属性:


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

新内容的新答案。您的第一个方法是使用全局目录,因此它就像使用

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

另外,Microsoft LDAP库通常有一种方法来判断您是否提供了服务器名称,因为它进行了一些优化,如果您不说它是服务器名称的话,这些优化可能会非常慢。对于DirectoryEntry,它是和
身份验证类型。ServerBind

谢谢@Hall72215,我用一些新发现编辑了我的帖子。seems说,同一目录条目的不同实例根据其构造方式给出不同的结果。如果不循环,现在只需不到18分之一秒:-)顺便问一下,你知道FindOne为什么比FindAll慢这么多吗?即使在以不同的方式实例化根节点之后,这种现象仍然存在。@LouisSomers不知道,你会认为FindOne是FindAll的包装器。说实话,如果它只是FindAll的包装器,我会非常失望。我的期望是,它将省略一些循环(服务器端),通过传输更少的数据来减少网络负载,并最终表现得更好。但是,令人惊讶,令人惊讶……实际上,如果您看到MS FindOne()/(围绕FindAll的包装器)实现在后台使用COM对象API/DLL导入进行调用。这可能也是FindOne速度较慢的一个原因。请参阅System.DirectoryServices.InteropI与FindOne()存在完全相同的问题,在循环中执行FindAll()所花的时间比FindAll()长。FindAll()将返回多个AD条目,但需要客户端中的自定义逻辑将返回的条目映射回最初查询FindAll()的项目。有点痛。MS FindOne实施未针对性能进行优化。您找到解决方案了吗?我不认为这里所接受的是您所追求的解决方案?@Spock,不,不是真的,除了问题中描述的使用LDAP之外,似乎AD实现从未针对性能进行过优化。我最终还是选择了LDAP。使用LDAP时会有一些复杂的情况,例如更改密码时需要安全连接(LDAPS或LDAP over SSL),这对于内部通信来说是一种过分的手段,但无论如何,它确实对我们的性能要求有足够的帮助。
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/>"));
    }
}
string postalCode = pResult.Properties["postalCode"][0] as string;
List<string> otherTelephones = new List<string>();
foreach(string otherTelephone in pResult.Properties["otherTelephone"])
{
    otherTelephones.Add(otherTelephone);
}
offEntry = pResult.GetDirectoryEntry();
offEntry.RefreshCache(propertyNameArray);
private static DirectoryEntry GetEntry3()
{
    return new DirectoryEntry(@"GC://SERVERNAME/OU=MyUnit,DC=SERVERNAME,DC=local", "USERNAME", "PASSWORD");
}