Our application has a process which fetches all users from Active Directory and updates the relevant SQL tables with their information. The process at nights and it was writ
While writing this question I was tinkering with the test code and managed to find the issue. By providing the domain address when constructing the root DirectoryEntry
:
// var root = new DirectoryEntry("LDAP://DC=mydomain,DC=com");
var root = new DirectoryEntry("LDAP://mydomain.com/DC=mydomain,DC=com");
The search with DirectorySearcher
outperformed that of PrincipalSearcher
. I'm not exactly sure why - perhaps it's something to do with where the searcher looks for the results - but it definitely boosted the search speed.
Take a look at my question and answer on the differences between the two methods. PrincipalSearcher
is merely a wrapper around DirectorySearcher
. It was designed to make it easier to work with Active Directory while providing some automated speed enhancements. DirectorySearcher
can be much faster than PrincipalSearcher
, but it requires a bit more work.
The main reason you're seeing slow behavior from your "old stuff" code is that in when you used PrincipalSearcher
in "new stuff", you got the underlying DirectorySearcher
and fed its PropertiesToLoad
collection. You did not do that in your "old stuff" code.
var properties = new[] { "cn", "name", "distinguishedname", "surname", "title", "displayname" };
//...
var underlying = searcher.GetUnderlyingSearcher() as DirectorySearcher;
//...
underlying.PropertiesToLoad.AddRange(properties);
As a result, your "old stuff" code was pulling every AD attribute for matched results (i.e. significantly more data being transferred), while your implementation using PrincipalSearcher
was only reading 6 attributes.
Doing that when using PrincipalSearcher
is also generally not necessary as it handles caching and picking of attributes on its own. Really, when working with PrincipalSearcher
the only time you need to get the underlying DirectorySearcher
is to set the PageSize
since PrincipalSearcher
doesn't provide a standard way to set it.
I suspect the reason you saw an improvement when specifying the domain is that it didn't have to do any work to figure out the domain name. You unfairly give "new stuff" a head start in that regards because you made the PrincipalContext
before you started the clock so to speak.
// New stuff
var context = new PrincipalContext(ContextType.Domain, "mydomain.com");
var properties = new[] { "cn", "name", "distinguishedname", "surname", "title", "displayname" };
var i = 0;
var now = DateTime.Now; // you should have done this BEFORE setting `context`.
Some other things I noticed in your code that would actually skew the timing in the opposite direction is that in "new stuff", you don't do any filtering, and the initialization of your delegated thread to show the progress occurs AFTER you recorded the start time.