Sekwencja nie zawiera pasującego elementu

112

Mam aplikację asp.net, w której używam linq do manipulacji danymi. Podczas uruchamiania otrzymuję wyjątek „Sekwencja nie zawiera pasującego elementu”.

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;
        }
    }
}
PROCHOWIEC
źródło

Odpowiedzi:

220

Cóż, spodziewałbym się, że ten wiersz rzuca wyjątek:

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

First()zgłosi wyjątek, jeśli nie może znaleźć żadnych pasujących elementów. Biorąc pod uwagę, że natychmiast potem testujesz pod kątem null, brzmi to tak, jak chcesz FirstOrDefault(), co zwraca domyślną wartość dla typu elementu (która jest zerowa dla typów referencyjnych), jeśli nie zostaną znalezione pasujące elementy:

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

Inne opcje do rozważenia w niektórych sytuacjach to Single()(kiedy uważasz, że jest dokładnie jeden pasujący element) i SingleOrDefault()(kiedy uważasz, że jest dokładnie jeden lub zero pasujących elementów). Podejrzewam, że FirstOrDefaultto najlepsza opcja w tym konkretnym przypadku, ale o pozostałych i tak warto wiedzieć.

Z drugiej strony wygląda na to, że w rzeczywistości może być lepiej, jeśli dołączysz tutaj. Jeśli nie obchodziło Cię, że wykona wszystkie dopasowania (a nie tylko pierwsze), możesz użyć:

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
}

To prostsza i wydajniejsza IMO.

Nawet jeśli nie zdecydują się zachować pętlę, mam kilka propozycji:

  • Pozbądź się zewnętrznej części if. Nie potrzebujesz tego, tak jakby Count ma wartość zero, treść pętli for nigdy nie zostanie wykonana
  • Użyj ekskluzywnych górnych granic w pętlach for - są one bardziej idiomatyczne w C #:

    for (i = 0; i < _lstAcl.Documents.Count; i++)
  • Eliminacja typowych podwyrażeń:

    var target = _lstAcl.Documents[i];
    // Now use target for the rest of the loop body
  • Jeśli to możliwe, foreachzamiast forzaczynać od:

    foreach (var target in _lstAcl.Documents)
Jon Skeet
źródło
39

Użyj FirstOrDefault . First nigdy nie zwróci wartości null - jeśli nie może znaleźć pasującego elementu, zgłasza wyjątek, który widzisz.

_dsACL.Documents.FirstOrDefault(o => o.ID == id);
Jakub Konecki
źródło
19
Dla uproszczenia - First może ogólnie zwrócić wartość null, jeśli predykat dopasował wartości null. Po prostu nie może tutaj zwrócić wartości null, ponieważ o.IDzgłosiłby wyjątek NullReferenceException na wartość null.
Jon Skeet
11

Z biblioteki MSDN:

First<TSource>(IEnumerable<TSource>)Sposób zgłasza wyjątek, jeżeli źródło zawiera elementy. Aby zamiast tego zwrócić wartość domyślną, gdy sekwencja źródłowa jest pusta, użyj FirstOrDefaultmetody.

KBoek
źródło
0

Dla tych z Was, którzy napotkali ten problem podczas tworzenia kontrolera za pomocą menu kontekstowego, ponowne otwarcie programu Visual Studio jako administrator rozwiązało problem.

Popiół
źródło
-4

Może użycie Where () before First () może ci pomóc, ponieważ mój problem został rozwiązany w tym przypadku.

var documentRow = _dsACL.Documents.Where(o => o.ID == id).FirstOrDefault();
Elnaz
źródło
3
Pomogło ci tutaj użycie .FirstOrDefault () zamiast .First () - przy użyciu .Where (o => o.ID == id) .FirstOrDefault () i .FirstOrDefault (o => o.ID == id ) będą identyczne.
pwdst
@pwdst przy użyciu warunku w klauzuli Where, a następnie FirstOrDefault bez żadnego wyrażenia lambda.
Elnaz