Последовательность не содержит соответствующего элемента

У меня есть приложение 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;
        }
    }
}

Ответ 1

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

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

First() будет выдавать исключение, если он не может найти подходящие элементы. Учитывая, что вы сразу же проверяете нулевое значение, похоже, что вы хотите 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
}

Это более простая и эффективная ИМО.

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

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

    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)
    

Ответ 2

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

_dsACL.Documents.FirstOrDefault(o => o.ID == id);

Ответ 3

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

Ответ 4

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

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