<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="http://forums.asp.net/utility/FeedStylesheets/rss.xsl" media="screen"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:wfw="http://wellformedweb.org/CommentAPI/"><channel><title>Active Directory and LDAP</title><link>http://forums.asp.net/93.aspx</link><description>Questions about Active Directory and other directory stores, such as those accessible via LDAP, are appropriate here.</description><dc:language>en</dc:language><generator>CommunityServer 2007 SP1 (Build: 20510.895)</generator><item><title>Common System.DirectoryServices Patterns</title><link>http://forums.asp.net/thread/1007517.aspx</link><pubDate>Tue, 02 Aug 2005 15:46:26 GMT</pubDate><guid isPermaLink="false">4c671506-2930-414c-a40b-8bf57ded5924:1007517</guid><dc:creator>dunnry</dc:creator><author>dunnry</author><slash:comments>0</slash:comments><comments>http://forums.asp.net/thread/1007517.aspx</comments><wfw:commentRss>http://forums.asp.net/commentrss.aspx?SectionID=93&amp;PostID=1007517</wfw:commentRss><description>&lt;font size="5"&gt;Common Patterns in System.DirectoryServices&lt;/font&gt;&lt;br /&gt;&lt;br /&gt;&lt;font color="#0000ff" size="4"&gt;&lt;i&gt;&lt;b&gt;Searching the Directory:&lt;/b&gt;&lt;/i&gt;&lt;/font&gt;&lt;br /&gt;&lt;br /&gt;
&lt;ol&gt;
&lt;li&gt;Create a DirectoryEntry that represents your SearchRoot.&amp;nbsp; Your
searches will be rooted to this location and will have the same
permissions as the bound SearchRoot.&amp;nbsp; Failure to specify a
SearchRoot (bad practice) means you will attempt to search the entire
current domain (as specified by RootDSE defaultNamingContext) and can
be problematic for ASP.NET applications. &lt;/li&gt;&lt;li&gt;Create your LDAP query. 
&lt;/li&gt;&lt;li&gt;Optionally specify PropertiesToLoad collection - this will be
more efficient if specified.&amp;nbsp; However, it will return all
available, non-constructed attributes if left null (Nothing in VB.NET).
&lt;/li&gt;&lt;li&gt;For v1.1 S.DS, use the DirectorySearcher.FindAll() for all
searches.&amp;nbsp; The DirectorySearcher.FindOne() has a memory leak in
certain situations - .NET 2.0 is unaffected and safe to use.&lt;br /&gt;&lt;/li&gt;&lt;/ol&gt;&lt;i&gt;&lt;b&gt;Sample For Retrieving Only 1 Result:&lt;/b&gt;&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;&lt;font face="Courier New" size="2"&gt;DirectoryEntry searchRoot = new DirectoryEntry(&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; "LDAP://server/OU=People,DC=domain,DC=com", //searches will be rooted under this OU&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; "domain\\user", //we will use these credentials&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; "password",&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; AuthenticationTypes.Secure&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; );&lt;br /&gt;&lt;br /&gt;using (searchRoot) //we are responsible to Dispose this!&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; DirectorySearcher ds = new DirectorySearcher(&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; searchRoot,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; "(sn=Smith)", //here is our query&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; new string[] {"sn"} //optionally specify attributes to load&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; );&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; ds.SizeLimit = 1;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; SearchResult sr = null;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; using (SearchResultCollection src = ds.FindAll())&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (src.Count &amp;gt; 0)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; sr = src[0];&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (sr != null)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; //now use your SearchResult&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;}&lt;/font&gt;&lt;br /&gt;&lt;br /&gt;&lt;i&gt;&lt;b&gt;Sample For Retrieving Multiple Results:&lt;/b&gt;&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;&lt;font face="Courier New" size="2"&gt;DirectoryEntry searchRoot = new DirectoryEntry(&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; "LDAP://server/OU=People,DC=domain,DC=com", //searches will be rooted under this OU&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; "domain\\user", //we will use these credentials&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; "password",&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; AuthenticationTypes.Secure&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; );&lt;br /&gt;&lt;br /&gt;using (searchRoot) //we are responsible to Dispose this!&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; DirectorySearcher ds = new DirectorySearcher(&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; searchRoot,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; "(sn=Smith)", //here is our query&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; new string[] {"sn"} //optionally specify attributes to load&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; );&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; ds.PageSize = 1000; //enable paging for large queries&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; using (SearchResultCollection src = ds.FindAll())&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; foreach (SearchResult sr in src)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; //use the SearchResult here&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;}&lt;/font&gt;&lt;br /&gt;&lt;br /&gt;Notice
the common pattern here and how all DirectoryEntry and
SearchResultCollection classes are Disposed.&amp;nbsp; Failure to do so can
leak memory.&lt;br /&gt;&lt;br /&gt;&lt;i&gt;&lt;b&gt;&lt;font color="#0000ff" size="4"&gt;Reading Attributes:&lt;/font&gt;&lt;/b&gt;&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;One
of the most common issues that people run into is getting an error
trying to read the attribute when it does not exist.&amp;nbsp; It is
important to understand there is no concept of null attributes in
Active Directory - the attribute either exists on the object or it
doesn't - it is never null.&amp;nbsp; This however can be confusing because
trying to read a non-existant attribute culminates in a null reference
exception in .NET.&lt;br /&gt;&lt;br /&gt;To protect against this, use this pattern:&lt;br /&gt;&lt;br /&gt;&lt;font face="Courier New" size="2"&gt;DirectoryEntry entry = new DirectoryEntry(...);&lt;br /&gt;using (entry)&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (entry.Properties.Contains("property"))&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; //now safe to access "property"&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; //cast as appropriate to string, byte[], int, etc...&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; //object o = entry.Properties["property"].Value;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;}&lt;/font&gt;&lt;br /&gt;&lt;br /&gt;This same pattern is appropriate for the SearchResult as well:&lt;br /&gt;&lt;br /&gt;&lt;font face="Courier New" size="2"&gt;SearchResult result;&lt;br /&gt;//... fill the result&lt;br /&gt;&lt;br /&gt;if (result.Properties.Contains("property"))&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; //now safe to access "property"&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; //cast as appropriate to string, byte[], int, etc...&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; //object o = result.Properties["property"][0];&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;/font&gt;Depending
on the attribute, the PropertyValueCollection (or
ResultPropertyValueCollection) will return either a single value or
multiple values.&amp;nbsp; To actually get a value from the DirectoryEntry,
we need to cast the 'object' to whatever type we are expecting.&amp;nbsp;
Thus, we get something like:&lt;br /&gt;
&lt;br /&gt;
&lt;font face="Courier New" size="2"&gt;DirectoryEntry entry = new DirectoryEntry(...);&lt;br /&gt;using (entry)&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (entry.Properties.Contains("&lt;/font&gt;&lt;font face="Courier New" size="2"&gt;sn&lt;/font&gt;&lt;font face="Courier New" size="2"&gt;"))&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; string lastName = entry.Properties["sn"][0].ToString();&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;}&lt;/font&gt;&lt;br /&gt;
&lt;br /&gt;
In this example, I cast the 'sn' attribute to the lastname.&amp;nbsp; It
makes 2 assumptions: 1.) The value can be interpreted as a string, and
2.) I am only interested in a single valued attribute.&lt;br /&gt;
&lt;br /&gt;
The pattern changes slightly if we are looking to read the values from a multi-valued attribute:&lt;br /&gt;
&lt;br /&gt;
&lt;font face="Courier New" size="2"&gt;DirectoryEntry entry = new DirectoryEntry(...);&lt;br /&gt;using (entry)&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (entry.Properties.Contains("&lt;/font&gt;&lt;font face="Courier New" size="2"&gt;mail&lt;/font&gt;&lt;font face="Courier New" size="2"&gt;"))&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; foreach (object o in entry.Properties["mail"])&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
//iterate through each value in the 'mail' attribute as a string&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Response.Output.Write("mail: {0}", o.ToString());&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;}&lt;/font&gt;&lt;br /&gt;
&lt;br /&gt;
Notice that I am iterating through all the objects and casting to
string in this case.&amp;nbsp; If the attribute was something else, it
might be appropriate to cast to a byte[] array or an integer,
etc.&amp;nbsp; It just depends on what it is, but the casting from object
to whatever type you need is up to you.&lt;br /&gt;
&lt;br /&gt;&lt;font size="4"&gt;&lt;i&gt;&lt;font color="#0000ff"&gt;Binding to a Data Control:&lt;/font&gt;&lt;/i&gt;&lt;/font&gt;&lt;br /&gt;&lt;br /&gt;In order to bind to things like a dropdown list or a datagrid, we need to perform the search first and manually create a datasource to use.&amp;nbsp; Since AD is hierarchical and datacontrols are not, we generally need to think about how we will model the data when rows/columns won't make any sense.&lt;br /&gt;&lt;br /&gt;Here is a sample function that will search Active Directory given a filter for searching and the attributes to retrieve.&amp;nbsp; I called it 'FindUsers' for lack of a better name about 5 years ago and decided not to change it to confuse people.&amp;nbsp; In reality, it can be used to find anything, not just users.&amp;nbsp; It returns a DataSet and optionally caches it for faster lookups next time.&lt;br /&gt;&lt;br /&gt;&lt;font face="Courier New" size="2"&gt;
public DataSet FindUsers(string sFilter, string[] columns, string path, bool useCached)&lt;br /&gt;
{&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; //try to retrieve from cache first&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; HttpContext context = HttpContext.Current;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; DataSet userDS = (DataSet)context.Cache[sFilter];&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; if((userDS == null) || (!useCached))&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; //setup the searching entries&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; DirectoryEntry deParent = new DirectoryEntry(path);&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; //deParent.Username = Config.Settings.UserName;&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; //deParent.Password = Config.Settings.Password;&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; deParent.AuthenticationType = AuthenticationTypes.Secure;&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; DirectorySearcher ds = new DirectorySearcher(&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; deParent,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; sFilter,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; columns,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;  &amp;nbsp;&amp;nbsp;  &amp;nbsp; SearchScope.Subtree&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; );&lt;br /&gt;&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; ds.PageSize = 1000;&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; using(deParent)&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; //setup the dataset that will store the results&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; userDS = new DataSet("userDS");&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; DataTable dt = userDS.Tables.Add("users");&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; DataRow dr;&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; //add each parameter as a column&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; foreach(string prop in columns)&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; dt.Columns.Add(prop, typeof(string));&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; using (SearchResultCollection src = ds.FindAll())&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; foreach(SearchResult sr in src)&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; dr = dt.NewRow();&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; foreach(string prop in
columns)&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
if(sr.Properties.Contains(prop))&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; dr[prop] =
sr.Properties[prop][0];&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; dt.Rows.Add(dr);&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; //cache it for later, with sliding 3 minute window&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; context.Cache.Insert(sFilter, userDS, null, DateTime.MaxValue, TimeSpan.FromSeconds(180));&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; return userDS;&lt;br /&gt;
}&lt;/font&gt;
&lt;br /&gt;
&lt;br /&gt;
Now, just place a datagrid (or dropdown) on your page, and with a few lines of code, you have a searcher:&lt;br /&gt;
&lt;br /&gt;&lt;font face="Courier New" size="2"&gt;
//sample use&lt;br /&gt;
string qry = String.Format("(&amp;amp;(objectCategory=person)(givenName={0}*))", txtFirstName.Text);&lt;br /&gt;
string[] columns = new string[]{"givenName", "sn", "cn", "sAMAccountName", "telephoneNumber", "l"}&lt;br /&gt;
string ldapPath = "LDAP://dc=mydomain";&lt;br /&gt;&lt;br /&gt;
DataSet ds = FindUsers(qry, columns, ldapPath, true);&lt;br /&gt;
DataGrid1.DataSource = ds;&lt;br /&gt;
DataGrid1.DataBind();&lt;/font&gt;
&lt;br /&gt;
&lt;br /&gt;
I will try to keep this thread updated with the most common questions.&lt;br /&gt;</description></item></channel></rss>