Query by Example

The query by example (QBE) search mechanism searches for objects in a directory using an existing object. To use QBE, the application creates an instance of a principal object, either computer, user, or group, and sets properties on the object. This object becomes the query filter for the PrincipalSearcher class. The FindAll method searches the domain specified in the principal context for objects that have identical properties to those set on the query filter. The FindAll method returns all objects that match the supplied object whereas the FindOne

method returns only a single matching principal object. The following code example creates a principal object for a user who has the name Jim Daly and searches the domain for users who have the same name.

// Create the context for the principal object. 
PrincipalContext ctx = new PrincipalContext(ContextType.Domain, 
                                            "fabrikam",
                                            "DC=fabrikam,DC=com");

// Create an in-memory user object to use as the query example.
UserPrincipal u = new UserPrincipal(ctx);

// Set properties on the user principal object.
u.GivenName = "Jim";
u.Surname = "Daly";

// Create a PrincipalSearcher object to perform the search.
PrincipalSearcher ps = new PrincipalSearcher();

// Tell the PrincipalSearcher what to search for.
ps.QueryFilter = u;

// Run the query. The query locates users 
// that match the supplied user principal object. 
PrincipalSearchResult<Principal> results = ps.FindAll();

The type of object passed to the QBE filter determines the type of principal object returned by the search. For example, in the code sample earlier in this topic, the application creates a UserPrincipal object and uses it as the query filter for the PrincipalSearcher class. Since the query filter is a UserPrincipal object type, all the objects returned from the FindAll method are also UserPrincipal objects. If the object passed to the QBE filter has no properties set, all store objects of specified type are returned.

The query created by QBE produces a logical AND of all the properties set on the principal object. When the attributes are multi-valued, the resulting query is a logical AND of all the values set for the attribute. The resulting query is specific to the type of store, either domain, or application directory, over which the query is performed. For example, the code in the earlier example produces the following LDAP query:

(&(objectCategory=user)(givenName=Jim)(sn=Daly))

Only non-referential properties are supported by QBE. If the Principal object that is used in the query contains properties values that are Principal objects or links to Principal objects the query will throw an InvalidOperationException. For example, consider a group object that has the display name and description properties set. Since these properties are non-referential, that is they are not other principal objects and do not link to other principal objects, the call to FindAll or FindOne succeeds. If the group object adds a member such as a UserPrincipal object, an exception is thrown when FindAll, or FindOne is called.

Setting a non-referential property

The following code example illustrates a simple QBE where no referential properties are set on the group object. In this case the query succeeds and the results include all matching group principal objects.

// Create the princiapl context for the group object
PrincipalContext ctx = new PrincipalContext(ContextType.Domain);

// Create the GroupPrincipal object and set the diplay name property. 
GroupPrincipal g = new GroupPrincipal(ctx);
g.DisplayName = "Administrators";
  
// Create a PrincipalSearcher object     
PrincipalSearcher ps = new PrincipalSearcher(g);

// Searches for all groups named "Administrators" PrincipalSearchResult<Principal> results = ps.FindAll();

Setting both referential and non-referential properties

The following example shows how to set a referential property on the group object. In this case the code example will fail with a InvalidOperationException.

// Create the principal context for the group object.
PrincipalContext ctx = new PrincipalContext(ContextType.Domain);

// Create the GroupPrincipal object and set the diplay name property. 
GroupPrincipal g = new GroupPrincipal(ctx);
g.DisplayName = "Administrators";

// Create a new user and add it to the group. 
UserPrincipal u = new UserPrincipal(ctx);
u.GivenName = "Jim";
g.Members.Add(u);// This sets a referential property. 

// Create a PrincipalSearcher object.
PrincipalSearcher ps = new PrincipalSearcher(g);

// An exception is thrown on this call. 
PrincipalSearchResult<Principal> fr = ps.FindAll();

Using Advanced Search Filters for Read-Only Properties

Some properties of the principal object are read-only properties ("LastLogonTime", "BadLogonCount", "LastBadPasswordAttempt"), because programmatic manipulation of those properties would make no sense. Thus, creating a principal object to use as a query filter for these properties is not possible. To handle these cases, use the AdvancedSearchFilter class. A principal's corresponding AdvancedSearchFilter object is accessed through the AdvancedSearchFilter property on each principal object. AdvancedSearchFilter also offers functionality to do more than just match on equality. It can also match by using greater-than/less-than logic, or partial matching. The following example shows how to use an AdvancedFilter object to search for users who have a LastLogonTime in the previous day:

// Create the principal context for the usr object.
PrincipalContext ctx = new PrincipalContext(ContextType.Domain, "fabrikam.com", "CN=Users,DC=fabrikam,DC=com", "administrator", "securelyStoredPassword");

// Create the principal user object from the context
UserPrincipal usr = new UserPrincipal(ctx);
usr.AdvancedSearchFilter.LastLogonTime(DateTime.Now, MatchType.LessThan); 
usr.AdvancedSearchFilter.LastLogonTime(DateTime.Yesterday, MatchType.GreaterThan);

// Create a PrincipalSearcher object.
PrincipalSearcher ps = new PrincipalSearcher(usr);
PrincipalSearchResult<Principal> fr = ps.FindAll();
foreach (UserPrincipal u in results)
        {
            Console.WriteLine(u.Name);
        }

See Also

Reference

System.DirectoryServices.AccountManagement

Concepts

About System.DirectoryServices.AccountManagement
Using System.DirectoryServices.AccountManagement

Send comments about this topic to Microsoft.

Copyright © 2008 by Microsoft Corporation. All rights reserved.