Finding results in a Enumerable object quickly

给你一囗甜甜゛ 提交于 2019-12-23 13:28:54

问题


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?


回答1:


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]



回答2:


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.




回答3:


var userMap = result.ToDictionary(u => u.SamAccountName);

foreach (WebAccess.WebLoginUsersRow usr in webAccess.WebLoginUsers)
{
    UserPrincipal b = userMap[usr.WEBUSER];

    // ...
}


来源:https://stackoverflow.com/questions/2358416/finding-results-in-a-enumerable-object-quickly

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!