Nothing, however, comes without a price. In this case, the price is usability - There is simply no DirectoryEntry in the SDS.P namespace - one has to do a search with a scope of Base. Not quite simple. Additionally, the only datatypes supported on the properties returned from a search operation are byte[] and string. And no support for generics anywhere...
If you want to perform your own requests using this class, just call the GetSendRequestOperation() which issues the request aynchronously using my InfiniTec.Threading library. This is not trivial if you are not familiar with the library - if you need advice on this topic, drop me a note and I will post an additional article on this topic.
Once you have connection, you can simply bind to a known object:
1 using (Connection connection = newConnection("dcaw", DirectoryIdentifierType.Server, false))
2 {
3 item = newItem("CN=Doe\, John, CN=Users, DC=AdventureWorks, DC=local", connection);
4 item.Refresh();
5 displayName = item.Properties.GetProperty<string>("displayName").Value;
6 }
It's as simple as this...
The Item class has the following characteristics:
... don't panic. The Searcher class will help you here. This class is by far the most complex class in this library:

The search operation which will be performed by the Searcher class can be extensively customized. Here are the main option:
Constraints - This property accepts a standard LDAP filter like "(mail=*)" or similar.
IncludeDeletedItems - If true, deleted items are returned.
NamingContextScope - This is important. You can specifiy if you want to search the current naming context only, or search the current and all subordinate contexts.
PageSize - Doing a paged search reduces the resources used during the search. This property let you specify the number of items returned per page.
PropertiesToLoad - Which properties should be populated during the search?
Scope - Do you want to search only the search root, the direct descendents of the searchroot, or all levels below the search root?
SearchRoot - Where does the search begin? If NamingContextScope is set to domain scope, a search root must be specified. Otherwise, this property can be left blank. In this case, the entire forest ist searched.
SizeLimit - This property lets you specify the maximum number of items you want to get. But you should use this sparingly - if more items are returned than specified here, an exception is thrown.
SortKeys - Very handy. This allows server-side sorting of the result set
To start a search, populate the desired fields and call FindAll or FindPage. The first method, performs the search and returns once the search is completed. The FindPage method returns once the next page of items is returned by the server.
For scalability reaonse, you should use the FindAllAsync and FindPageAsync methods - these methods return immediately and thus don't block the current thread. The FindCompleted and ProgressChanged events are fired, whenever an operation completes.
The following example performs an ambiguous name resolution and finds all entries which start with the character a:
1 staticvoid Main(string[] args)
2 {
3 Searcher searcher;
4 SearchToken token;
5
6
7 using (Connection connection = newConnection("dcaw", DirectoryIdentifierType.Server, false))
8 {
9 searcher = newSearcher(connection);
10 searcher.PageSize = 1000;
11
12 searcher.Constraints = "(aNR=a*)";
13 searcher.NamingContextScope = NamingContextScope.IncludeSubDomains;
14 searcher.SearchRoot = Constants.WellknownDistinguishedNames.RootDse;
15 searcher.ProgressChanged += searcher_ProgressChanged;
16 searcher.FindCompleted += searcher_FindCompleted;
17
18 _Event = newManualResetEvent(false);
19
20 searcher.FindPageAsync();
21
22 _Event.WaitOne();
23
24 Console.ReadLine();
25 }
26 }
27
28 staticvoid searcher_FindCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
29 {
30 Console.WriteLine("Finished");
31 if (e.Error != null) Console.WriteLine("Error: " + e.Error.ToString());
32 _Event.Set();
33 }
34
35 staticvoid searcher_ProgressChanged(object sender, SearchProgressChangedEventArgs e)
36 {
37 Console.WriteLine("Found items: " + e.Items.Count);
38 foreach (Item item in e.Items)
39 {
40 Console.WriteLine("\t" + item.DistinguishedName);
41 }
42 Console.WriteLine();
43 }
If you want to get one page and continue the search at a later point in time (for example from an ASPX page), you can use the SearchToken class to recreate a search operation:
1 using (Connection connection = newConnection("dcaw", DirectoryIdentifierType.Server, false))
2 {
3 searcher = newSearcher(connection);
4 searcher.PageSize = 1000;
5
6 // Create a searcher and perform the initial search
7
8 // Now, save the current search token.
9 token = searcher.SearchToken;
10
11
12 // Create a new search operation from the saved token:
13 searcher = newSearcher(connection, token);
14 searcher.ProgressChanged += searcher_ProgressChanged;
15 searcher.FindCompleted += searcher_FindCompleted;
16
17 // Continue the search operation
18 searcher.FindPageAsync();
19 }
The SearchToken is marked as serializable, so it can be persisted in the viewstate of an ASPX page.