Feb. 21, 2010, 9:24 a.m.
posted by vendetta
Searching the Network Directory
One of the most frequently used functions within network directory services is searching for information. To help make this job easier, the .NET library includes the DirectorySearcher class, which provides a substantial number of properties to help you define the search and retrieve the results.
Step 1: Defining the Search Properties
Four properties of the DirectorySearcher class control how the search is performed:
SearchRoot
The SearchRoot property defines where in the directory structure the search will begin. The SearchRoot value can be specified using the standard URI technique to define a DirectoryEntry object:
SearchRoot = "LDAP://192.168.1.100/dc=ispnet1, dc=net";
For some network directory services (including AD), a special name is assigned to the root object. The RootDSE object can reference the root object in the directory:
SearchRoot = "LDAP://192.168.1.100/RootDSE";
If no value is set for the SearchRoot property, the search will attempt to find the root object and start there.
Filter
By default, the DirectorySearcher object will return all objects in the directory services database. The Filter property can define what objects will be returned in the search results.
The Filter property is a string value and must be enclosed in parentheses. The default filter value is as follows:
(objectClass=*)
This value returns all objectClass objects in the database (filter values can use wildcard characters). Alternatively, you can choose to filter on any objectClass or any object property that is defined in the database. For example:
(objectClass=person) Returns all objects that have an objectClass of person
(sn=Bl*) Returns all objects that have a surname value that starts with Bl
(sn>=Blum) Returns all objects that have a surname value that are alphabetically equal or greater than Blum
The last example shows that any type of mathematical expression can be used within the filter value. You can also specify multiple filter values, along with a boolean operation to use.
For example, the following returns the object that has an objectClass of person and the sn value of Blum:
(&(objectClass=person)(sn=Blum))
Likewise, you can use multiple boolean operations:
(&(objectClass=person)(|(sn=Blum)(sn=Pierce)))
This hodgepodge returns all objects that are objectClass of person and have a surname value of either Blum or Pierce.
PropertiesToLoad
By default, the DirectorySearcher object returns all the properties associated with an object. For some objects, this returns a lot of data. If you are only interested in a few properties, you can tell the DirectorySearcher which properties to return, saving time and network bandwidth. The PropertiesToLoad property is a StringCollection object, which adds each property name to retrieve in the collection using the Add() method:
ds.PropertiesToLoad.Add("cn");
ds.PropertiesToLoad.Add("sn");
The return result set (see the “Extracting the Search Results” section) will only contain the object properties that you specified in the PropertiesToLoad property.
SearchScope
By default, the DirectorySearcher object will search the entire tree under the SearchRoot object. You can define the search depth using the SearchScope property. It can be one of three values:
SearchScope.Base Limits the search to only the base object defined in the SearchRoot property
SearchScope.OneLevel Limits the search to only the immediate children of the base object defined in the SearchRoot property
SearchScope.Subtree Searches the entire tree under the base object defined in the SearchRoot property
Step 2: Retrieving the Search Results
Once the DirectorySearcher parameters have been determined, you can start the search and retrieve the results. Use either FindOne() or FindAll() to retrieve results from a search.
FindOne()
The FindOne() method returns only one result of the defined search in a SearchResult class object. If more than one item is found in the result, only the first item is returned.
The SearchResult class is similar to the DirectoryEntry object but only refers to a database object by reference and cannot access the object. The SearchResult object includes the Path and Properties properties that can extract information about the database object, such as its location in the database, and the names and values of its properties.
The Path property can create a DirectoryEntry object, which can then directly access the object to modify or delete the object or any properties of the object.
FindAll()
The FindAll() method returns all of the results of a search in a SearchResultCollection class object. The SearchResultCollection object is a collection of SearchResult objects, one object for each result from the search.
You can iterate through the SearchResultCollection object using the foreach statement, extracting each individual SearchResult object.
Step 3: Extracting the Search Results
Once the SearchResult or SearchResultCollection object is retrieved from the search, you can extract the individual database object information using the Properties property:
SearchResult sr = ds.FindOne(); string user = sr.Properties["cn"][0];
This code snippet finds the first result from the search and extracts the common name (cn) property value.
Alternatively, you can use the Path property to create a DirectoryEntry object to use for further manipulation:
SearchResult sr = ds.FindOne(); string newpath = sr.Path; DirectoryEntry de = new DirectoryEntry(newpath);
Here, after the DirectoryEntry object is created, it can extract, modify, or delete any property of the object, or the object itself, as shown in the "Modifying Directory Data" section.
Performing a Search
Listing 15.10, the SimpleSearch.cs program, demonstrates how to perform a simple network directory services search for all of the items in the database.
using System;
using System.DirectoryServices;
class SimpleSearch
{
public static void Main()
{
DirectoryEntry root = new DirectoryEntry(
"LDAP://192.168.1.100/DC=ispnet1,DC=net",
"cn=Administrator, dc=ispnet1, dc=net", "password",
AuthenticationTypes.ServerBind);
DirectorySearcher searcher = new DirectorySearcher(root);
searcher.Filter = "(&(objectClass=person)(sn=Blum))";
searcher.PropertiesToLoad.Add("cn");
searcher.PropertiesToLoad.Add("telephoneNumber");
SearchResultCollection results = searcher.FindAll();
foreach(SearchResult result in results)
{
string searchpath = result.Path;
Console.WriteLine("path: {0}", searchpath);
ResultPropertyCollection rpc = result.Properties;
foreach(string property in rpc.PropertyNames)
{
foreach(object value in rpc[property])
Console.WriteLine(" property = {0} value = {1}",
property, value);
}
}
}
}
The SimpleSearch program uses a filter to specify retrieving only objects that use the person objectClass and have a surname of Blum. The results are stored in a SearchResultCollection and extracted using a foreach statement.
For each result, the properties are extracted using the ResultPropertyCollection object and the PropertyNames property. The resulting output should contain the desired objects. For instance, in the following output, you can see that the only properties that were returned by the search were the properties specified in the PropertiesToLoad Add() methods:
C:\>SimpleSearch path: LDAP://192.168.1.100/cn=kblum, ou=sales, dc=ispnet1, dc=net property= cn value = Katie Blum property= telephonenumber value = (111)222-3333 property= telephonenumber value = (444)555-6666 property= adspath value = LDAP://192.168.1.100/cn=kblum, ou=sales, Â dc=ispnet1, dc=net path: LDAP://192.168.1.100/cn=jblum, ou=accounting, dc=ispnet1, dc=net property= cn value = Jessica Blum property= adspath value = LDAP://192.168.1.100/cn=jblum, ou=accounting, Â dc=ispnet1, dc=net C:\>
| Note |
You may notice that both objects contained an extra property, the adspath. This is an internal property that is used for the network directory service and cannot be modified or removed by you. |
Advanced Search Features
The DirectorySearcher class also provides some advanced functions that can help the searching process, in terms of both the searching process and the results returned.
Using Server Referrals
If you are running your AD programs in an AD tree environment, multiple domains can be accessed. If you specify a server address within your DirectorySearcher object, it is possible that the actual database object may be contained on a different server. If this is the case, the server you attempt to bind to will send you a server referral notice, which informs the program of the proper server to use to bind to the database object.
By default, the DirectorySearcher object will produce an exception when it receives a server referral. You can change this behavior by using the ReferralChasing property to define the DirectorySearcher object reaction when it receives a server referral notice from the server. Four options can be specified, each one enumerated by the ReferralChasingOption enumerator:
None Specifies that the program does not follow server referrals and will throw an Exception.
Subordinate Specifies that the program will follow server referrals for servers that contain subordinate objects in the directory tree.
External Specifies that the program will follow server referrals for servers that are external to the local network.
All Specifies that the program will follow all server referrals received by network directory service servers.
Limiting Searches
For searches on large network directory service databases, you may have to place limits on how much data is returned to prevent having applications appear to “lock-up” during a search. Use the following properties in the DirectorySearcher class to specify search limitations:
ClientTimeout Allows you to set the amount of time (in seconds) to wait for an answer from the server before timing out and throwing an Exception.
PageSize Defines the maximum number of objects to return in a search. The default value is 0, which defines an unlimited number of objects.
If the PageSize property is set to a value larger than 0, and more objects are present in the returned results, the next search will pick up where the initial search left off. This allows an application to retrieve as many objects as necessary and continue the search until the proper object is found.
PropertyNamesOnly A boolean value. If it is set to true, the DirectorySearcher object returns only the names of the object properties without any data values. The default value is false.
ServerPageTimeLimit Allows the program to specify how long the server should spend (in seconds) looking for search results for an individual page (see the PageSize property definition). If the search exceeds the time limit, the server should return the results obtained up to the time limit, along with information about where to resume searching. The default value is -1, which defines no page time limit.
ServerTimeLimit Defines the total amount of time (in seconds) the server should spend processing a search query. If the time limit is exceeded, the server should return the results obtained up to the time limit, along with information about where to resume the search. The default value for this property is -1, indicating no server time limit.
SizeLimit Sets a total number of objects the server should return for a search. If more results are available, the server should disregard them. There is no way to continue searching from the SizeLimit point. Each network directory service server has its own set SizeLimit value. If the SizeLimit property is set to exceed the server default value, the server value will be used.