Tip #2 already dealt with one of the problems while using multi-value properties.But there is another pitfall that got me once - and usually only happens in production environments and is very hard to debug. I am talking about property range fetch a mechanism introduced to allow the active directory server to use certain optimizations. Unfortunatelly the optimization means a harder to understand API and more work for us programmer.

When dealing with multi-value properties with a huge number of values the active directory server will only return a certain amount of values on the first access (usually 1500 items). On some properties these values don't have an order so that there is a certain randomness to which values are returned - and therefore a certain randomness if your code works as expected or not. Which makes spotting the problem so hard.

So instead of relying on the fact that directoryEntry.Properties["member"] will return all values one must use the range fetch method using DirectorySearcher in order to get all values for said property. In the code sample below I used a cutoff value to determine if simple access should be trusted or if range fetch (slower) must be used.

C#:
  1. public static readonly int ATTRIBUTE_RANGE_FETCH_CUTOFF = 500;
  2.  
  3. public static List<object> GetAdObjectProperties(DirectoryEntry entry, string propertyName)
  4. {
  5.   if (entry == null) { throw new ArgumentNullException("entry"); }
  6.   if (string.IsNullOrEmpty(propertyName)) { throw new ArgumentException("propertyName"); }
  7.  
  8.   List<object> result = new List<object>();
  9.  
  10.   if (entry.Properties.Contains(propertyName))
  11.   {
  12.     int count = entry.Properties[propertyName].Count;
  13.  
  14.     if (count == 0)
  15.     {
  16.       // Nothing to do.
  17.     }
  18.     if (count == 1)
  19.     {
  20.       result.Add(entry.Properties[propertyName].Value.ToString());
  21.     }
  22.     else if (count <ATTRIBUTE_RANGE_FETCH_CUTOFF)
  23.     {
  24.       foreach (object value in entry.Properties[propertyName])
  25.       {
  26.         result.Add(value.ToString());
  27.       }
  28.     }
  29.     else
  30.     {
  31.       return GetAdObjectPropertiesWithRangeFetch(entry, propertyName);
  32.     }
  33.   }
  34.  
  35.   return result;
  36. }
  37.  
  38. private static List<object> GetAdObjectPropertiesWithRangeFetch(DirectoryEntry entry, string propertyName)
  39. {
  40.   List<object> result = new List<object>();
  41.   int index = 0;
  42.  
  43.   int step = entry.Properties[propertyName].Count - 1;
  44.   string rangeFormatString = propertyName + ";range={0}-{1}";
  45.  
  46.   string currentRange = string.Format(rangeFormatString, 0, step);
  47.  
  48.   using (DirectorySearcher searcher = new DirectorySearcher(entry, string.Format("({0}=*)", propertyName), new string[] { currentRange }, SearchScope.Base))
  49.   {
  50.     SearchResult searchResult = FindCurrentRange(searcher, currentRange);
  51.  
  52.     while ((searchResult != null) && searchResult.Properties.Contains(currentRange))
  53.     {
  54.       foreach (object item in searchResult.Properties[currentRange])
  55.       {
  56.         if (item != null)
  57.         {
  58.           result.Add(item);
  59.         }
  60.         index++;
  61.       }
  62.  
  63.       currentRange = string.Format(rangeFormatString, index, (index + step));
  64.  
  65.       searchResult = FindCurrentRange(searcher, currentRange);
  66.     }
  67.  
  68.     if (searchResult != null)
  69.     {
  70.       // final search with '*' as upper limit
  71.       string finalRange = string.Format(rangeFormatString, index, "*");
  72.       searchResult = FindCurrentRange(searcher, finalRange);
  73.  
  74.       foreach (object item in searchResult.Properties[finalRange])
  75.       {
  76.         if (item != null)
  77.         {
  78.           result.Add(item);
  79.         }                 
  80.       }
  81.     }
  82.  
  83.     return result;
  84.   }
  85. }
  86.  
  87. private static SearchResult FindCurrentRange(DirectorySearcher searcher, string currentRange)
  88. {
  89.   searcher.PropertiesToLoad.Clear();
  90.   searcher.PropertiesToLoad.Add(currentRange);
  91.  
  92.   SearchResult searchResult = searcher.FindOne();
  93.   return searchResult;
  94. }