Быстрый поиск результатов в перечисляемом объекте - PullRequest
4 голосов
/ 01 марта 2010

Я пытаюсь написать утилиту, чтобы проверить, вошел ли пользователь в 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) раз)

Какие-нибудь рекомендации о способах повышения моей эффективности?

Ответы [ 3 ]

4 голосов
/ 01 марта 2010

Я бы предложил:

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

Поскольку PrincipalSearchResult не кэшируется, как другие вещи, это приведет вас к уровню производительности O (n).

3 голосов
/ 01 марта 2010
var userMap = result.ToDictionary(u => u.SamAccountName);

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

    // ...
}
3 голосов
/ 01 марта 2010

Удивительно, что Single() должно занимать так много времени в таком маленьком списке. Я должен верить, что здесь происходит что-то еще. Вызов ps.FindAll() может возвращать объект, который не кэширует его результаты, и вынуждает вас делать дорогой вызов некоторого ресурса на каждой итерации в Single().

Возможно, вы захотите использовать профилировщик, чтобы выяснить, куда уходит время, когда вы нажимаете эту строку. Я также предложил бы взглянуть на реализацию FIndAll(), потому что она возвращает что-то необычно дорогое, чтобы перебирать.

Таким образом, после прочтения вашего кода, становится понятным, почему Single() стоит так дорого. Класс PrincipalSearcher использует хранилище служб каталогов в качестве хранилища для поиска. Он не кэширует эти результаты . Вот что влияет на вашу производительность.

Вы, вероятно, хотите материализовать список, используя ToList() или ToDictionary(), чтобы доступ к основной информации происходил локально.

Вы также можете полностью избежать такого рода кода и использовать вместо него метод FindOne(), который позволяет вам напрямую запрашивать требуемый принципал.

Но если вы не можете использовать это, то что-то вроде этого должно работать лучше:

result.ToDictionary(u => u.SamAccountName)[usr.WEBUSER]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...