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


У меня есть 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;
        }
    }
}
4 89

4 ответа:

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

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
}

это проще и более эффективный ИМО.

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

  • избавиться от внешних if. Вам это не нужно, так как если Count равен нулю, тело цикла 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)
    

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

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

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

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

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