Well, I decided to try a test program and my results are considerably different than yours. I also added in the overhead of converting this to a DataTable and not just a collection of SearchResults. I only have a domain with 2247 groups, but this program returned within 3 seconds:
using System;
using System.Data;
using System.DirectoryServices;
namespace GroupGrabber
{
class Invoker
{
[STAThread]
static void Main(string[] args)
{
if (args.Length != 3)
{
throw new ArgumentException("Invalid Arguments - use path, username, password");
}
string path = args[0];
string username = args[1];
string password = args[2];
GroupGrabber gg = new GroupGrabber(path, username, password);
try
{
// DataSet ds = new DataSet();
// ds.Tables.Add(gg.GetGroups("(objectCategory=group)", new string[]{"description", "sAMAccountName"}));
// ds.WriteXml(Console.Out);
Console.WriteLine(gg.GetGroups("(objectCategory=group)", new string[]{"description", "sAMAccountName"}).Rows.Count);
}
catch(Exception ex)
{
Console.WriteLine(ex.ToString());
}
Console.WriteLine("Press Enter to Continue");
Console.ReadLine();
}
}
public class GroupGrabber
{
private string _adsPath;
private DirectoryEntry _de;
public GroupGrabber(string adsPath): this(adsPath, null, null)
{
}
public GroupGrabber(string adsPath, string username, string password)
{
_adsPath = adsPath;
_de = new DirectoryEntry(_adsPath, username, password, AuthenticationTypes.Secure);
}
public DataTable GetGroups(string ldapQuery, string[] properties)
{
//build a datatable, but you could really use anything here
DataTable dt = new DataTable();
foreach (string s in properties)
{
dt.Columns.Add(new DataColumn(s, typeof(string)));
}
//create our searchers
DirectorySearcher ds = new DirectorySearcher(_de, ldapQuery, properties);
SearchResultCollection src = null;
//set paging
ds.PageSize = 1000;
try
{
src = ds.FindAll();
foreach (SearchResult sr in src)
{
foreach (string s in properties)
{
if(sr.Properties.Contains(s))
{
DataRow dr = dt.NewRow();
dr[s] = sr.Properties[s][0].ToString();
dt.Rows.Add(dr);
}
}
}
}
finally
{
if(src != null)
src.Dispose();
ds.Dispose();
}
return dt;
}
}
}
Sample command line: GroupGrabber.exe "LDAP://server:389/OU=SomeOU,DC=domain,DC=com" "username" "password"
Just take the whole thing and drop it into a Console application. You can either hardcode or modify the Invoker class for your particulars. I passed in the entire domain and it was very quick. Originally, I had thought about not using the DirectorySearcher and binding directly to the OU and then accessing the Child collection. But, I think the DirectorySearcher is the most efficient.