优化.NET访问Active Directory的性能

从Active Directory获取大量对象时应特别注意,一不小心,就会掉入性能瓶颈甚至引起内存泄漏。本文提供了一个关于.NET访问Active Directory的优化例子。

1.获取对象的属性值 DirectoryEntry.Properties

获取一个DirectoryEntry对象后,我们就开始检索它的属性值;习惯了用foreach的朋友一般会这样写:

DirectoryEntry entry = //
foreach(Property property in entry.Properties)
{
    
if(property.Name == //)
        
//
}

事实上,System.DirectoryService.PropertyCollection内部包含了一个HashTable用于键值对访问,并且在索引器中,对未包含的键,创建一个新的PropertyValueCollection对象并加入到HashTable中。

所以,我们可以放心地直接写下:

string propertName = //
object value = entry.Properties[propertName].Value;

 

对这个新建的PropertyValueColletion,它的Value属性定义同样保证了访问的健壮性,这是它的源代码:

public object 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对象的属性了:

object value = entry.Properties[propertyName].Value;
if ( value != null )
{
    
//
}

 

在测试中,查询两万个对象的属性值时,可以节省90秒左右的时间。

2.构建搜索器 DirectorySearcher

使用这个类应注意三点:

(1)对搜索结果的缓存。它默认的CacheResults为true,如果你不需要,应记得将它设置为false,否则一大块空间就浪费了。

(2)仅搜索已赋值的Property。PropertyNamesOnly默认为false,会搜索所有的Property返回多于你想要的对象,记得将它设置为true。

(3)可以在其构造函数中指定PropertyName数组,只搜索包含PropertyName的对象。

下面的例子是搜索包含UserName和Password属性并且属性已赋值的对象:

DirectorySearcher searcher = new DirectorySearcher(
                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的源代码:

private ArrayList InnerList
{
    
get
    {
        
if (this.innerList == null)
        {
            
this.innerList = new ArrayList();
            IEnumerator enumerator 
= new ResultsEnumerator(thisthis.rootEntry.GetUsername(), this.rootEntry.GetPassword(), this.rootEntry.AuthenticationType);
            
while (enumerator.MoveNext())
            {
                
this.innerList.Add(enumerator.Current);
            }
        }
        
return this.innerList;
    }
}

 

而foreach是直接调用迭代器创建和返回一个SearchResult对象,避免了装箱与拆箱的过程。

应严重注意的是:SearchResultCollection是未托管资源,而且会占用大量的内存,需要获取大量对象的属性时,推荐使用集合来保存所需的属性值,完成之后立即调用SearchResultCollection.Dispose()来对它进行释放,否则,会导致内存泄露。

posted @ 2008-11-18 23:11  Justina Chen  阅读(2340)  评论(5编辑  收藏  举报