ONDotNet.com    
 Published on ONDotNet.com (http://www.ondotnet.com/)
 See this if you're having trouble printing code examples


Introduction to System.DirectoryServices, Part 2

by Robbie Allen, coauthor of Active Directory, 2nd Edition
08/04/2003

In Part 1 of this two-part series, I showed how easy it is to read individual objects from an LDAP directory with the System.DirectoryServices namespace's DirectoryEntry class. Now, let's look at how to search with the DirectorySearcher class. The DirectorySearcher class works like many other LDAP-based search APIs. Table 1 contains all of the DirectorySearcher properties that can be used to define search criteria. If you are familiar with LDAP, many of the properties, such as SearchScope, should look familiar.

Active Directory

Related Reading

Active Directory
By Robbie Allen, Alistair G. Lowe-Norris

Table 1. DirectorySearcher Properties

Property Name Description
CacheResults Gets or sets the flag that determines if results are cached on the client side.
ClientTimeout Gets or sets the time period the client is willing to wait for the server to answer the search.
Filter Gets or sets the search filter string.
PageSize Gets or sets the page size for paged searching.
PropertiesToLoad Gets or sets the attributes to return from a search.
PropertyNamesOnly Gets or sets the flag that indicates that only attribute names are to be returned from a search.
ReferralChasing Gets or sets whether referrals are chased.
SearchRoot Gets or sets the base from which the search should start.
SearchScope Gets or sets the scope of the search.
ServerPageTimeLimit Gets or sets the time the server will wait for an individual page to return from a search.
ServerTimeLimit Gets or sets the time the server will wait for a search to complete.
SizeLimit Gets or sets the maximum number of objects that can be returned by a search.
Sort Gets or sets the attribute that is used when returning sorted search results.

The following code shows how to search for all user objects in the rallencorp.com domain.


Dim objSearch As New DirectorySearcher()
objSearch.SearchRoot = New DirectoryEntry("LDAP://dc=rallencorp,dc=com")
objSearch.Filter = "(&(objectclass=user)(objectcategory=person))"
objSearch.SearchScope = SearchScope.Subtree
objSearch.PropertiesToLoad.Add("cn")

Dim colQueryResults As SearchResultCollection
colQueryResults = objSearch.FindAll()

Dim objResult As SearchResult
For Each objResult In colQueryResults
    Console.WriteLine(objResult.Properties("cn")(0))
Next

After instantiating a new DirectorySearcher class, I set four properties before executing the search. The SearchRoot accepts a DirectoryEntry object representing the search base, the Filter property is the LDAP filter string, SearchScope is one of the values contained in the System.DirectoryServices.SearchScope enumeration, and PropertiesToLoad.Add() builds the attribute list to return from the query. You can specify multiple attributes in a single statement by using PropertiesToLoad.AddRange:


objSearch.PropertiesToLoad.AddRange(New String() {"cn", "sn", "givenname"})

After all of the search parameters have been set, I can use the FindAll method to invoke the search. A System.DirectoryServices.SearchResultsCollection is returned by the FindAll method, and you can iterate over each entry using a For Each loop. The SearchResultsCollection contains System.DirectoryServices.SearchResult objects, which are very similar to DirectoryEntry objects.

If you only want to retrieve the first object in the search results, you can use the FindOne() method, which returns a single SearchResult object.

Manipulating Objects

Modifying objects with System.DirectoryServices can be done in a couple of different ways. To modify an attribute that currently has a value, you can set it using the Properties property. For example, the following code would modify the givenName attribute:


objADObject.Properties("givenName")(0) = "Robert"

If you want to set an attribute that was previously unset, you must use the Properties.Add method. The following code would set the previously unset as an attribute:


objADObject.Properties("sn").Add("Robert")

To determine if an attribute has been set, you can use Properties("<attributename>").Count, which will return the number of values that have been set for the attribute. Just like with ADSI, all modifications are made initially to the local property cache and must committed to the server. With ADSI, you would use the IADs::SetInfo method, and with System.DirectoryServices it is called CommitChanges, which is available from the DirectoryEntry class.


objADObject.CommitChanges()
Now that I've covered how to set an attribute, I can modify the code from Part 1 -- which prints all of the values of an attribute -- to instead set an attribute. The following code expects three command-line parameters; the first is the ADsPath of the object to modify, the second is the attribute name, and the third is the value to set the attribute to.

Dim strADsPath As String
Dim strAttrName As String
Dim strAttrValue As String
Try
    Dim intArgs As Integer = Environment.GetCommandLineArgs().Length()
    If intArgs <> 4 Then
        Throw (New Exception("All parameters are required"))
    Else
        strADsPath = Environment.GetCommandLineArgs()(1)
        strAttrName = Environment.GetCommandLineArgs()(2)
        strAttrValue = Environment.GetCommandLineArgs()(3)
    End If
Catch objExp As Exception
    Console.WriteLine("Error: " & objExp.Message)
    Console.WriteLine("Usage: " & Environment.GetCommandLineArgs()(0) & _
                      " ADsPath AttributeName Attribute Value")
    Console.WriteLine()
    Return
End Try

Dim objADObject As New <code>DirectoryEntry</code>()
Try
    If objADObject.Exists(strADsPath) = False Then
        Throw (New Exception("Object does not exist"))
    End If
Catch objExp As Exception
    Console.WriteLine("Error retrieving object: " & strADsPath)
    Console.WriteLine("Error: " + objExp.Message)
    Return
End Try

Dim strOldValue As String
Try
    objADObject.Path = strADsPath
    If objADObject.Properties(strAttrName).Count > 0 Then
        strOldvalue = objADObject.Properties(strAttrName)(0)
        objADObject.Properties(strAttrName)(0) = strAttrValue
    Else
        objADObject.Properties(strAttrName).Add(strAttrValue)
    End If
    objADObject.CommitChanges()
Catch objExp As Exception
    Console.WriteLine("Error setting object: " & strADsPath)
    Console.WriteLine("Error: " + objExp.Message)
    Return
End Try

Console.WriteLine(strADsPath)
Console.WriteLine("Attribute: " + strAttrName)
Console.WriteLine("Old value: " + strOldValue)
Console.WriteLine("New value: " + strAttrValue)
Console.WriteLine()
Console.WriteLine("Update Successful")

This code is not that different than the example program from earlier. The main difference is the check for additional command-line parameters, and determination if the attribute that was specified on the command-line has been set previously or not.

Adding objects with System.DirectoryServices is similar in nature to ADSI. You must first get a reference to the parent object, and then add a child. You can add a child by using the Children.Add method of a DirectoryEntry object. The following example shows how to create a user object:


Dim objParent As New DirectoryEntry("LDAP://ou=sales,dc=rallencorp, _
                                    dc=com", _
                                    "administrator@rallencorp.com",_
                                    "MyPassword", _
                                    AuthenticationTypes.Secure)
Dim objChild As DirectoryEntry = objParent.Children.Add("cn=jdoe", _
                                                                     "user")
objChild.Properties("sAMAccountName").Add("jdoe")
objChild.CommitChanges()

objChild.NativeObject.AccountDisabled = False
objChild.CommitChanges()

Console.WriteLine("Added user")

You may have noticed several things. First, when I instantiated the DirectoryEntry object, I passed three additional parameters that I haven't used before. The second parameter is the user to authenticate with, the third is the password for the user, and last is any authentication options from the AuthenticationTypes enumeration (ADS_AUTHENTICATION_ENUM in ADSI).

After the first CommitChanges call, the object is created in the directory. After that, I enable the account by calling ADSI's AccountDisabled method. System.DirectoryServices does not duplicate all of the functionality of ADSI. As I said earlier, it is primarily a wrapper around ADSI.

One of the reasons System.DirectoryServices is so powerful is because you can still access native ADSI interfaces by using the NativeObject method. NativeObject will return the IADs interface of the specific type of object. In our previous example, NativeObject will return an IADsUser object, on which I can then call the IADsUser::AccountDisabled method. A final CommitChanges call will update the directory and enable the account.

To use the NativeObject method, you'll need to add a reference to the ActiveDs.dll library. From VS.NET, select Project->Add Reference from the menu. Click the COM tab, click Active DS Type Library under Component Name, and click the Select button. Click OK to close the window.

This concludes our introduction to the .NET Framework and the System.DirectoryServices namespace. The information I've covered should be sufficient to get you started writing directory-enabled applications with .NET, but if you need additional information, check out MSDN, which contains detailed documentation on the .NET class library, including System.DirectoryServices.


O'Reilly & Associates recently released (April 2003) Active Directory, 2nd Edition.

Robbie Allen is the coauthor of Active Directory, 2nd Edition and the author of the Active Directory Cookbook.


Return to ONDotnet.com

Copyright © 2009 O'Reilly Media, Inc.