1

The following code allows me to extract the entire Global Address List from DirectoryServices. The code is functional in that it gives me what I need. The problem is that it takes about 20 seconds to return 1000 objects. Is there anything that I can do to speed this up?

    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; }

    }
TimidObserver
  • 157
  • 2
  • 11

2 Answers2

0

From your code I can see that you are returning a fully populated list. You could modify this method to yeild return values as soon as they are identified. To do this change the return type from List to Ienumerable, then remove the returning list and where you had been adding to that list return the newly created object with a yeild return

dotnetperls has a great definition for the yeild statement.

You'll have have something like this...

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;
            }
        }
    }
}

You should also check out Joes accepted answer.

Community
  • 1
  • 1
Helix 88
  • 701
  • 6
  • 19
  • I will give this a shot tomorrow. – TimidObserver Aug 21 '15 at 00:58
  • I tried this and it did not increase the speed of the method at all. Thanks for the suggestion. – TimidObserver Aug 21 '15 at 13:01
  • Its worth ensuring that where you are calling the method you are not called a ToList(), as this again will block the execution until all items have been retrieved. – Helix 88 Aug 21 '15 at 13:35
  • Also its worth noting that this code wont make 1000 items retrieve any quicker, but it won't block the execution. – Helix 88 Aug 21 '15 at 13:37
  • Yeh I am thinking that I won't be able to make it go any faster, so I will have to just load it with a background worker so that it doesn't stall the whole application. – TimidObserver Aug 21 '15 at 14:15
  • if you are using .Net 4.5 or greater you could use an async task – Helix 88 Aug 21 '15 at 14:22
  • Sadly I am saddled with 4.0 for this project. – TimidObserver Aug 21 '15 at 14:24
  • Well depending on the context of use you could add a cache which gets loaded from a background worker when the application loads. For an extra bonus feature you could have a refresh button to clear and repopulate he cache. However if the bottleneck isn't the application itself, rather the server then that could be where the issue is? Do you know what OS/version it is running? – Helix 88 Aug 21 '15 at 20:55
0

It turns out that GetDirectoryEntry() is the problem. Apparently using it is very resource intensive because it allows you to actually update the Directory entry after retrieving it. In other words, every call to it does an additional call to active directory every time it is called. I just need to access/read the properties not update them, so I rewrote the method without GetDirectoryEntry(). It now returns the entire global address list instantly. The code that solved my problem is below.

    [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;
        }

    }
TimidObserver
  • 157
  • 2
  • 11