AccountManagement, PrincipalContext, Principals, And Performance

Often times in trivial AD operations using the *.AccountManagement namespace there is a performance hit in comparison to using ASQ approaches. This has been pretty frustrating to me in the past, as detailed here. In my journeys with these objects though I have determined a better approach. Let’s take the examples from the previous post two methods, GetUser and Get Group. To make these more efficient, we are going to use a combination of PrincipalContext, *Principal, and PrincipalSearcher objects along with a materialized Dictionary object in order to speed things up a bit.

Enough with the filler, let’s look at some code. What we are going to do is create a new PrincipalContext (the static method to hydrate this is discussed in the aforementioned post), then create a new *Principal object consuming the hydrated PrincipalContext in the constructor. The *Principal object will subsequently use the SamAccountName property with a wild card as a search parameter. This *Principal object is then passed into the constructor of a new PrincipalSearcher object so the principal search results can be returned using stuff like FindAll and FindOne. Once the PrincipalSearcher is well formed, we execute a FindAll, ToList to a strongly typed collection with IQueryable support, which allows us to execute a Where clause. The clause leveraged a Regular Expression (ala Regex.IsMatch), and the results are collated in a Dictionary(Of TKey, TValue) according to a specified key selector function. Once the Dictionary is built, a simple if clause is inserted which allows us to find, and return, the object we want! Comparative times of query have shown this cuts down on query time BY 5 TIMES!!!!!!! 

Firstly, the modified GetUser method:

[csharp]

public static UserPrincipal GetUser(string userName)
{
UserPrincipal userPrincipal = null;
SPSecurity.RunWithElevatedPrivileges(() =>
{
PrincipalContext principalContext = GetUserPrincipalContext();
var search = new UserPrincipal(principalContext);
search.SamAccountName = userName + ‘*’;
var ps = new PrincipalSearcher(search);
var pr = ps.FindAll().ToList().Where(a =>
Regex.IsMatch(a.SamAccountName, String.Format(@”{0}\D”, userName))).
ToDictionary(a => a.SamAccountName);
pr.Add(userName, Principal.FindByIdentity(principalContext, IdentityType.SamAccountName, userName));
if (pr.ContainsKey(userName))
{
userPrincipal = (UserPrincipal)pr[userName];
}
});
return userPrincipal;
}

[/csharp]

And the GetGroup method:

[csharp]

public static GroupPrincipal GetGroup(string groupName)
{
GroupPrincipal groupPrincipal = null;
SPSecurity.RunWithElevatedPrivileges(() =>
{
PrincipalContext principalContext = GetGroupPrincipalContext();
var search = new GroupPrincipal(principalContext);
search.SamAccountName = groupName + ‘*’;
var ps = new PrincipalSearcher(search);
var pr = ps.FindAll().ToList().Where(a =>
Regex.IsMatch(a.SamAccountName, String.Format(@”{0}\D”, groupName))).
ToDictionary(a => a.SamAccountName);
pr.Add(groupName, Principal.FindByIdentity(principalContext, IdentityType.SamAccountName, groupName));
if (pr.ContainsKey(groupName))
{
groupPrincipal = (GroupPrincipal) pr[groupName];
}
});
return groupPrincipal;
}

[/csharp]

The first thing you should notice is that these could be placed in a static method of the PrincipalContext as an extension, which would be a whole lot more succint, but I was just happy I got it to work. I’ll leave that up to someone else.

Share

One thought on “AccountManagement, PrincipalContext, Principals, And Performance”

Leave a Reply

Your email address will not be published. Required fields are marked *