Последовательность не содержит подходящего элемента

113

У меня есть приложение asp.net, в котором я использую linq для обработки данных. Во время работы я получаю исключение «Последовательность не содержит подходящего элемента».

if (_lstAcl.Documents.Count > 0)
{
    for (i = 0; i <= _lstAcl.Documents.Count - 1; i++)
    {
        string id = _lstAcl.Documents[i].ID.ToString();                           
        var documentRow = _dsACL.Documents.First(o => o.ID == id);
        if (documentRow !=null)
        {

            _lstAcl.Documents[i].Read = documentRow.Read;
            _lstAcl.Documents[i].ReadRule = documentRow.ReadRule;

            _lstAcl.Documents[i].Create= documentRow.Create;
            _lstAcl.Documents[i].CreateRule = documentRow.CreateRule;

            _lstAcl.Documents[i].Update = documentRow.Update;
            _lstAcl.Documents[i].UpdateRule = documentRow.UpdateRule;

            _lstAcl.Documents[i].Delete = documentRow.Delete;
            _lstAcl.Documents[i].DeleteRule = documentRow.DeleteRule;
        }
    }
}
MAC
источник

Ответы:

221

Что ж, я ожидал, что именно эта строка выдает исключение:

var documentRow = _dsACL.Documents.First(o => o.ID == id)

First()выдает исключение, если не может найти подходящих элементов. Учитывая, что вы проверяете значение null сразу после этого, похоже, что вы хотите FirstOrDefault(), что возвращает значение по умолчанию для типа элемента (которое имеет значение null для ссылочных типов), если совпадающие элементы не найдены:

var documentRow = _dsACL.Documents.FirstOrDefault(o => o.ID == id)

Другие варианты, которые следует учитывать в некоторых ситуациях: Single()(когда вы считаете, что есть ровно один совпадающий элемент) и SingleOrDefault()(когда вы считаете, что есть ровно один или ноль совпадающих элементов). Я подозреваю, что FirstOrDefaultэто лучший вариант в данном конкретном случае, но все равно стоит знать о других.

С другой стороны, похоже, что вам на самом деле может быть лучше с объединением здесь в первую очередь. Если вам было все равно, что он будет выполнять все совпадения (а не только первый), вы можете использовать:

var query = from target in _lstAcl.Documents
            join source in _dsAcl.Document
            where source.ID.ToString() equals target.ID
            select new { source, target };
foreach (var pair in query)
{
    target.Read = source.Read;
    target.ReadRule = source.ReadRule;
    // etc
}

Это проще и эффективнее IMO.

Даже если вы все же решите сохранить цикл, у меня есть несколько предложений:

  • Избавьтесь от внешнего if. Вам это не нужно, так как если Count равен нулю, тело цикла for никогда не выполнится
  • Используйте эксклюзивные верхние границы в циклах for - они более идиоматичны в C #:

    for (i = 0; i < _lstAcl.Documents.Count; i++)
  • Исключите общие подвыражения:

    var target = _lstAcl.Documents[i];
    // Now use target for the rest of the loop body
  • По возможности используйте foreachвместо for:

    foreach (var target in _lstAcl.Documents)
Джон Скит
источник
39

Используйте FirstOrDefault . First никогда не вернет null - если он не может найти подходящий элемент, он выдает исключение, которое вы видите.

_dsACL.Documents.FirstOrDefault(o => o.ID == id);
Якуб Конецки
источник
19
Чтобы немного уточнить - сначала может быть возвращено значение null, если ваш предикат соответствует нулевым значениям. Он просто не может здесь вернуть null, так как o.IDвызовет исключение NullReferenceException для нулевого значения.
Джон Скит,
11

Из библиотеки MSDN:

First<TSource>(IEnumerable<TSource>)Метод генерирует исключение , если источник не содержит элементов. Чтобы вместо этого вернуть значение по умолчанию, когда исходная последовательность пуста, используйте FirstOrDefaultметод.

KBoek
источник
0

Для тех из вас, кто столкнулся с этой проблемой при создании контроллера через контекстное меню, повторное открытие Visual Studio от имени администратора исправило ее.

Ясень
источник
-4

Возможно, использование Where () перед First () может вам помочь, поскольку в этом случае моя проблема была решена.

var documentRow = _dsACL.Documents.Where(o => o.ID == id).FirstOrDefault();
Elnaz
источник
3
На самом деле вам здесь помогло использование .FirstOrDefault () вместо .First () - с использованием .Where (o => o.ID == id) .FirstOrDefault () и .FirstOrDefault (o => o.ID == id ) будет идентичным.
pwdst
@pwdst с использованием условия в предложении Where, а затем FirstOrDefault без лямбда-выражения.
Elnaz