0
votes

I am developing a program that will update certain active directory properties using HR data (retrieved from SQL Server). The 1st stage was to match using values like samAccountName, email, surname etc. and that has been successful.

Now I need to change the filtering, so that I can retrieve the relevant Active Directory entry using employee number which I believe is employeeID in Active Directory. I have been trying code such as:

dirSearcher.Filter = String.Format("(&(objectCategory=user)(employeeid={0}))", employeeId);

src = dirSearcher.FindAll();

try
{
    de = src[0].GetDirectoryEntry();
    textBoxResult.Text = de.Guid.ToString();
}
catch (Exception ex)
{
    textBoxResult.Text = ex.Message;
    activeDirectoryID = "";
}
return activeDirectoryID;

However, src is always null.

I did manage to get it working using:

PrincipalContext ctx = new PrincipalContext(ContextType.Domain, directoryRootPath, username, password);

UserPrincipal searchTemplate = new UserPrincipal(ctx);
searchTemplate.EmployeeId = HREmployeeNumber;
PrincipalSearcher ps = new PrincipalSearcher(searchTemplate);

UserPrincipal user = (UserPrincipal)ps.FindOne();

try
{
    activeDirectoryID = user.Guid.ToString();
}

However, this code is a lot slower. Originally, when it was matching on samAccountName or email address it processed just over 4000 records in about 5 minutes. In principal the code works, but takes 40 minutes.

Any advice?

1
Time depends on the size of the database. So if 4000 records take 5 minutes you would expect 8 times the number of records in 40 minutes so you are getting 32,000 records. If the number of records is larger than you expect the query is returning too many records. With SQL Server the time may be reduced if you defragment the database. SQL Express is also slow and switching to full version will be faster. The best way of speeding up date data base is to switch from SQL Client to Entity. - jdweng

1 Answers

0
votes

Your query looks ok. The only thing I suspect is maybe your employeeId is an odd type that isn't getting cast to a string the way you expect. What type is it? When you debug the code, inspect dirSearcher.Filter after you set it and make sure it is what you expect it to be.

Apart from that, I can comment on performance. Using PrincipalSearcher is always slower than using DirectorySearcher directly (which is what PrincipalSearcher uses in the background anyway). The whole AccountManagement namespace does make things easier sometimes, but at the cost of control over performance.

This query will always be a little slow since employeeId is not an indexed attribute. So it has to go through every user account to find a match. You can speed this up a little by using FindOne() instead of FindAll() so that it stops after it finds one and doesn't keep trying to look for another match.

You're also better off asking for only objectGuid in the search results, since if you don't specify anything, it will return every attribute for each result.

Then you can use the value of objectGuid returned during the search instead of using GetDirectoryEntry(). The new DirectoryEntry object will go back out to AD and get the info again, which isn't necessary. The only real reason to use GetDirectoryEntry() is if you're going to update the object.

Here's an example of what I mean:

dirSearcher.Filter = String.Format("(&(objectCategory=user)(employeeid={0}))", employeeId);
dirSearcher.PropertiesToLoad.Add("objectGuid");

try
{
    var result = dirSearcher.FindOne();
    var guid = new Guid((byte[]) result.Properties["objectGuid"][0]);

    textBoxResult.Text = guid.ToString();
}
catch (Exception ex)
{
    textBoxResult.Text = ex.Message;
    activeDirectoryID = "";
}
return activeDirectoryID;

I go into more detail about that in an article I wrote about performance when talking to AD: Active Directory: Better performance