优化.NET访问Active Directory的性能
从Active Directory获取大量对象时应特别注意,一不小心,就会掉入性能瓶颈甚至引起内存泄漏。本文提供了一个关于.NET访问Active Directory的优化例子。
1.获取对象的属性值 DirectoryEntry.Properties
获取一个DirectoryEntry对象后,我们就开始检索它的属性值;习惯了用foreach的朋友一般会这样写:
foreach(Property property in entry.Properties)
{
if(property.Name == //)
//
}
事实上,System.DirectoryService.PropertyCollection内部包含了一个HashTable用于键值对访问,并且在索引器中,对未包含的键,创建一个新的PropertyValueCollection对象并加入到HashTable中。
所以,我们可以放心地直接写下:
object value = entry.Properties[propertName].Value;
对这个新建的PropertyValueColletion,它的Value属性定义同样保证了访问的健壮性,这是它的源代码:
{
get
{
if (base.Count == 0)
{
return null;
}
if (base.Count == 1)
{
return base.List[0];
}
object[] array = new object[base.Count];
base.List.CopyTo(array, 0);
return array;
}
}
这样,代码中不需要去调用entry.Properties.Contains方法,而直接使用以下代码就可以高效地访问DirectoryEntry对象的属性了:
if ( value != null )
{
//
}
在测试中,查询两万个对象的属性值时,可以节省90秒左右的时间。
2.构建搜索器 DirectorySearcher
使用这个类应注意三点:
(1)对搜索结果的缓存。它默认的CacheResults为true,如果你不需要,应记得将它设置为false,否则一大块空间就浪费了。
(2)仅搜索已赋值的Property。PropertyNamesOnly默认为false,会搜索所有的Property返回多于你想要的对象,记得将它设置为true。
(3)可以在其构造函数中指定PropertyName数组,只搜索包含PropertyName的对象。
下面的例子是搜索包含UserName和Password属性并且属性已赋值的对象:
entry,
"username=*",
new string[] { "UserName", "Password", });
searcher.PropertyNamesOnly = true;
SearchResultCollection results = searcher.FindAll();
3.遍历和释放结果集 SearchResultCollection
foreach对于SearchResultCollection的访问是更高效的方式,这是因为,SearchResultCollection的内部使用ArrayList进行集合的索引访问,在第一次加载ArrayList时,它调用迭代器将所有的SearchResult对象创建并装箱到ArrayList中,当进行SearchResultCollection[int index]访问时,它对ArrayList中的对象进行拆箱操作。下面是SearchResultCollection构建InnerList的源代码:
{
get
{
if (this.innerList == null)
{
this.innerList = new ArrayList();
IEnumerator enumerator = new ResultsEnumerator(this, this.rootEntry.GetUsername(), this.rootEntry.GetPassword(), this.rootEntry.AuthenticationType);
while (enumerator.MoveNext())
{
this.innerList.Add(enumerator.Current);
}
}
return this.innerList;
}
}
而foreach是直接调用迭代器创建和返回一个SearchResult对象,避免了装箱与拆箱的过程。
应严重注意的是:SearchResultCollection是未托管资源,而且会占用大量的内存,需要获取大量对象的属性时,推荐使用集合来保存所需的属性值,完成之后立即调用SearchResultCollection.Dispose()来对它进行释放,否则,会导致内存泄露。