How to enumerate over Organizational Units and enumerate the members efficiently ?

Jun 22, 2011 at 11:58 AM

Is it possible to enumerate the OU’s ?

In my case the company is divided in sub-divisions and sub-division has a number of employees. I would like have a list of all sub-divisions and a list of the users belonging to it.

The OU of a sub-division is as follows :

“OU=DIV_A,OU=Sites,DC=Company,DC=local"

“OU=DIV_B,OU=Sites,DC=Company,DC=local"

The OU containers for the user accounts for division ‘DIV_A’ are :

"OU=users 1,OU=users,OU=Accounts,OU= DIV_A,OU=Sites,DC=Company,DC=local"

"OU=users 2,OU=users,OU=Accounts,OU= DIV_A,OU=Sites,DC=Company,DC=local"

 

The following query takes 22 seconds for 400 entries:

         string namingContext = "OU=Users,OU=Accounts,OU=DIV_A,OU=Sites,DC=company,DC=local";

         var example = new

                   {

                       DistinguishedName = "",

                        Name = "",

                       Cn = "",

                        ObjectGuid = default(Guid),

                        ObjectSid = default(SecurityIdentifier)

                    };

         var query = context.Query(example, SearchScope.Subtree, namingContext, objectClass: "Person")

                       .OrderBy(u => u.Name)

                       .Select(u => u);

 

         // Perform Query

         var users = query.PageAll(10000).ToList();

 

Jun 22, 2011 at 2:21 PM

Found the query to enumerate:

         var query = context.Query<LdapSite>(SearchScope.OneLevel)

                           .Select(s => s);

          // Perform Query

         var users = query.PageAll(10000).ToList();

  

Whereas LdapSite:

   [DirectorySchema("OU=Sites,DC=Company,DC=local", ObjectCategory = "OrganizationalUnit")]

   public class LdapSite

   {

       [DirectoryAttribute]

       public string DistinguishedName { get; set; }

 

       [DirectoryAttribute("name")]

       public string Name { get; set; }

 

       [DirectoryAttribute("objectguid")]

       public Guid Guid { get; set; }

 

       [DirectoryAttribute("description")]

       public string Description { get; set; }

 

       [DirectoryAttribute]

       public DateTime? WhenCreated { get; set; }

 

       [DirectoryAttribute]

       public DateTime? WhenChanged { get; set; }

   }

 

 

Enumeration of the members (Person) still takes a lot of time : between 17 and 22 seconds for 400 entries ...

 

 

Coordinator
Jun 25, 2011 at 7:55 AM

That it is way too long for that number of entries.  I ran that query against my local directory and I get about 14 seconds for 50,000 entries.  If I remove the Guid and SecurityIdentifier properties I get around 3 seconds.  When I perform it as a dynamic query I get around 4 seconds for those properties.  This leads me to believe I'm paying a huge boxing / unboxing penalty.  I'll definitely look into speeding this up.

Can you tell me what the performance difference is for sorting locally vs. on the server?  Also, how many divisions do you have in total?  I'm going to try to mimic your structure locally.

Jun 28, 2011 at 8:03 AM

Some more info about my scenario :

The total number of sites is currently 105 and the users are stored in the following structure :

  • ·         Company\Sites\SiteName\Accounts\users\Users 1
  • ·         Company\Sites\SiteName\Accounts\users\Users 2
  • ·         Company\Sites\SiteName\Accounts\users\... (Minor important)

 

For the tests I ran for Division DIV_A :

  • ·         Company\Sites\DIV_A\Accounts\users\Users 1                            -> 387 users
  • ·         Company\Sites\DIV_A\Accounts\users\Users 2                            -> 303 users
  • ·         Company\Sites\DIV_A\Accounts\users\...                                     ->   21 users
  • ·         Company\Sites\DIV_A\Accounts\users                                Total -> 711 users

 

When I run the Linq Query and select only the DistinguishedName then it takes 3 seconds for 711 users

// We don't use the standard User object because the NamingContext does already

// determines the root from which the objects will be searched

string namingContext = "OU=Users,OU=Accounts,OU=DIV_A,OU=Sites,DC=company,DC=local";

var example = new { DistinguishedName = "" };

 

var query = context.Query(example, SearchScope.Subtree, namingContext, objectCategory: "Person")

                   .FilterWith("(|(objectClass=contact)(objectClass=user))")

                  .Select(u => u);

 

// Perform Query

var users = query.ToList();

 

When I run the same Linq query and select more fields then it takes 18 seconds for 711 users :

// We don't use the standard User object because the NamingContext does already

// determines the root from which the objects will be searched

string namingContext = "OU=Users,OU=Accounts,OU=DIV_A,OU=Sites,DC=company,DC=local";

var example = new { DistinguishedName = "", Name = "", Cn = "", ObjectGuid = default(Guid), ObjectSid = default(SecurityIdentifier), WhenCreated = default(DateTime?), WhenChanged = default(DateTime?) };

 

var query = context.Query(example, SearchScope.Subtree, namingContext, objectCategory: "Person")

                                       .FilterWith("(|(objectClass=contact)(objectClass=user))")

                                       .Select(u => u);

 // Perform Query

var users = query.ToList();

 

I don not use any sorting here.

When I browse the users in the Microsoft Management Console (Active Directory Users and Computers) then it takes about 2 seconds to get a list of 350 users with the

following attributes : Name, Type, description, Office Communication Server Address, SAP Account, SAP HR ID

 

Coordinator
Jul 20, 2011 at 3:29 AM

John,

This is a bug in the way I'm accessing multi-valued attributes.  S.DS.P has two methods and I'm using the method designed for single-valued attributes.  I will have this checked in soon and I'll create a bug report for this.  I was able to cut the performance down from 20 seconds to about 100ms.  Thanks for catching this!