Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Finding results in a Enumerable object quickly

I am trying to write a utility to see if a user has logged in to windows since a date that I have stored in a database.

private void bwFindDates_DoWork(object sender, DoWorkEventArgs e)
{
    UserPrincipal u = new UserPrincipal(context);
    u.SamAccountName = "WebLogin*";
    PrincipalSearcher ps = new PrincipalSearcher(u);
    var result = ps.FindAll();
    foreach (WebAccess.WebLoginUsersRow usr in webAccess.WebLoginUsers)
    {
        UserPrincipal b = (UserPrincipal)result.
            Single((a) => a.SamAccountName == usr.WEBUSER);
        if (b.LastLogon.HasValue)
        {
            if (b.LastLogon.Value < usr.MODIFYDATE)
                usr.LastLogin = "Never";
            else
                usr.LastLogin = b.LastLogon.Value.ToShortDateString();
        }
        else
        {
            usr.LastLogin = "Never";
        }
    }
}

However the performance is very slow. The user list I am pulling from has about 150 Windows users, so when I hit the line UserPrincipal b = (UserPrincipal)result.Single((a) => a.SamAccountName == usr.CONVUSER); it takes 10 to 15 seconds for it to complete per user (stepping through i can see it is doing the step a.SamAccountName == usr.CONVUSE is run for every person so the worst case is running O(n^2) times)

Any recommendations on ways to improve my efficiency?

like image 554
Scott Chamberlain Avatar asked Dec 30 '22 00:12

Scott Chamberlain


2 Answers

I would suggest:

var result = ps.FindAll().ToList();

Since PrincipalSearchResult doesn't cache like other things, this will bring you down near an O(n) performance level.

like image 136
Nick Craver Avatar answered Jan 01 '23 17:01

Nick Craver


It's surprising that Single() should take quite so long on such a small list. I have to believe something else is going on here. The call to ps.FindAll() may be returning an object that does not cache it's results, and is forcing you to make an expensive call to some resource on each iteration within Single().

You may want to use a profiler to investigate where time is going when you hit that line. I would also suggest looking at the implementation of FIndAll() because it's returning something unusually expensive to iterate over.

So after reading your code a little more closely, it makes sense why Single() is so expensive. The PrincipalSearcher class uses the directory services store as the repository against which to search. It does not cache these results. That's what's affecting your performance.

You probably want to materialize the list using either ToList() or ToDictionary() so that accessing the principal information happens locally.

You could also avoid this kind of code entirely, and use the FindOne() method instead, which allows you to query directly for the principal you want.

But if you can't use that, then something like this should work better:

result.ToDictionary(u => u.SamAccountName)[usr.WEBUSER]
like image 35
LBushkin Avatar answered Jan 01 '23 16:01

LBushkin