Результаты List.Intersect меняются после foreach

У меня проблема с этим фрагментом кода, когда я делаю метод пересечения, все работает нормально.

Когда я делаю счет на моем пересечении перед foreach, у меня есть 1.

После foreach, если счет снова после foreach, у меня есть 0, почему это происходит? Всегда должно быть 1...

var matchedRoles = roles.Intersect(user.Roles);
int before = matchedRoles.Count();

foreach (var matchedRole in matchedRoles)
{
    user.Roles.Remove(matchedRole);
}

int after = matchedRoles.Count();
if (matchedRoles.Any())
{
    accountRepository.Update(user);   
}

4 ответа

Решение

Это происходит потому, что запросы LINQ оцениваются лениво: результат не создается до тех пор, пока он не должен быть (что происходит всякий раз, когда вы вызываете Count). Само собой разумеется, что если вы измените user.Roles тем временем Count рассчитанный после модификации будет другим.

Если вы хотите "исправить" результат, вы должны заставить LINQ сделать локальную копию результатов, например, так:

// Here, ToArray() forces LINQ to immediately produce the results
var matchedRoles = roles.Intersect(user.Roles).ToArray();

Таким образом, любые дальнейшие операции на matchedRoles будет работать с фиксированным "снимком" и давать те же результаты, пока вы не измените matchedRoles сам.

Intersect использует отложенное выполнение. Это означает, что каждый раз, когда вы перечисляете результат, код выполняется.

Итак, ваш второй звонок Count перечисляет matchedRoles перечислим во второй раз, вызывая повторное выполнение roles.Intersect(user.Roles), Поскольку вы удалили роль у пользователя, теперь она не возвращает никаких элементов.

Чтобы избежать этого, перечислите результат один раз, используя ToList и работать с перечисленными значениями оттуда:

var matchedRoles = roles.Intersect(user.Roles).ToList();

Linq использует отложенное выполнение: по сути, он дважды пересекается. Один раз с соответствующей ролью все еще в user.Roles, и один раз после вашего вызова, чтобы удалить роль.

Ваш LINQ будет снова выполнен в тот момент, когда вы получите к нему доступ во второй раз. Если вы хотите получить копию ссылок, вы можете использовать метод ToList и перебирать его элементы.

Другие вопросы по тегам