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.
- public static readonly int ATTRIBUTE_RANGE_FETCH_CUTOFF = 500;
- public static List<object> GetAdObjectProperties(DirectoryEntry entry, string propertyName)
- {
- if (entry.Properties.Contains(propertyName))
- {
- int count = entry.Properties[propertyName].Count;
- if (count == 0)
- {
- // Nothing to do.
- }
- if (count == 1)
- {
- result.Add(entry.Properties[propertyName].Value.ToString());
- }
- else if (count <ATTRIBUTE_RANGE_FETCH_CUTOFF)
- {
- foreach (object value in entry.Properties[propertyName])
- {
- result.Add(value.ToString());
- }
- }
- else
- {
- return GetAdObjectPropertiesWithRangeFetch(entry, propertyName);
- }
- }
- return result;
- }
- private static List<object> GetAdObjectPropertiesWithRangeFetch(DirectoryEntry entry, string propertyName)
- {
- int index = 0;
- int step = entry.Properties[propertyName].Count - 1;
- string rangeFormatString = propertyName + ";range={0}-{1}";
- string currentRange = string.Format(rangeFormatString, 0, step);
- {
- SearchResult searchResult = FindCurrentRange(searcher, currentRange);
- while ((searchResult != null) && searchResult.Properties.Contains(currentRange))
- {
- foreach (object item in searchResult.Properties[currentRange])
- {
- if (item != null)
- {
- result.Add(item);
- }
- index++;
- }
- currentRange = string.Format(rangeFormatString, index, (index + step));
- searchResult = FindCurrentRange(searcher, currentRange);
- }
- if (searchResult != null)
- {
- // final search with '*' as upper limit
- string finalRange = string.Format(rangeFormatString, index, "*");
- searchResult = FindCurrentRange(searcher, finalRange);
- foreach (object item in searchResult.Properties[finalRange])
- {
- if (item != null)
- {
- result.Add(item);
- }
- }
- }
- return result;
- }
- }
- private static SearchResult FindCurrentRange(DirectorySearcher searcher, string currentRange)
- {
- searcher.PropertiesToLoad.Clear();
- searcher.PropertiesToLoad.Add(currentRange);
- SearchResult searchResult = searcher.FindOne();
- return searchResult;
- }
