在 Enumerable 对象中快速查找结果

4

我正在尝试编写一个实用程序,以查看自从我在数据库中存储的日期以来,用户是否已登录到Windows。

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";
        }
    }
}

然而,性能非常慢。我从中获取的用户列表大约有150个Windows用户,因此当我遇到这行代码时 UserPrincipal b = (UserPrincipal)result.Single((a) => a.SamAccountName == usr.CONVUSER); 每个用户需要10到15秒才能完成(通过逐步执行,我可以看到它在运行 a.SamAccountName == usr.CONVUSE 步骤以获取每个人,所以最坏情况下运行O(n ^ 2)次)。
有什么建议可以提高效率吗?

2
我讨厌 Stack Overflow 有时候喜欢处理代码段中的制表符,有时候又不喜欢。 - Scott Chamberlain
3个回答

4
我建议:

我建议:

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

由于 PrincipalSearchResult 不像其他东西一样可以缓存,这会使性能下降至接近 O(n) 级别。


3
很惊讶于在如此小的列表上调用Single()需要这么长时间。我相信还有其他问题。调用ps.FindAll()可能会返回一个不缓存其结果并强制您在Single()内的每次迭代中进行昂贵调用的对象。
您可能需要使用分析器来调查命中该行时时间的去向。我还建议查看FIndAll()的实现,因为它返回了一些异常昂贵的迭代内容。
所以,在更仔细地阅读您的代码后,Single()为什么如此昂贵就有意义了。 PrincipalSearcher类使用目录服务存储库作为搜索的存储库。 它不会缓存这些结果。这就是影响性能的原因。
您可能希望使用ToList()ToDictionary()将列表实体化,以便访问主体信息在本地发生。
你也可以完全避免这种代码,改用FindOne()方法,直接查询所需的主体。
但如果无法使用该方法,则应该使用以下内容:
result.ToDictionary(u => u.SamAccountName)[usr.WEBUSER]

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

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

    // ...
}

请注意,我使用了 ToDictionary,因为您使用 Single 暗示了您期望每个 WebLoginUsersRow 都有一个匹配项。根据需要进行调整。 - mqp
感谢。关于第一个输入约15秒,其他内容立即返回翻译后的文本。 - Scott Chamberlain

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接