从 DirectoryServices 检索全局地址列表非常慢

Retrieving Global Address List from DirectoryServices is Extremely Slow

以下代码允许我从 DirectoryServices 中提取整个全局地址列表。该代码的功能在于它为我提供了我需要的东西。问题是 return 1000 个对象大约需要 20 秒。我可以做些什么来加快速度吗?

    public static List<Address> GetGlobalAddressList()
    {
        using (var searcher = new DirectorySearcher())
        {
            using (var entry = new DirectoryEntry(searcher.SearchRoot.Path, "*****", "*****"))
            {
                searcher.Filter = "(&(mailnickname=*)(objectClass=user))";
                searcher.PropertiesToLoad.Add("cn");
                searcher.PropertyNamesOnly = true;
                searcher.SearchScope = SearchScope.Subtree;
                searcher.Sort.Direction = SortDirection.Ascending;
                searcher.Sort.PropertyName = "cn";
                var results = searcher.FindAll();
                var addressList = new List<Address>();
                foreach (SearchResult i in results)
                {
                    var address = new Address
                    {
                        DisplayName = (string)i.GetDirectoryEntry().Properties["displayName"].Value,
                        Mail = (string) i.GetDirectoryEntry().Properties["mail"].Value
                    };
                    addressList.Add(address);

                }
                return addressList;
            }
        }
    }

    public class Address
    {
        public string DisplayName { get; set; }
        public string Mail { get; set; }

    }

从您的代码中我可以看出您正在 return 填写一个完整的列表。您可以修改此方法以在识别出值后立即生成 return 值。为此,将 return 类型从 List 更改为 Ienumerable,然后删除 returning 列表以及您添加到该列表的位置 return 新创建的对象 return

dotnetperls 对 yield 语句有很好的定义。

你会有这样的东西...

public static IEnumerable<Address> GetGlobalAddressList()
{
    using (var searcher = new DirectorySearcher())
    {
        using (var entry = new DirectoryEntry(searcher.SearchRoot.Path, "*****", "*****"))
        {
            searcher.Filter = "(&(mailnickname=*)(objectClass=user))";
            searcher.PropertiesToLoad.Add("cn");
            searcher.PropertyNamesOnly = true;
            searcher.SearchScope = SearchScope.Subtree;
            searcher.Sort.Direction = SortDirection.Ascending;
            searcher.Sort.PropertyName = "cn";

            foreach (SearchResult i in searcher.FindAll())
            {
                var address = new Address
                {
                    DisplayName = (string)i.GetDirectoryEntry().Properties["displayName"].Value,
                    Mail = (string) i.GetDirectoryEntry().Properties["mail"].Value
                };
                yeild return address;
            }
        }
    }
}

您还应该查看 Joes 已接受的答案。

事实证明问题出在 GetDirectoryEntry() 上。显然,使用它会占用大量资源,因为它允许您在检索目录条目后实际更新它。换句话说,每次调用它都会在每次调用时额外调用活动目录。我只需要 access/read 属性而不更新它们,所以我重写了没有 GetDirectoryEntry() 的方法。它现在 returns 整个全球地址列表立即。解决我问题的代码如下。

    [WebMethod()]
    public static List<Address> GetAddresses()
    {
        using (var objsearch = new DirectorySearcher())
        {
            objsearch.Filter = "(& (mailnickname=*)(objectClass=user))";
            objsearch.SearchScope = SearchScope.Subtree;
            objsearch.PropertiesToLoad.Add("cn");                
            objsearch.PropertiesToLoad.Add("mail");
            objsearch.PropertyNamesOnly = false;
            objsearch.Sort.Direction = SortDirection.Ascending;
            objsearch.Sort.PropertyName = "cn";
            objsearch.PageSize = 5000;
            var colresults = objsearch.FindAll();
            var addressList = new List<Address>();
            foreach (SearchResult objresult in colresults)
            {
                var address = new Address();

                var cn = objresult.Properties["cn"];
                if (cn.Count >= 1) address.DisplayName = (cn[0]) as string;

                var mail = objresult.Properties["mail"];
                if (mail.Count >= 1) address.Mail = (mail[0]) as string;

                addressList.Add(address);
            }
            return addressList;
        }

    }