Link pair attributes in Active Directory and ADAM can be quite big. I don't know the official limit, but needless to say, for practical purposes you can assume they are quite large indeed. By default, AD and ADAM will not return the entire attribute if it contains more than a certain number of values (1000 for Windows 2000 and 1500 for Windows 2003+ by default). As such, if you truly want robust code, you need to always use what is called range retrieval for link value paired attributes.
Range retrieval is a process similar to paging in directory services, whereby you ask the directory for a certain range of particular attribute. You know that you are using range retrieval when you see the attribute being requested in the following format:
"[attribute];range=[start]-[end]"
As an example, in the case of the 'member' attribute, you might ask for the first 1500 values like so:
"member;range=0-1499"
Notice, it is zero based so you need to take this into account. The general algorithm as such is:
We covered how to use range retrieval in SDS in our book, and you can download sample code that shows how from the book's website. What we didn't cover was how to do it in SDS.P.
SDS.P is a layer closer the the metal than our ADSI based System.DirectoryServices (SDS). As such, if you are expected to do range retrieval for SDS, you can be assured that you need to do it for SDS.P as well. Adopting the code SDS, you get something like this (but modified to some extent):
static List<string> RangeRetrieve(
LdapConnection connect, string dn, string attribute)
{
int idx = 0;
int step = 0;
List<string> list = new List<string>();
string range = String.Format(
"{0};range={{0}}-{{1}}",
attribute
);
string currentRange = String.Format(range, idx, "*");
SearchRequest request = new SearchRequest(
dn,
String.Format("({0}=*)", attribute),
SearchScope.Base,
new string[] { currentRange }
SearchResultEntry entry = null;
bool lastSearch = false;
while (true)
SearchResponse response =
(SearchResponse)connect.SendRequest(request);
if (response.Entries.Count == 1) //should only be one
entry = response.Entries[0];
//this might be optimized to find full step or just use 1000 for
//compromise
foreach (string attrib in entry.Attributes.AttributeNames)
currentRange = attrib;
lastSearch = currentRange.IndexOf("*", 0) > 0;
step = entry.Attributes[currentRange].Count;
}
foreach (string member in
entry.Attributes[currentRange].GetValues(typeof(string)))
list.Add(member);
idx++;
if (lastSearch)
break;
currentRange = String.Format(range, idx, (idx + step));
request.Attributes.Clear();
request.Attributes.Add(currentRange);
else
return list;
Happy coding... of course, if you are clever you will realize you can avoid all this range retrieval mess by using an attribute scope query (ASQ). :)
*edit: tried to fix the style for code to render in Google Reader correctly
Comments [0] August 10, 2007 Trackback
This is the personal site of Ryan Dunn, co-author of the The .NET Developers Guide to Directory Services Programming.
Ryan currently works for Microsoft and is the Technical Evangelist for SQL Server Data Services (SSDS)
Buy the Book
Contact Ryan